From f5e6fd57cdd7a1c577eb3f622b6b90b939a23ed1 Mon Sep 17 00:00:00 2001 From: Sam Liu Date: Fri, 29 Mar 2024 14:22:19 +0700 Subject: [PATCH] first --- .browserslistrc | 2 + .editorconfig | 9 + .eslintignore | 0 .eslintrc.js | 197 + .gitattributes | 4 + .gitignore | 11 + README.md | 112 + babel.config.json | 13 + gulpfile.js/config/browser-sync-prod.js | 20 + gulpfile.js/config/browser-sync.js | 12 + gulpfile.js/config/directories.js | 77 + gulpfile.js/config/externals-js.js | 8 + gulpfile.js/config/nodemon.js | 6 + gulpfile.js/config/pug.js | 4 + gulpfile.js/config/rename.js | 11 + gulpfile.js/config/sass.js | 3 + gulpfile.js/config/server.js | 20 + gulpfile.js/config/usemin.js | 9 + gulpfile.js/config/webpack.js | 51 + gulpfile.js/index.js | 11 + .../private-tasks/backup--chunk-scripts.js | 15 + gulpfile.js/private-tasks/build--assets.js | 20 + gulpfile.js/private-tasks/build--locales.js | 37 + .../private-tasks/build--scripts-es6.js | 24 + .../private-tasks/build--scripts-external.js | 22 + .../private-tasks/build--styles-rtl.js | 27 + gulpfile.js/private-tasks/build--styles.js | 35 + gulpfile.js/private-tasks/build--views-min.js | 20 + gulpfile.js/private-tasks/build--views.js | 47 + gulpfile.js/private-tasks/bundle--scripts.js | 27 + gulpfile.js/private-tasks/bundle--styles.js | 25 + gulpfile.js/private-tasks/clean--output.js | 9 + .../private-tasks/clean--temp-scripts.js | 14 + .../private-tasks/clean--temp-styles.js | 14 + gulpfile.js/private-tasks/copy--assets.js | 12 + gulpfile.js/private-tasks/lint--scripts.js | 26 + .../private-tasks/minify--chunk-scripts.js | 20 + gulpfile.js/private-tasks/minify--styles.js | 19 + gulpfile.js/private-tasks/print--results.js | 64 + gulpfile.js/private-tasks/reload.js | 10 + gulpfile.js/private-tasks/run--dev-server.js | 8 + gulpfile.js/private-tasks/run--views.js | 35 + gulpfile.js/private-tasks/run--watchers.js | 71 + gulpfile.js/private-tasks/versionfy.js | 15 + gulpfile.js/tasks/build.js | 34 + gulpfile.js/tasks/default.js | 15 + gulpfile.js/utils/browser-sync.js | 7 + gulpfile.js/utils/errors.js | 63 + gulpfile.js/utils/index.js | 46 + index.html | 1 + jsconfig.json | 10 + package.json | 96 + server/index.js | 35 + server/route.js | 83 + src/assets/data/.gitkeep | 0 src/assets/favicon/android-chrome-192x192.png | Bin 0 -> 13644 bytes src/assets/favicon/android-chrome-512x512.png | Bin 0 -> 44744 bytes src/assets/favicon/apple-touch-icon.png | Bin 0 -> 12678 bytes src/assets/favicon/browserconfig.xml | 9 + src/assets/favicon/favicon-16x16.png | Bin 0 -> 970 bytes src/assets/favicon/favicon-32x32.png | Bin 0 -> 1871 bytes src/assets/favicon/favicon.ico | Bin 0 -> 15086 bytes src/assets/favicon/mstile-150x150.png | Bin 0 -> 8134 bytes src/assets/favicon/safari-pinned-tab.svg | 53 + src/assets/favicon/site.webmanifest | 19 + src/assets/fonts/.gitkeep | 0 src/assets/fonts/icomoon/icomoon.eot | Bin 0 -> 1544 bytes src/assets/fonts/icomoon/icomoon.svg | 12 + src/assets/fonts/icomoon/icomoon.ttf | Bin 0 -> 1380 bytes src/assets/fonts/icomoon/icomoon.woff | Bin 0 -> 1456 bytes src/assets/images/logo.png | Bin 0 -> 12118 bytes src/assets/images/transparent.png | Bin 0 -> 68 bytes src/scripts/_libs/.gitkeep | 0 src/scripts/_libs/jquery.fullPage.min.js | 55 + src/scripts/_libs/modernizr-custom-3.6.0.js | 774 ++ src/scripts/_libs/select2.js | 6210 +++++++++++++++++ src/scripts/cores/plugin.js | 97 + src/scripts/cores/prototype.js | 17 + src/scripts/initializations/browser-detect.js | 59 + .../initializations/import-jquery-plugins.js | 9 + .../initializations/improve-window-events.js | 82 + .../initializations/update-js-assets-path.js | 7 + src/scripts/main.js | 8 + src/scripts/plugins/back-to-top.js | 22 + src/scripts/repositories/api.js | 98 + src/scripts/utils/cookies.js | 40 + src/scripts/utils/date.js | 11 + src/scripts/utils/doms.js | 9 + src/scripts/utils/http.js | 75 + src/scripts/utils/index.js | 53 + src/scripts/utils/layout.js | 225 + src/scripts/utils/variables.js | 17 + src/styles/$components/footer.scss | 15 + src/styles/$components/header.scss | 31 + src/styles/$libs.scss | 8 + src/styles/_cores/layout.scss | 55 + src/styles/_libs/.gitkeep | 0 src/styles/_libs/jquery.fullPage.css | 203 + src/styles/_utils/common.scss | 21 + src/styles/_utils/functions.scss | 43 + src/styles/_utils/index.scss | 5 + src/styles/_utils/mixins.scss | 182 + src/styles/_utils/override-grid.scss | 26 + src/styles/_utils/variables.scss | 5 + src/styles/_utils/variables/colors.scss | 2 + src/styles/_utils/variables/fonts.scss | 7 + src/styles/_utils/variables/icomoon.scss | 33 + src/styles/_utils/variables/others.scss | 0 src/styles/_utils/variables/z-indexes.scss | 0 src/styles/apps.scss | 4 + src/views/$modules/back-to-top.pug | 2 + src/views/$modules/footer.pug | 5 + src/views/$modules/header.pug | 12 + src/views/_blocks/favicon.pug | 12 + src/views/_blocks/meta.pug | 8 + src/views/_blocks/scripts.pug | 4 + src/views/_blocks/styles.pug | 9 + src/views/_layouts/layout-component.pug | 154 + src/views/_layouts/layout.pug | 81 + src/views/_mixins/all.pug | 7 + src/views/_mixins/common/cta.pug | 24 + src/views/_mixins/common/img.pug | 13 + src/views/_mixins/common/link.pug | 8 + src/views/_mixins/common/nav.pug | 23 + src/views/_mixins/common/style.pug | 2 + src/views/components/footer.pug | 8 + src/views/components/header.pug | 8 + src/views/index.pug | 10 + src/views/sitemap.pug | 138 + 129 files changed, 10715 insertions(+) create mode 100644 .browserslistrc create mode 100644 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc.js create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 README.md create mode 100644 babel.config.json create mode 100644 gulpfile.js/config/browser-sync-prod.js create mode 100644 gulpfile.js/config/browser-sync.js create mode 100644 gulpfile.js/config/directories.js create mode 100644 gulpfile.js/config/externals-js.js create mode 100644 gulpfile.js/config/nodemon.js create mode 100644 gulpfile.js/config/pug.js create mode 100644 gulpfile.js/config/rename.js create mode 100644 gulpfile.js/config/sass.js create mode 100644 gulpfile.js/config/server.js create mode 100644 gulpfile.js/config/usemin.js create mode 100644 gulpfile.js/config/webpack.js create mode 100644 gulpfile.js/index.js create mode 100644 gulpfile.js/private-tasks/backup--chunk-scripts.js create mode 100644 gulpfile.js/private-tasks/build--assets.js create mode 100644 gulpfile.js/private-tasks/build--locales.js create mode 100644 gulpfile.js/private-tasks/build--scripts-es6.js create mode 100644 gulpfile.js/private-tasks/build--scripts-external.js create mode 100644 gulpfile.js/private-tasks/build--styles-rtl.js create mode 100644 gulpfile.js/private-tasks/build--styles.js create mode 100644 gulpfile.js/private-tasks/build--views-min.js create mode 100644 gulpfile.js/private-tasks/build--views.js create mode 100644 gulpfile.js/private-tasks/bundle--scripts.js create mode 100644 gulpfile.js/private-tasks/bundle--styles.js create mode 100644 gulpfile.js/private-tasks/clean--output.js create mode 100644 gulpfile.js/private-tasks/clean--temp-scripts.js create mode 100644 gulpfile.js/private-tasks/clean--temp-styles.js create mode 100644 gulpfile.js/private-tasks/copy--assets.js create mode 100644 gulpfile.js/private-tasks/lint--scripts.js create mode 100644 gulpfile.js/private-tasks/minify--chunk-scripts.js create mode 100644 gulpfile.js/private-tasks/minify--styles.js create mode 100644 gulpfile.js/private-tasks/print--results.js create mode 100644 gulpfile.js/private-tasks/reload.js create mode 100644 gulpfile.js/private-tasks/run--dev-server.js create mode 100644 gulpfile.js/private-tasks/run--views.js create mode 100644 gulpfile.js/private-tasks/run--watchers.js create mode 100644 gulpfile.js/private-tasks/versionfy.js create mode 100644 gulpfile.js/tasks/build.js create mode 100644 gulpfile.js/tasks/default.js create mode 100644 gulpfile.js/utils/browser-sync.js create mode 100644 gulpfile.js/utils/errors.js create mode 100644 gulpfile.js/utils/index.js create mode 100644 index.html create mode 100644 jsconfig.json create mode 100644 package.json create mode 100644 server/index.js create mode 100644 server/route.js create mode 100644 src/assets/data/.gitkeep create mode 100644 src/assets/favicon/android-chrome-192x192.png create mode 100644 src/assets/favicon/android-chrome-512x512.png create mode 100644 src/assets/favicon/apple-touch-icon.png create mode 100644 src/assets/favicon/browserconfig.xml create mode 100644 src/assets/favicon/favicon-16x16.png create mode 100644 src/assets/favicon/favicon-32x32.png create mode 100644 src/assets/favicon/favicon.ico create mode 100644 src/assets/favicon/mstile-150x150.png create mode 100644 src/assets/favicon/safari-pinned-tab.svg create mode 100644 src/assets/favicon/site.webmanifest create mode 100644 src/assets/fonts/.gitkeep create mode 100644 src/assets/fonts/icomoon/icomoon.eot create mode 100644 src/assets/fonts/icomoon/icomoon.svg create mode 100644 src/assets/fonts/icomoon/icomoon.ttf create mode 100644 src/assets/fonts/icomoon/icomoon.woff create mode 100644 src/assets/images/logo.png create mode 100644 src/assets/images/transparent.png create mode 100644 src/scripts/_libs/.gitkeep create mode 100644 src/scripts/_libs/jquery.fullPage.min.js create mode 100644 src/scripts/_libs/modernizr-custom-3.6.0.js create mode 100644 src/scripts/_libs/select2.js create mode 100644 src/scripts/cores/plugin.js create mode 100644 src/scripts/cores/prototype.js create mode 100644 src/scripts/initializations/browser-detect.js create mode 100644 src/scripts/initializations/import-jquery-plugins.js create mode 100644 src/scripts/initializations/improve-window-events.js create mode 100644 src/scripts/initializations/update-js-assets-path.js create mode 100644 src/scripts/main.js create mode 100644 src/scripts/plugins/back-to-top.js create mode 100644 src/scripts/repositories/api.js create mode 100644 src/scripts/utils/cookies.js create mode 100644 src/scripts/utils/date.js create mode 100644 src/scripts/utils/doms.js create mode 100644 src/scripts/utils/http.js create mode 100644 src/scripts/utils/index.js create mode 100644 src/scripts/utils/layout.js create mode 100644 src/scripts/utils/variables.js create mode 100644 src/styles/$components/footer.scss create mode 100644 src/styles/$components/header.scss create mode 100644 src/styles/$libs.scss create mode 100644 src/styles/_cores/layout.scss create mode 100644 src/styles/_libs/.gitkeep create mode 100644 src/styles/_libs/jquery.fullPage.css create mode 100644 src/styles/_utils/common.scss create mode 100644 src/styles/_utils/functions.scss create mode 100644 src/styles/_utils/index.scss create mode 100644 src/styles/_utils/mixins.scss create mode 100644 src/styles/_utils/override-grid.scss create mode 100644 src/styles/_utils/variables.scss create mode 100644 src/styles/_utils/variables/colors.scss create mode 100644 src/styles/_utils/variables/fonts.scss create mode 100644 src/styles/_utils/variables/icomoon.scss create mode 100644 src/styles/_utils/variables/others.scss create mode 100644 src/styles/_utils/variables/z-indexes.scss create mode 100644 src/styles/apps.scss create mode 100644 src/views/$modules/back-to-top.pug create mode 100644 src/views/$modules/footer.pug create mode 100644 src/views/$modules/header.pug create mode 100644 src/views/_blocks/favicon.pug create mode 100644 src/views/_blocks/meta.pug create mode 100644 src/views/_blocks/scripts.pug create mode 100644 src/views/_blocks/styles.pug create mode 100644 src/views/_layouts/layout-component.pug create mode 100644 src/views/_layouts/layout.pug create mode 100644 src/views/_mixins/all.pug create mode 100644 src/views/_mixins/common/cta.pug create mode 100644 src/views/_mixins/common/img.pug create mode 100644 src/views/_mixins/common/link.pug create mode 100644 src/views/_mixins/common/nav.pug create mode 100644 src/views/_mixins/common/style.pug create mode 100644 src/views/components/footer.pug create mode 100644 src/views/components/header.pug create mode 100644 src/views/index.pug create mode 100644 src/views/sitemap.pug diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 0000000..b1622b5 --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,2 @@ +last 2 major versions +Firefox ESR diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d4a31d9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*{js,pug,scss,json}] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +quote_type = single diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..e69de29 diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..3d75c73 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,197 @@ +module.exports = { + root: true, + env: { + browser: true, + es6: true, + node: true + }, + parser: 'babel-eslint', + parserOptions: { + ecmaFeatures: { + legacyDecorators: true + } + }, + globals: { + $: true, + jQuery: true, + Plugin: true, + __webpack_require__: true + }, + rules: { + // recommended + 'for-direction': 2, + 'getter-return': 2, + 'no-compare-neg-zero': 2, + 'no-cond-assign': [2, 'always'], + 'no-console': [1, { + allow: ['warn', 'error'] + }], + 'no-constant-condition': 2, + 'no-control-regex': 1, + 'no-debugger': 1, + 'no-dupe-args': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-empty': 1, + 'no-empty-character-class': 1, + 'no-ex-assign': 2, + 'no-extra-boolean-cast': 1, + 'no-extra-semi': 2, + 'no-func-assign': 2, + 'no-inner-declarations': 2, + 'no-invalid-regexp': 2, + 'no-irregular-whitespace': 2, + 'no-obj-calls': 2, + 'no-regex-spaces': 1, + 'no-sparse-arrays': 2, + 'no-unexpected-multiline': 2, + 'no-unreachable': 2, + 'no-unsafe-finally': 2, + 'no-unsafe-negation': 2, + 'use-isnan': 1, + 'valid-typeof': [2, { + "requireStringLiterals": true + }], + 'no-case-declarations': 2, + 'no-empty-pattern': 2, + 'no-fallthrough': 2, + 'no-global-assign': 2, + 'no-octal': 2, + 'no-redeclare': [2, { + 'builtinGlobals': true + }], + 'no-self-assign': 2, + 'no-unused-labels': 1, + 'no-useless-escape': 1, + 'no-delete-var': 2, + 'no-undef': 2, + 'no-unused-vars': 1, + 'no-mixed-spaces-and-tabs': 1, + 'constructor-super': 2, + 'no-class-assign': 2, + 'no-const-assign': 2, + 'no-dupe-class-members': 2, + 'no-new-symbol': 2, + 'no-this-before-super': 2, + 'require-yield': 2, + + // error + 'no-async-promise-executor': 2, + 'no-misleading-character-class': 2, + 'require-atomic-updates': 2, + 'accessor-pairs': 2, + 'block-scoped-var': 2, + 'curly': 2, + 'dot-location': [2, 'property'], + 'eqeqeq': [2, 'always'], + 'no-caller': 2, + 'no-eq-null': 2, + 'no-eval': 2, + 'no-implied-eval': 2, + 'no-iterator': 2, + 'no-labels': 2, + 'no-lone-blocks': 2, + 'no-loop-func': 2, + 'no-multi-str': 2, + 'no-new-func': 2, + 'no-new-wrappers': 2, + 'no-octal-escape': 2, + 'no-param-reassign': 2, + 'no-proto': 2, + 'no-return-assign': [2, 'always'], + 'no-return-await': 2, + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-with': 2, + 'vars-on-top': 2, + 'wrap-iife': [2, 'inside', { + functionPrototypeMethods: true + }], + 'no-label-var': 2, + 'no-use-before-define': 2, + + // Warning + 'no-await-in-loop': 1, + 'array-callback-return': 1, + 'class-methods-use-this': 1, + 'consistent-return': [1, { + treatUndefinedAsUnspecified: true + }], + 'dot-notation': [0, { + allowKeywords: false + }], + 'guard-for-in': 1, + 'no-alert': 1, + 'no-div-regex': 1, + 'no-else-return': 1, + 'no-empty-function': 1, + 'no-extra-bind': 1, + 'no-extra-label': 1, + 'no-floating-decimal': 1, + 'no-multi-spaces': [1, { + ignoreEOLComments: true, + exceptions: { + Property: false, + BinaryExpression: true, + VariableDeclarator: true, + ImportDeclaration: true + } + }], + 'no-script-url': 1, + 'no-useless-call': 1, + 'no-useless-concat': 1, + 'no-useless-return': 1, + 'no-void': 1, + 'require-await': 1, + 'no-undef-init': 1, + 'no-undefined': 1, + + // Stylist + 'block-spacing': 1, + 'brace-style': 1, + 'comma-spacing': 1, + 'comma-style': 1, + 'computed-property-spacing': 1, + 'func-call-spacing': 1, + 'indent': [1, 2, { + SwitchCase: 1, + MemberExpression: 'off' + }], + 'key-spacing': 1, + 'keyword-spacing': 1, + 'lines-between-class-members': 1, + 'max-len': [1, { + code: 80, + ignoreRegExpLiterals: true, + ignoreTemplateLiterals: true, + ignoreStrings: true + }], + 'no-lonely-if': 1, + 'no-multi-assign': 1, + 'no-multiple-empty-lines': [1, { + max: 2, + maxEOF: 1, + maxBOF: 0 + }], + 'no-trailing-spaces': 1, + 'no-unneeded-ternary': [1, { + defaultAssignment: false + }], + 'no-whitespace-before-property': 1, + 'quotes': [1, 'single', { + allowTemplateLiterals: true + }], + 'semi-spacing': 1, + 'semi': 1, + 'space-infix-ops': 1, + 'switch-colon-spacing': 1, + 'no-duplicate-imports': [1, { + includeExports: true + }], + 'no-useless-computed-key': 1, + 'no-var': 1, + 'object-shorthand': 1, + 'prefer-template': 1, + 'template-curly-spacing': 1 + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a4cb017 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +*.json text eol=lf +*.js text eol=lf +*.pug text eol=lf +*.scss text eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..af86d0a --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.git +npm-debug.log +.publish +.vscode +static +dist +node_modules +src/locales/*.json +package-lock.json +yarn.lock +.idea/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b0b9c1e --- /dev/null +++ b/README.md @@ -0,0 +1,112 @@ +WEB TEMPLATE +=================== + +Web template use es6, pug, scss + +## 1. Installation +```bash +npm i +``` + +## 2. Configuration +- Every configuration is stored in `./gulpfile.js/config/**` + +## 3. Constructor +```bash +src/ +| assets/ +| | # All files and folders (except site folder) will be copied to /dist +| | favicon/ +| | # All content in this folder will be copied direct to /dist +| | +| | +| scrips/ +| | _lib/ +| | | # Store custom libraries, +| | | # which cannot be downloaded through npm +| | | +| | | +| | | +| | *.js # These files is the entry file to compile with webpack +| | +| | +| styles/ +| | _*/ +| | | # Every files in folders have start name, is _, +| | | # won't be compiled. +| | | # It only use to store many libraries or variables +| | | +| | | +| | $*/ +| | | # Every files in folders have start name, is $, +| | | # won't be compiled. +| | | # It only use to merge into apps.css +| | | +| | | +| | ... # Others will be compiled to /dist/css/ +| | +| | +| views/ +| _*/ +| | # Every files in folders have start name, is _, +| | # won't be compiled. +| | # It only use to store many reusable components +| | +| | +| $*/ +| | # Every files in folders have start name, is $, +| | # won't be compiled. +| | # It only use to store many main/page components +| | +| | +| ... # Others will be compiled to /dist/ +| +| +gulpfile.js/ +| .... # Manage build tasks and configuration +| +| +server/ +| .... # Manage server side render views +| +| +dist/ +| # Store compiled html, css, js (production build) +| +| +static/ +| # Store compiled css, js (work on dev) +| +| +.browserslistrc +.eslintrc.js +.gitignore +.gitattributes +index.html +package.json +README.md +``` + + +## 4. Tasks +- `npm start`: Shorthand for `npm run dev` +- `npm run dev`: Build Project + Watch + Node Server +- `npm run build`: Build Project (min) +- `npm run serve`: Run production local server + + +## 5. Pug global variables +- `$translator`: object variable get value from `app/locales/#{lang}.json` +- `$localeName`: variable get value equal name of current using locale json + + +## 6. JS Dynamic import path (chunks files) +At `src/views/_layouts/layout.pug`, there is a script with variable `staticJsAssetsPath`, update that variable to server js path, and it will load correctly chunks files + + +## 7. Single language mode +Just remove folder `src/locales` + + +## 8. Site favicon +Use this online tool: https://realfavicongenerator.net/ to get the site favicon package and put it in `app/assets/site` diff --git a/babel.config.json b/babel.config.json new file mode 100644 index 0000000..dc1ebf2 --- /dev/null +++ b/babel.config.json @@ -0,0 +1,13 @@ +{ + "presets": ["@babel/preset-env"], + "plugins": [ + ["@babel/plugin-transform-runtime", { + "helpers": true, + "regenerator": true, + "version": "^7.7.4" + }], + ["@babel/plugin-proposal-decorators", { "legacy": true }], + ["@babel/plugin-proposal-class-properties", { "loose": false }], + "@babel/plugin-proposal-private-methods" + ] +} diff --git a/gulpfile.js/config/browser-sync-prod.js b/gulpfile.js/config/browser-sync-prod.js new file mode 100644 index 0000000..480a1b0 --- /dev/null +++ b/gulpfile.js/config/browser-sync-prod.js @@ -0,0 +1,20 @@ +process.env.NODE_ENV = 'production'; + +const compression = require('compression'); + +const { output } = require('./directories'); +const server = require('./server'); + +module.exports = { + server: output, + port: server.PROD_PORT, + ui: { + port: server.PROD_DASHBOARD_PORT, + }, + open: 'local', + ghostMode: false, + logPrefix: 'SYNC', + middleware: [ + compression(), + ], +}; diff --git a/gulpfile.js/config/browser-sync.js b/gulpfile.js/config/browser-sync.js new file mode 100644 index 0000000..d1a0604 --- /dev/null +++ b/gulpfile.js/config/browser-sync.js @@ -0,0 +1,12 @@ +const server = require('./server'); + +module.exports = { + port: server.DEV_PORT, + proxy: `http://localhost:${server.STATIC_PORT}`, + ui: { + port: server.DASHBOARD_PORT, + }, + open: true, + ghostMode: false, + logPrefix: 'SYNC', +}; diff --git a/gulpfile.js/config/directories.js b/gulpfile.js/config/directories.js new file mode 100644 index 0000000..632c921 --- /dev/null +++ b/gulpfile.js/config/directories.js @@ -0,0 +1,77 @@ +const { addPath } = require('../utils'); + +const isProduction = process.env.NODE_ENV === 'production'; +const ignoreTemplate = ['$*/**/*', '_*/**/*']; + +const nodeModules = 'node_modules/'; +exports.nodeModules = nodeModules; + +const src = 'src/'; +exports.src = src; + +const dest = 'static/'; +const dist = 'dist/'; +const output = isProduction ? dist : dest; +exports.output = output; + +const srcLocales = `${src}locales/`; +exports.srcLocales = srcLocales; + +const srcAsset = `${src}assets/`; +exports.srcAsset = srcAsset; + +const filesCopy = addPath(srcAsset, '**', 'favicon/**'); +const filesFavicon = addPath(srcAsset, 'favicon/**'); +const filesAssets = filesCopy + .concat(filesFavicon) + .concat(isProduction ? 'index.html' : []); +exports.filesAssets = filesAssets; + +const srcScript = `${src}scripts/`; +exports.srcScript = srcScript; + +const filesJs = addPath(srcScript, '**/*.js', ignoreTemplate); +exports.filesJs = filesJs; + +const filesJsES6 = `${srcScript}*.js`; +exports.filesJsES6 = filesJsES6; + +const outputScript = `${output}js/`; +exports.outputScript = outputScript; + +const outputChunkScripts = `${outputScript}chunks/`; +exports.outputChunkScripts = outputChunkScripts; + +const filesChunkJs = addPath(outputChunkScripts, '*.js', '*.backup.js'); +exports.filesChunkJs = filesChunkJs; + +const srcStyle = `${src}styles/`; +exports.srcStyle = srcStyle; + +const filesScssBuilt = addPath( + srcStyle, + '**/*.scss', + ['$*/**/*.scss', '_*/**/*.scss'], +); +exports.filesScssBuilt = filesScssBuilt; + +const filesScssPartial = addPath( + srcStyle, + ['$*/**/*.scss', '_*/**/*.scss'], +); +exports.filesScssPartial = filesScssPartial; + +const outputStyle = `${output}css/`; +exports.outputStyle = outputStyle; + +const filesCssBuilt = addPath(outputStyle, '*.css', '*-rtl.css'); +exports.filesCssBuilt = filesCssBuilt; + +const srcView = `${src}views/`; +exports.srcView = srcView; + +const filesPugBuilt = addPath(srcView, '**/*.pug', ['$*/**/*', '_*/**/*']); +exports.filesPugBuilt = filesPugBuilt; + +const filesPug = addPath(srcView, '**/*.pug'); +exports.filesPug = filesPug; diff --git a/gulpfile.js/config/externals-js.js b/gulpfile.js/config/externals-js.js new file mode 100644 index 0000000..51c10a2 --- /dev/null +++ b/gulpfile.js/config/externals-js.js @@ -0,0 +1,8 @@ +const { srcScript } = require('./directories'); + +const libPath = `${srcScript}_libs/`; + +module.exports = [ + `${libPath}modernizr-custom-3.6.0.js`, + `node_modules/detectizr/dist/detectizr.js`, +]; diff --git a/gulpfile.js/config/nodemon.js b/gulpfile.js/config/nodemon.js new file mode 100644 index 0000000..abeca76 --- /dev/null +++ b/gulpfile.js/config/nodemon.js @@ -0,0 +1,6 @@ +module.exports = { + script: 'server/index.js', + watch: [ + 'server/', + ], +}; diff --git a/gulpfile.js/config/pug.js b/gulpfile.js/config/pug.js new file mode 100644 index 0000000..8ca1f17 --- /dev/null +++ b/gulpfile.js/config/pug.js @@ -0,0 +1,4 @@ +module.exports = { + pretty: true, + doctype: 'html', +}; diff --git a/gulpfile.js/config/rename.js b/gulpfile.js/config/rename.js new file mode 100644 index 0000000..986284b --- /dev/null +++ b/gulpfile.js/config/rename.js @@ -0,0 +1,11 @@ +module.exports = { + rtl: { + suffix: '-rtl', + }, + min: { + suffix: '.min', + }, + backup: { + suffix: '.backup', + }, +}; diff --git a/gulpfile.js/config/sass.js b/gulpfile.js/config/sass.js new file mode 100644 index 0000000..3418c00 --- /dev/null +++ b/gulpfile.js/config/sass.js @@ -0,0 +1,3 @@ +module.exports = { + outputStyle: 'expanded', +}; diff --git a/gulpfile.js/config/server.js b/gulpfile.js/config/server.js new file mode 100644 index 0000000..6e4dbef --- /dev/null +++ b/gulpfile.js/config/server.js @@ -0,0 +1,20 @@ +const PORT = 4999; + +module.exports = { + PROD_PORT: 8080, + STATIC_PORT: PORT, + + get DEV_PORT() { + return this.STATIC_PORT + 1; + }, + + get DASHBOARD_PORT() { + return this.STATIC_PORT - 1; + }, + + get PROD_DASHBOARD_PORT() { + return this.PROD_PORT - 1; + }, + + DEFAULT_LANG: 'en', +}; diff --git a/gulpfile.js/config/usemin.js b/gulpfile.js/config/usemin.js new file mode 100644 index 0000000..36947eb --- /dev/null +++ b/gulpfile.js/config/usemin.js @@ -0,0 +1,9 @@ +// Change this value to false if you don't wanna use cache +const { DEFAULT_LANG } = require('./server'); + +module.exports = { + css: `dist/css/styles.min.css?v=${Date.now()}`, + 'css-rtl': `dist/css/styles-rtl.min.css?v=${Date.now()}`, + js: `dist/js/scripts.min.js?v=${Date.now()}`, + redirect: ``, +}; diff --git a/gulpfile.js/config/webpack.js b/gulpfile.js/config/webpack.js new file mode 100644 index 0000000..9ef92c4 --- /dev/null +++ b/gulpfile.js/config/webpack.js @@ -0,0 +1,51 @@ +/* eslint-disable-next-line */ +const { resolve, join } = require('path'); +const { ProvidePlugin } = require('webpack'); + +const { srcScript, output, outputScript } = require('./directories'); + +const nodeEnv = process.env.NODE_ENV; +const isDevelopment = nodeEnv === 'development'; + +module.exports = { + mode: 'none', + context: join(__dirname, '../../', output), + output: { + path: join(__dirname, '../../', outputScript), + filename: '[name].js', + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + loader: 'babel-loader', + options: { + cacheDirectory: true, + }, + }, + ], + }, + resolve: { + alias: { + '@': resolve(srcScript), + }, + }, + plugins: [ + new ProvidePlugin({ + Plugin: ['@/cores/plugin', 'default'], + jQuery: 'jquery', // replace by using external jquery + $: ['jquery'], + }), + ], + optimization: { + nodeEnv, + chunkIds: 'total-size', + concatenateModules: true, + flagIncludedChunks: true, + moduleIds: 'size', + sideEffects: true, + splitChunks: false, + }, + devtool: isDevelopment && 'source-map', +}; diff --git a/gulpfile.js/index.js b/gulpfile.js/index.js new file mode 100644 index 0000000..81095d6 --- /dev/null +++ b/gulpfile.js/index.js @@ -0,0 +1,11 @@ +const requireDir = require('require-dir'); + +const task = process.argv[2]; + +process.env.NODE_ENV = ~['build'].indexOf(task) ? 'production' : 'development'; + +const tasks = requireDir('./tasks'); + +Object.keys(tasks).forEach((name) => { + exports[name] = tasks[name]; +}); diff --git a/gulpfile.js/private-tasks/backup--chunk-scripts.js b/gulpfile.js/private-tasks/backup--chunk-scripts.js new file mode 100644 index 0000000..1cd9e6e --- /dev/null +++ b/gulpfile.js/private-tasks/backup--chunk-scripts.js @@ -0,0 +1,15 @@ +const { src, dest } = require('gulp'); +const rename = require('gulp-rename'); + +const { filesChunkJs, outputChunkScripts } = require('../config/directories'); +const { backup } = require('../config/rename'); + +function backupChunkScripts() { + return src(filesChunkJs) + .pipe(rename(backup)) + .pipe(dest(outputChunkScripts)); +} + +backupChunkScripts.displayName = 'backup:chunk-scripts'; + +module.exports = backupChunkScripts; diff --git a/gulpfile.js/private-tasks/build--assets.js b/gulpfile.js/private-tasks/build--assets.js new file mode 100644 index 0000000..e7b60b7 --- /dev/null +++ b/gulpfile.js/private-tasks/build--assets.js @@ -0,0 +1,20 @@ +const { series } = require('gulp'); +const buildLocales = require('./build--locales'); +const copyAssets = require('./copy--assets'); +const lintScripts = require('./lint--scripts'); +const buildScriptsES6 = require('./build--scripts-es6'); +const buildScriptsExternal = require('./build--scripts-external'); +const buildStyles = require('./build--styles'); +const buildStylesRtl = require('./build--styles-rtl'); + +const buildAssets = series( + buildLocales, + copyAssets, + lintScripts, + buildScriptsES6, + buildScriptsExternal, + buildStyles, + buildStylesRtl, +); + +module.exports = buildAssets; diff --git a/gulpfile.js/private-tasks/build--locales.js b/gulpfile.js/private-tasks/build--locales.js new file mode 100644 index 0000000..e3c4963 --- /dev/null +++ b/gulpfile.js/private-tasks/build--locales.js @@ -0,0 +1,37 @@ +const { src, dest, parallel } = require('gulp'); +const merge = require('gulp-merge-json'); + +const { srcLocales } = require('../config/directories'); +const { getFolders } = require('../utils'); +const { handleError } = require('../utils/errors'); + +function generateBuildTmpFn(folder) { + function buildLocale() { + return src(`${srcLocales}/${folder}/**/*.json`) + .pipe(merge({ + fileName: `${folder}.json`, + jsonSpace: ' ', + })) + .on('error', handleError) + .pipe(dest(srcLocales)); + } + + buildLocale.displayName = `build:locale:${folder}`; + + return buildLocale; +} + +function buildLocales(cb) { + const folders = getFolders(srcLocales); + + if (folders[0]) { + process.env.MULTI_LANGUAGE = folders; + return parallel(...folders.map(generateBuildTmpFn))(cb); + } + + return cb(); +} + +buildLocales.displayName = 'build:locales'; + +module.exports = buildLocales; diff --git a/gulpfile.js/private-tasks/build--scripts-es6.js b/gulpfile.js/private-tasks/build--scripts-es6.js new file mode 100644 index 0000000..963d5bf --- /dev/null +++ b/gulpfile.js/private-tasks/build--scripts-es6.js @@ -0,0 +1,24 @@ +const { src, dest } = require('gulp'); +const gulpWebpack = require('webpack-stream'); +const webpack = require('webpack'); +const vinylNamed = require('vinyl-named'); + +const { filesJsES6, outputScript } = require('../config/directories'); +const option = require('../config/webpack'); +const { list, handleError } = require('../utils/errors'); + +function buildScriptsES6(cb) { + if (!list.isJSValid) { + return cb(); + } + + return src(filesJsES6) + .pipe(vinylNamed()) + .pipe(gulpWebpack(option, webpack)) + .on('error', handleError) + .pipe(dest(outputScript)); +} + +buildScriptsES6.displayName = 'build:scripts-es6'; + +module.exports = buildScriptsES6; diff --git a/gulpfile.js/private-tasks/build--scripts-external.js b/gulpfile.js/private-tasks/build--scripts-external.js new file mode 100644 index 0000000..3602c4f --- /dev/null +++ b/gulpfile.js/private-tasks/build--scripts-external.js @@ -0,0 +1,22 @@ +const { src, dest } = require('gulp'); +const concat = require('gulp-concat'); + +const jsExternalPaths = require('../config/externals-js'); +const { outputScript } = require('../config/directories'); +const { handleError } = require('../utils/errors'); + +const isDevelopment = process.env.NODE_ENV !== 'production'; + +/** + * Building libraries scripts + */ +function buildScriptsExternal () { + return src(jsExternalPaths, { sourcemaps: isDevelopment }) + .pipe(concat('externals.js')) + .on('error', handleError) + .pipe(dest(outputScript, { sourcemaps: isDevelopment && '.' })); +} + +buildScriptsExternal.displayName = 'build:scripts-external'; + +module.exports = buildScriptsExternal; diff --git a/gulpfile.js/private-tasks/build--styles-rtl.js b/gulpfile.js/private-tasks/build--styles-rtl.js new file mode 100644 index 0000000..e93e6a4 --- /dev/null +++ b/gulpfile.js/private-tasks/build--styles-rtl.js @@ -0,0 +1,27 @@ +const { src, dest, lastRun } = require('gulp'); +const rtlcss = require('gulp-rtlcss'); +const rename = require('gulp-rename'); + +const { + outputStyle, + filesCssBuilt, +} = require('../config/directories'); +const { rtl: renameOpts } = require('../config/rename'); +const browserSync = require('../utils/browser-sync'); +const { handleError } = require('../utils/errors'); + +function buildStylesRtl() { + return src(filesCssBuilt, { + since: lastRun(buildStylesRtl), + }) + .pipe(rtlcss()) + .on('error', handleError) + .pipe(rename(renameOpts)) + .on('error', handleError) + .pipe(dest(outputStyle)) + .pipe(browserSync.stream()); +} + +buildStylesRtl.displayName = 'build:styles-rtl'; + +module.exports = buildStylesRtl; diff --git a/gulpfile.js/private-tasks/build--styles.js b/gulpfile.js/private-tasks/build--styles.js new file mode 100644 index 0000000..9ac8604 --- /dev/null +++ b/gulpfile.js/private-tasks/build--styles.js @@ -0,0 +1,35 @@ +const { src, dest } = require('gulp'); +// const sass = require('sass'); +// const gulpSass = require('gulp-sass'); +const sass = require('gulp-sass')(require('sass')); +const cached = require('gulp-cached'); +const sassUnicode = require('gulp-sass-unicode'); +const autoprefixer = require('gulp-autoprefixer'); +// const header = require('gulp-header'); + +// gulpSass.compiler = sass; + +const { + filesScssBuilt, + outputStyle, +} = require('../config/directories'); +const sassOpts = require('../config/sass'); +const browserSync = require('../utils/browser-sync'); +const { handleError } = require('../utils/errors'); + +function buildStyles() { + return src(filesScssBuilt) + .pipe(cached('scss')) + .pipe(sass(sassOpts)) + .on('error', handleError) + .pipe(sassUnicode()) + .on('error', handleError) + .pipe(autoprefixer()) + .on('error', handleError) + .pipe(dest(outputStyle)) + .pipe(browserSync.stream()); +} + +buildStyles.displayName = 'build:styles'; + +module.exports = buildStyles; diff --git a/gulpfile.js/private-tasks/build--views-min.js b/gulpfile.js/private-tasks/build--views-min.js new file mode 100644 index 0000000..22d3ef8 --- /dev/null +++ b/gulpfile.js/private-tasks/build--views-min.js @@ -0,0 +1,20 @@ +const gulp = require('gulp'); +const htmlReplace = require('gulp-html-replace'); + +const useminOpts = require('../config/usemin'); +const { output } = require('../config/directories'); +const { handleError } = require('../utils/errors'); + +function buildViewsMin() { + return gulp + .src(`${output}**/*.html`) + .pipe(htmlReplace(useminOpts, { + resolvePaths: true, + })) + .on('error', handleError) + .pipe(gulp.dest(output)); +} + +buildViewsMin.displayName = 'build:views-min'; + +module.exports = buildViewsMin; diff --git a/gulpfile.js/private-tasks/build--views.js b/gulpfile.js/private-tasks/build--views.js new file mode 100644 index 0000000..fc992c0 --- /dev/null +++ b/gulpfile.js/private-tasks/build--views.js @@ -0,0 +1,47 @@ +const { src, dest, parallel } = require('gulp'); +const pug = require('gulp-pug'); + +const { srcLocales, filesPugBuilt, output } = require('../config/directories'); +const options = require('../config/pug'); +const { handleError } = require('../utils/errors'); + +function generateBuildTmpFn(lang) { + let outputPath = output; + const pugOpts = { + ...options, + locals: { + $translator: {}, + $localeName: lang, + }, + }; + + if (lang) { + outputPath = `${output + lang}/`; + /* eslint-disable-next-line */ + pugOpts.locals.$translator = require(`../../${srcLocales + lang}.json`); + } + + function buildView() { + return src(filesPugBuilt) + .pipe(pug(pugOpts)) + .on('error', handleError) + .pipe(dest(outputPath)); + } + + buildView.displayName = `build:views:${lang || 'single-lang'}`; + return buildView; +} + +function buildViews(cb) { + const folders = process.env.MULTI_LANGUAGE; + + if (folders) { + return parallel(...folders.split(',').map(generateBuildTmpFn))(cb); + } + + return generateBuildTmpFn()(); +} + +buildViews.displayName = 'build:views'; + +module.exports = buildViews; diff --git a/gulpfile.js/private-tasks/bundle--scripts.js b/gulpfile.js/private-tasks/bundle--scripts.js new file mode 100644 index 0000000..87dcba0 --- /dev/null +++ b/gulpfile.js/private-tasks/bundle--scripts.js @@ -0,0 +1,27 @@ +const { src, dest } = require('gulp'); +const concat = require('gulp-concat'); +const rename = require('gulp-rename'); +const uglify = require('gulp-uglify'); + +const { outputScript } = require('../config/directories'); +const { handleError } = require('../utils/errors'); +const { min } = require('../config/rename'); + +function bundleScripts() { + return src([`${outputScript}externals.js`, `${outputScript}*.js`]) + .pipe(concat('scripts.js')) + .on('error', handleError) + .pipe(dest(outputScript)) + .pipe(rename(min)) + .pipe(uglify({ + mangle: { + keep_fnames: true, + }, + })) + .on('error', handleError) + .pipe(dest(outputScript)); +} + +bundleScripts.displayName = 'bundle:scripts'; + +module.exports = bundleScripts; diff --git a/gulpfile.js/private-tasks/bundle--styles.js b/gulpfile.js/private-tasks/bundle--styles.js new file mode 100644 index 0000000..cbe976f --- /dev/null +++ b/gulpfile.js/private-tasks/bundle--styles.js @@ -0,0 +1,25 @@ +const { src, dest } = require('gulp'); +const concat = require('gulp-concat'); + +const { outputStyle } = require('../config/directories'); +const { handleError } = require('../utils/errors'); + +function bundleStyles(isRTL) { + const suffix = isRTL ? '-rtl' : ''; + const libsCss = `${outputStyle}$libs${suffix}.css`; + const appsCss = `${outputStyle}apps${suffix}.css`; + const concatCss = `styles${suffix}.css`; + + function bundleStyle() { + return src([libsCss, appsCss]) + .pipe(concat(concatCss)) + .on('error', handleError) + .pipe(dest(outputStyle)); + } + + bundleStyle.displayName = `bundle:styles:${isRTL ? 'rtl' : 'origin'}`; + + return bundleStyle; +} + +module.exports = bundleStyles; diff --git a/gulpfile.js/private-tasks/clean--output.js b/gulpfile.js/private-tasks/clean--output.js new file mode 100644 index 0000000..e375ced --- /dev/null +++ b/gulpfile.js/private-tasks/clean--output.js @@ -0,0 +1,9 @@ +const del = require('del'); + +const { output } = require('../config/directories'); + +const cleanOutput = () => del(output); + +cleanOutput.displayName = 'clean:output'; + +module.exports = cleanOutput; diff --git a/gulpfile.js/private-tasks/clean--temp-scripts.js b/gulpfile.js/private-tasks/clean--temp-scripts.js new file mode 100644 index 0000000..930d79b --- /dev/null +++ b/gulpfile.js/private-tasks/clean--temp-scripts.js @@ -0,0 +1,14 @@ +const del = require('del'); + +const { outputScript } = require('../config/directories'); + +const cleanTempScripts = () => del([ + `${outputScript}*.js`, + `${outputScript}*.map`, + `!${outputScript}scripts.js`, + `!${outputScript}scripts.min.js`, +]); + +cleanTempScripts.displayName = 'clean:temp-scripts'; + +module.exports = cleanTempScripts; diff --git a/gulpfile.js/private-tasks/clean--temp-styles.js b/gulpfile.js/private-tasks/clean--temp-styles.js new file mode 100644 index 0000000..5d4976b --- /dev/null +++ b/gulpfile.js/private-tasks/clean--temp-styles.js @@ -0,0 +1,14 @@ +const del = require('del'); + +const { outputStyle } = require('../config/directories'); + +const cleanTempStyles = () => del([ + `${outputStyle}$libs.css`, + `${outputStyle}$libs-rtl.css`, + `${outputStyle}apps.css`, + `${outputStyle}apps-rtl.css`, +]); + +cleanTempStyles.displayName = 'clean:temp-styles'; + +module.exports = cleanTempStyles; diff --git a/gulpfile.js/private-tasks/copy--assets.js b/gulpfile.js/private-tasks/copy--assets.js new file mode 100644 index 0000000..de59add --- /dev/null +++ b/gulpfile.js/private-tasks/copy--assets.js @@ -0,0 +1,12 @@ +const { src, dest, lastRun } = require('gulp'); +const { filesAssets, output } = require('../config/directories'); + +function copyAssets() { + return src(filesAssets, { + since: lastRun(copyAssets), + }).pipe(dest(output)); +} + +copyAssets.displayName = 'copy:assets'; + +module.exports = copyAssets; diff --git a/gulpfile.js/private-tasks/lint--scripts.js b/gulpfile.js/private-tasks/lint--scripts.js new file mode 100644 index 0000000..6095589 --- /dev/null +++ b/gulpfile.js/private-tasks/lint--scripts.js @@ -0,0 +1,26 @@ +const { src } = require('gulp'); +const eslint = require('gulp-eslint'); +const cached = require('gulp-cached'); + +const { filesJs } = require('../config/directories'); +const { handleESLintError } = require('../utils/errors'); +const stream = require('../utils/browser-sync'); + +function lintScripts() { + const gulpInstance = src(filesJs) + .pipe(cached('eslint')) + .pipe(eslint()) + .pipe(eslint.results(handleESLintError)); + + if (stream.isStreaming) { + return gulpInstance; + } + + return gulpInstance + .pipe(eslint.format()) + .pipe(eslint.failOnError()); +} + +lintScripts.displayName = 'lint:scripts'; + +module.exports = lintScripts; diff --git a/gulpfile.js/private-tasks/minify--chunk-scripts.js b/gulpfile.js/private-tasks/minify--chunk-scripts.js new file mode 100644 index 0000000..8bcbf01 --- /dev/null +++ b/gulpfile.js/private-tasks/minify--chunk-scripts.js @@ -0,0 +1,20 @@ +const { src, dest } = require('gulp'); +const uglify = require('gulp-uglify'); + +const { filesChunkJs, outputChunkScripts } = require('../config/directories'); +const { handleError } = require('../utils/errors'); + +function minifyChunkScripts() { + return src(filesChunkJs) + .pipe(uglify({ + mangle: { + keep_fnames: true, + }, + })) + .on('error', handleError) + .pipe(dest(outputChunkScripts)); +} + +minifyChunkScripts.displayName = 'minify:chunk-scripts'; + +module.exports = minifyChunkScripts; diff --git a/gulpfile.js/private-tasks/minify--styles.js b/gulpfile.js/private-tasks/minify--styles.js new file mode 100644 index 0000000..5d89fe6 --- /dev/null +++ b/gulpfile.js/private-tasks/minify--styles.js @@ -0,0 +1,19 @@ +const { src, dest } = require('gulp'); +const rename = require('gulp-rename'); +const cleanCss = require('gulp-clean-css'); + +const { outputStyle } = require('../config/directories'); +const { min } = require('../config/rename'); +const { handleError } = require('../utils/errors'); + +function minifyStyles() { + return src(`${outputStyle}**/*.css`) + .pipe(rename(min)) + .pipe(cleanCss()) + .on('error', handleError) + .pipe(dest(outputStyle)); +} + +minifyStyles.displayName = 'minify:styles'; + +module.exports = minifyStyles; diff --git a/gulpfile.js/private-tasks/print--results.js b/gulpfile.js/private-tasks/print--results.js new file mode 100644 index 0000000..4bfe960 --- /dev/null +++ b/gulpfile.js/private-tasks/print--results.js @@ -0,0 +1,64 @@ +const notify = require('gulp-notify'); +const stripIndent = require('strip-indent'); + +const { pluralText } = require('../utils'); +const errors = require('../utils/errors'); + +const { log } = console; + +notify.logLevel(0); + +function printResults(cb) { + const errList = errors.list; + const { + totalError, totalWarning, totalIssue, data, + } = errList; + const errorString = `${totalError} ${pluralText('error', totalError)}`; + const warningString = `${totalWarning} ${pluralText('warning', totalWarning)}`; + const resultString = `The project has ${errorString} & ${warningString}`; + const resultStr = resultString; // Improve Performance Node + const dashChar = ''.padEnd(resultStr.length + 4, '='); + + log(stripIndent(` + ${dashChar} + + ${resultStr} + + ${dashChar} + `)); + + if (!totalIssue) { + return cb(); + } + + const infoLogs = stripIndent(data.map(({ + message, code, title, type, + }, i) => { + if (type !== 'Warning') { + notify.onError({ + title, + message, + })(); + } + + return ` + ---[ ${type} ${i + 1} ]------------------------- + | Path : ${message} + | ${code} + `; + }).join(` + `)); + + log((` + ${infoLogs} + ${dashChar} + `).replace(/^ {2}/gm, '')); + + errors.resetError(); + + return cb(); +} + +printResults.displayName = 'print:results'; + +module.exports = printResults; diff --git a/gulpfile.js/private-tasks/reload.js b/gulpfile.js/private-tasks/reload.js new file mode 100644 index 0000000..a45c895 --- /dev/null +++ b/gulpfile.js/private-tasks/reload.js @@ -0,0 +1,10 @@ +const browserSync = require('../utils/browser-sync'); + +function reload(cb) { + browserSync.reload(); + cb(); +} + +reload.displayName = 'reload'; + +module.exports = reload; diff --git a/gulpfile.js/private-tasks/run--dev-server.js b/gulpfile.js/private-tasks/run--dev-server.js new file mode 100644 index 0000000..130d23a --- /dev/null +++ b/gulpfile.js/private-tasks/run--dev-server.js @@ -0,0 +1,8 @@ +const { parallel } = require('gulp'); + +const runViews = require('./run--views'); +const runWatchers = require('./run--watchers'); + +const runDevServer = parallel(runViews, runWatchers); + +module.exports = runDevServer; diff --git a/gulpfile.js/private-tasks/run--views.js b/gulpfile.js/private-tasks/run--views.js new file mode 100644 index 0000000..4465ab6 --- /dev/null +++ b/gulpfile.js/private-tasks/run--views.js @@ -0,0 +1,35 @@ +const nodemon = require('gulp-nodemon'); + +const browserSync = require('../utils/browser-sync'); +const options = require('../config/nodemon'); +const browserSyncOpts = require('../config/browser-sync'); + +function runViews(cb) { + let started = false; + + nodemon({ + ...options, + env: { + MULTI_LANGUAGE: process.env.MULTI_LANGUAGE, + }, + }) + .on('start', () => { + if (started) { + cb(); + return; + } + + started = true; + + setTimeout(() => { + browserSync.isStreaming = true; + browserSync.init(browserSyncOpts); + + cb(); + }, 1000); + }); +} + +runViews.displayName = 'run:views'; + +module.exports = runViews; diff --git a/gulpfile.js/private-tasks/run--watchers.js b/gulpfile.js/private-tasks/run--watchers.js new file mode 100644 index 0000000..e827339 --- /dev/null +++ b/gulpfile.js/private-tasks/run--watchers.js @@ -0,0 +1,71 @@ +const { series, parallel, watch } = require('gulp'); +const cached = require('gulp-cached'); + +const reload = require('./reload'); +const buildLocales = require('./build--locales'); +const printResults = require('./print--results'); +const buildStyles = require('./build--styles'); +const buildScriptsExternal = require('./build--scripts-external'); +const lintScripts = require('./lint--scripts'); +const buildScriptsES6 = require('./build--scripts-es6'); +const copyAssets = require('./copy--assets'); + +const { + filesPug, + srcLocales, + filesScssBuilt, + filesScssPartial, + filesJs, + filesAssets, +} = require('../config/directories'); +const jsExternalPaths = require('../config/externals-js'); + +function runWatchers(cb) { + const reloadAndShowResults = parallel(printResults, reload); + + // HTML -------------------- + watch(filesPug, reload); + + // LOCALE -------------------- + watch(`${srcLocales}*/*.json`, series( + buildLocales, + reloadAndShowResults, + )); + + // CSS -------------------- + watch(filesScssBuilt, series( + buildStyles, + printResults, + )); + + watch(filesScssPartial, series( + buildStyles, + printResults, + )).on('change', () => delete cached.caches.scss); + + // JS -------------------- + watch(jsExternalPaths, series( + buildScriptsExternal, + reloadAndShowResults + )); + + watch(filesJs, series( + lintScripts, + buildScriptsES6, + reloadAndShowResults, + )); + + // ASSETS -------------------- + watch(filesAssets, series( + copyAssets, + reloadAndShowResults, + )); + + if (typeof cb === 'function') { + cb(); + } +} + +runWatchers.displayName = 'run:watchers'; + +module.exports = runWatchers; diff --git a/gulpfile.js/private-tasks/versionfy.js b/gulpfile.js/private-tasks/versionfy.js new file mode 100644 index 0000000..3e03a63 --- /dev/null +++ b/gulpfile.js/private-tasks/versionfy.js @@ -0,0 +1,15 @@ +const { src, dest } = require('gulp'); +const header = require('gulp-header'); +const dayjs = require('dayjs'); + +const { outputScript, outputStyle } = require('../config/directories'); + +function versionfy() { + return src([`${outputScript}/**/*.js`, `${outputStyle}/**/*.css`]) + .pipe(header(`/* version: ${dayjs().format('DD-MM-YYYY HH:mm:ss')} */`)) + .pipe(dest((file) => file.base)); +} + +versionfy.displayName = 'versionfy'; + +module.exports = versionfy; diff --git a/gulpfile.js/tasks/build.js b/gulpfile.js/tasks/build.js new file mode 100644 index 0000000..e3b07d9 --- /dev/null +++ b/gulpfile.js/tasks/build.js @@ -0,0 +1,34 @@ +const { series } = require('gulp'); + +const cleanOutput = require('../private-tasks/clean--output'); +const buildAssets = require('../private-tasks/build--assets'); +const bundleScripts = require('../private-tasks/bundle--scripts'); +const cleanTempScripts = require('../private-tasks/clean--temp-scripts'); +const backupChunkScripts = require('../private-tasks/backup--chunk-scripts'); +const minifyChunkScripts = require('../private-tasks/minify--chunk-scripts'); +const bundleStyles = require('../private-tasks/bundle--styles'); +const cleanTempStyles = require('../private-tasks/clean--temp-styles'); +const minifyStyles = require('../private-tasks/minify--styles'); +const versionfy = require('../private-tasks/versionfy'); +const buildViews = require('../private-tasks/build--views'); +const buildViewsMin = require('../private-tasks/build--views-min'); +const printResults = require('../private-tasks/print--results'); + +const tasks = [ + cleanOutput, + buildAssets, + bundleScripts, + cleanTempScripts, + backupChunkScripts, + minifyChunkScripts, + bundleStyles(), + bundleStyles(true), + cleanTempStyles, + minifyStyles, + versionfy, + buildViews, + buildViewsMin, + printResults, +]; + +module.exports = series(...tasks); diff --git a/gulpfile.js/tasks/default.js b/gulpfile.js/tasks/default.js new file mode 100644 index 0000000..d9b6883 --- /dev/null +++ b/gulpfile.js/tasks/default.js @@ -0,0 +1,15 @@ +const { series } = require('gulp'); + +const cleanOutput = require('../private-tasks/clean--output'); +const buildAssets = require('../private-tasks/build--assets'); +const printResults = require('../private-tasks/print--results'); +const runDevServer = require('../private-tasks/run--dev-server'); + +const tasks = [ + cleanOutput, + buildAssets, + printResults, + runDevServer, +]; + +module.exports = series(...tasks); diff --git a/gulpfile.js/utils/browser-sync.js b/gulpfile.js/utils/browser-sync.js new file mode 100644 index 0000000..3c6c5bb --- /dev/null +++ b/gulpfile.js/utils/browser-sync.js @@ -0,0 +1,7 @@ +const browserSync = require('browser-sync'); + +const browserSyncInstance = browserSync.create(); + +browserSyncInstance.isStreaming = false; + +module.exports = browserSyncInstance; diff --git a/gulpfile.js/utils/errors.js b/gulpfile.js/utils/errors.js new file mode 100644 index 0000000..4951bfc --- /dev/null +++ b/gulpfile.js/utils/errors.js @@ -0,0 +1,63 @@ +const cached = require('gulp-cached'); + +const list = { + get totalIssue() { + return this.totalError + this.totalWarning; + }, +}; +exports.list = list; + +function pushError(info) { + list.data.push(info); + list[info.type === 'Error' ? 'totalError' : 'totalWarning'] += 1; +} + +function handleError({ plugin, message, codeFrame = '' } = {}) { + pushError({ + type: 'Error', + plugin: plugin.toUpperCase(), + message: message.trim(), + code: codeFrame, + }); + + if (typeof this.emit === 'function') { + this.emit('end'); + } else if (typeof this === 'function') { + this(); + } +} + +exports.handleError = handleError; + +exports.handleESLintError = (results) => { + results.forEach(({ filePath, messages } = {}) => { + messages.forEach(({ + severity, line, column, message, + } = {}) => { + const type = severity === 2 ? 'Error' : 'Warning'; + + pushError({ + type, + plugin: `ES Lint ${type}`, + message: filePath, + code: `[${line}:${column}] ${message}`, + }); + }); + }); + + list.isJSValid = !results.errorCount; + + if (results.errorCount || results.warningCount) { + delete cached.caches.eslint; + } +}; + +function resetError() { + list.data = []; + list.totalError = 0; + list.totalWarning = 0; + list.isJSValid = true; +} +exports.resetError = resetError; + +resetError(); diff --git a/gulpfile.js/utils/index.js b/gulpfile.js/utils/index.js new file mode 100644 index 0000000..6109088 --- /dev/null +++ b/gulpfile.js/utils/index.js @@ -0,0 +1,46 @@ +const { readdirSync, lstatSync } = require('fs'); + +const pushPath = (src, paths, wontInclude) => { + const not = wontInclude ? '!' : ''; + + return [].concat(paths || []).map((item) => not + src + item); +}; + +exports.addPath = (src, paths, notIncludePaths) => { + if (typeof src === 'undefined') { + return '**/*'; + } + + if (typeof paths === 'undefined') { + return `${src}**/*`; + } + + const includePaths = pushPath(src, paths, false); + const notICPath = pushPath(src, notIncludePaths, true); + + return includePaths.concat(notICPath); +}; + +exports.getFolders = (dir) => { + try { + return readdirSync(dir) + .filter((folder) => lstatSync(dir + folder).isDirectory()); + } catch (err) { + return []; + } +}; + +exports.pluralText = (text, number) => text + (number > 1 ? 's' : ''); + +exports.renderErrorHTML = (msg) => ` + + +
${msg}
+ +`; diff --git a/index.html b/index.html new file mode 100644 index 0000000..2544814 --- /dev/null +++ b/index.html @@ -0,0 +1 @@ + diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..3d611c9 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "baseUrl": "./", + "paths": { + "@/*": ["./src/scripts/*"] + } + }, + "exclude": ["node_modules"] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d700b6e --- /dev/null +++ b/package.json @@ -0,0 +1,96 @@ +{ + "name": "web-template", + "title": "Web Template", + "description": "Web template use es6, pug, scss, bootstrap", + "version": "4.0.0", + "author": "", + "homepage": "", + "license": "MIT", + "repository": { + "type": "git", + "url": "" + }, + "keywords": [ + "web", + "template", + "es6", + "pug", + "scss", + "bootstrap" + ], + "engines": { + "node": ">=10", + "npm": ">=7" + }, + "scripts": { + "list-tasks": "gulp --tasks", + "start": "gulp", + "dev": "gulp", + "build": "gulp build", + "serve": "browser-sync start -c 'gulpfile.js/config/browser-sync-prod.js'", + "upload": "gulp build && gh-pages -d dist" + }, + "dependencies": { + "@babel/runtime": "7.13.17", + "bootstrap": "^5.1.3", + "bootstrap-datepicker": "^1.9.0", + "bootstrap-select": "^1.13.18", + "dayjs": "1.10.4", + "debounce": "^1.2.1", + "detectizr": "^2.2.0", + "html-to-image": "^1.9.0", + "jquery": "^3.6.0", + "jquery-mask-plugin": "^1.14.16", + "jquery-validation": "^1.19.3", + "moment": "^2.29.1", + "parsleyjs": "^2.9.2", + "slick-carousel": "^1.8.1" + }, + "devDependencies": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-pipeline-operator": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.6.2", + "@babel/preset-env": "^7.16.4", + "@babel/runtime": "^7.6.2", + "babel-eslint": "^10.0.3", + "babel-loader": "^8.2.3", + "browser-sync": "^2.27.7", + "compression": "^1.7.4", + "del": "^6.0.0", + "eslint-config-airbnb-base": "14.2.1", + "eslint-import-resolver-webpack": "0.13.0", + "eslint-plugin-import": "2.22.1", + "express": "4.17.1", + "gh-pages": "^3.2.3", + "gulp": "^4.0.2", + "gulp-autoprefixer": "^6.1.0", + "gulp-cached": "1.1.1", + "gulp-clean-css": "4.3.0", + "gulp-cli": "2.3.0", + "gulp-concat": "2.6.1", + "gulp-eslint": "6.0.0", + "gulp-header": "2.0.9", + "gulp-html-replace": "1.6.2", + "gulp-merge-json": "2.1.1", + "gulp-nodemon": "^2.2.1", + "gulp-notify": "^4.0.0", + "gulp-pug": "^5.0.0", + "gulp-rename": "2.0.0", + "gulp-rtlcss": "^2.0.0", + "gulp-sass": "^5.1.0", + "gulp-sass-unicode": "^1.0.2", + "gulp-uglify": "3.0.2", + "pug": "3.0.2", + "require-dir": "1.2.0", + "sass": "^1.51.0", + "strip-indent": "3.0.0", + "vinyl-named": "1.1.0", + "webpack": "^5.65.0", + "webpack-stream": "6.1.2" + } +} diff --git a/server/index.js b/server/index.js new file mode 100644 index 0000000..563c686 --- /dev/null +++ b/server/index.js @@ -0,0 +1,35 @@ +const express = require('express'); +const pug = require('pug'); + +const route = require('./route'); +const { STATIC_PORT } = require('../gulpfile.js/config/server'); +const { srcView, output } = require('../gulpfile.js/config/directories'); +const pugOptions = require('../gulpfile.js/config/pug'); +const { renderErrorHTML } = require('../gulpfile.js/utils'); + +const app = express(); +app.locals.moment = require('moment'); +const { log } = console; + +app.engine('pug', (path, options, callback) => { + const opts = { ...pugOptions, ...options }; + + pug.renderFile(path, opts, (err, result) => { + const data = result || renderErrorHTML(err.message); + + callback(null, data); + }); +}); + +app.set('views', srcView); +app.set('view engine', 'pug'); +app.use(express.static(output)); +app.use(express.json()); +app.use(express.urlencoded({ + extended: true, +})); +app.use('/', route); + +const logPort = `----- View server is running at http://localhost:${STATIC_PORT} -----`; + +app.listen(STATIC_PORT, () => log(logPort)); diff --git a/server/route.js b/server/route.js new file mode 100644 index 0000000..c6487fd --- /dev/null +++ b/server/route.js @@ -0,0 +1,83 @@ +/* eslint-disable-next-line */ +const { join } = require('path'); +const express = require('express'); + +const router = express.Router(); +const { output, srcLocales } = require('../gulpfile.js/config/directories'); +const { renderErrorHTML } = require('../gulpfile.js/utils'); +const { DEFAULT_LANG } = require('../gulpfile.js/config/server'); + +const multiLang = process.env.MULTI_LANGUAGE; + +function forceRequire(path) { + const realPath = join(__dirname, path); + + delete require.cache[realPath]; + + /* eslint-disable-next-line */ + return require(path); +} + +router.get('/', (_, res) => { + const defautLangPath = multiLang ? `/${DEFAULT_LANG}` : ''; + + res.redirect(`${defautLangPath}/index.html`); +}); + +router.get('/*.html', (req, res) => { + try { + let lang; + let match; + let localeLang; + let { path: url } = req; + + if (multiLang) { + const testLang = /^\/([^/]+)\//.exec(url); + + if (!testLang) { + throw new Error('No language in the url'); + } + + [match, lang] = testLang; + localeLang = forceRequire(`../${srcLocales + lang}.json`); + + url = url.replace(match, ''); + } + + const testFile = /[/]?(.+)\.html/.exec(url); + + if (!testFile) { + throw new Error('Not found'); + } + + res.render(testFile[1], { + $translator: localeLang || {}, + $localeName: lang, + $path: req.url, + }, (err, html) => { + if (err) { + throw err; + } + + res.send(html); + }); + } catch (err) { + res.send(renderErrorHTML(err)).status(404); + } +}); + +router.get(/^\/.*[^(.html)]$/, (req, res) => { + res.redirect(join(req.path, 'index.html')); +}); + +router.post('*', (req, res) => { + try { + const json = forceRequire(join(__dirname, '..', output, req.url)); + + res.send(json); + } catch (err) { + res.send(renderErrorHTML(err)).status(404); + } +}); + +module.exports = router; diff --git a/src/assets/data/.gitkeep b/src/assets/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/assets/favicon/android-chrome-192x192.png b/src/assets/favicon/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..71ed165587cc6323e676ccefeefbac2250412310 GIT binary patch literal 13644 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
SR5 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 d(4LDU6%Rn~)&{We^tx>jp{Xb*T84~~i literal 0 HcmV?d00001 diff --git a/src/assets/favicon/browserconfig.xml b/src/assets/favicon/browserconfig.xml new file mode 100644 index 0000000..249c5c1 --- /dev/null +++ b/src/assets/favicon/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #ffc40d + + + diff --git a/src/assets/favicon/favicon-16x16.png b/src/assets/favicon/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..c5a2c6da3d16727e91c514291a312d54c3f29c86 GIT binary patch literal 970 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>dgwXSYl}2uODR^tgAXL&d4%WIB9XDviPoV1vg9Mky5~H0?oHB3xL$Y`a>KBJGS=ma|aY^{5bze;@S77iv^z>VWXXE+* zssam|Dl}v|CmFJA`RHOa_1uNX2epoR@e8_Ct-{kTv~3Jpd-bK;iEBhjN@7W>RdP`(kYX@0Ff!6LFw-?M3NbLZGB&g_GSD_Kure@M z`b+jciiX_$l+3hB+#0SdUz!Zm5MC7$Q4*9`u24{vpO%@Es!&o{kgAYbPy&>!nDh7( z4@Y5`hQ=xX(`P)N1~D)zbL%B@3o8qIPZnVoR&Z%BIh?|*yg5YS^o=Vgj+{9nbA!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+081JlX?pAc7|f|qGJFVl5i zrfT1|jPcwWNr}HRY=S7On%QW4Wsk%?nHC_N2 z>AEj6wBO|Dz0a|Jk*fVFRp$jz3(&N|h;#>^tLPk7B!)mL(0L?kp*Y@wsR%uqQbEb- z|NsAc-yMqpCf0`~L4LtN8W-@`Ym~K7CgQ2P<&$Hop}sDUk1F^*JFFh`;FQ+AD66~Y z^q3SqZ(lT=bj4JTktNzWZW=#h0LP}-cWZfGE!H{H{&KtQ5w&TEM^>ApWfyLyTPYuQYj zTbET1J!dKWJkR2~c`$R%A|Vs&RC`9I^pDF$zGWx9Un8b_c7x=}@?Se6-)xokE;#sN zgRuAe?Nw}dHJ?1>T3hm1+y?kR0+ z?*iuFU{4pv5R22v2@T9{nUaximX)k-uBxeSY&s2ruA#oM&Yr>E(eC2Anbd4+nVp@L zlZAub(%9B$Mf99GqpP!ajbxR|7(#z~6|1wWmQpwcZ;=mz69_XsyP*W(6R zuZ|xx{PgML!dKgS{S$nAs%ES?ow0n@q*F7d%{zFaf02UmY3tkiChF4-_4g~x*|=fF z4i-}r3GdUJPK7qQh0h2LGYq>fa{b!EolP6}JzN;2mE2alSSPtGxVVaY)*goR)sh!7 zrf&PjckJA{XLu*nwhT4%EMp4neD;cb|Rc5I#o>q9oWQ#>|iizpT^T|0g<7|{?7cNWnR|49z zc;T!pwa0629xYxa`TX2$dqG`a4r9HD9UlHEA15t6%_TW~eZ)o~TVDesf8JQtT~)r< zZk6`Dp8GsE;mz$GjT42BvdYdj%fGoR_c!0MBXXyGdzRj{E?=l_<2L(l^|yD)cj^z^ z2wq?!`y|AqGJgGn=5`5R{%mdIb5=!PBA9mVx&2XC-QV!oOO3f=1@@cYwyc^jv5Lzr z_-#t~u{)ce|Ctf?{#IFB>cd6c8j2?m?8u+4yLg??3)jNgYrVzSrtc~F$)!9?Rj6m( zbbp!YTW;2xK031XbPXLfCt@?Q0Bck$7-z*l+(>F0Nq z|7VP0WVm0p>QeO5BmKY>r&{6~QIe8al4_M)lnSI6j0}v7bPddOjf_GJ%&m+Kt&9w` z4GgRd43_?qy^o?HH$NpatrE9}E6bNA12u$Kg+!DDC6+4`6y>L7=AsHgKi%N5z)O$emAGKZCnwXXKr0wLUHx3vIVCg!05u>lK>z>% literal 0 HcmV?d00001 diff --git a/src/assets/favicon/favicon.ico b/src/assets/favicon/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8b3e01a4612f4988c787d66a2777887e25a1dc33 GIT binary patch literal 15086 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 + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + + + + + + + + diff --git a/src/assets/favicon/site.webmanifest b/src/assets/favicon/site.webmanifest new file mode 100644 index 0000000..b20abb7 --- /dev/null +++ b/src/assets/favicon/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/src/assets/fonts/.gitkeep b/src/assets/fonts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/assets/fonts/icomoon/icomoon.eot b/src/assets/fonts/icomoon/icomoon.eot new file mode 100644 index 0000000000000000000000000000000000000000..da4524436931b502240d436094b8c651c0d704a7 GIT binary patch literal 1544 zcmb7EO>0wG7=BJ}zFKW6Z9ZVA@g}V`cN3eWO(<9q97}8!rBYEa>NSaJYLi>i6k2fM zY8FNr7qd}h7BXvpfXpImnPE0FFtZpJx+-0}^5goPbFZ3c5q#m?^SsY_pYwI!^9KDu z#Rmv@&~E|{gk;W~@8@N#W4pzX%9mq62z8WUp}}UM$$$`+P(=-!Sc8ccnJ6xjX<>u0 zGDb%RX-uiuFV!UFg8Og>8m7)&IGPNb$G>(G^zX3!vQjlGiT_Xj2WVlA=T}Gs|MDHAe}?|VN~66sx{M-yInUUdRW|Wl za6#=J{R0hiYaRP>iT-2ydedxFA1(~Mq0jw@pVqC7Hup*M{V2zwY5#}^;0=2odj25u zv(t-RoLJ?_uC0zM`&HUixV0f2jyLSG+hC?nrvenb9zlhjlruF`w`^N^^(&u zwIl3zwykv1gYty^%Kkg%qq=u?xt3t;a1YBQDa@*%&ynBHc>%lY1y3{t1E;VI98ONo ziRpYU8$Bq(el1%#E(-aX=_6@zWO6(i2#J6{5{|`1EIOK1T_mi?@uUiwnO1@rXUJru zkxbY>o=Hy5co2!lBW&UN)lw%#>y)n6ZyqavCuh^1iBQfV*# zwV!_O?JigB(w7g?$tCy{YWi|UN*u`pVqi!|zOnPeXvbCpb zPjabR+gvkS{q=Cq$a$;T)@#*fwPm)e75&Dxe!6U(<=)bmS0%H}Q`A^Qo2rnSXtS@7 WyMb+fPpcAq)-Azl-k+=t->?tQq~rqt literal 0 HcmV?d00001 diff --git a/src/assets/fonts/icomoon/icomoon.svg b/src/assets/fonts/icomoon/icomoon.svg new file mode 100644 index 0000000..6635d1c --- /dev/null +++ b/src/assets/fonts/icomoon/icomoon.svg @@ -0,0 +1,12 @@ + + + +Generated by IcoMoon + + + + + + + + \ No newline at end of file diff --git a/src/assets/fonts/icomoon/icomoon.ttf b/src/assets/fonts/icomoon/icomoon.ttf new file mode 100644 index 0000000000000000000000000000000000000000..418739e27ace41cad79283a147b0fed530e59969 GIT binary patch literal 1380 zcmb7EOHUI~6#j0f4?qDUeIy2JTV91qKQR^GL|CkkWvyzT(ELs zHM($vF)oa2e}Hk(wb8iIL>Icy6=Chl$N1elqn60VliYj1^Z3p=_x4O7035&;Gz?w1 zcs$*C*iQpqa9t~ujn$nodKi;;iYuGfA3d(esc&&TSTc>p_{Y;9 z04+#=c8P|TH@>6P&r$DPD%Uo;W-&os=ILIs3I<*Z7S!%gKU6k0R&fxQs6V0}s~BZ- zd#d#tb@n4}ty*g}_DS)bk$z~}J8>UAz_aanMawJx zG%h;jTl|)o0@W7)h>z^%?6Q_% zY-f|z1LQ_k(BH`aw|N1(;RSEh4jpGP3-l%W$HZ_plMWvhLBE#HofNt3$ne0R80haw z1Uf{(9|}gIA`bTIk?$yD9cTB2Wt=iN#G_^T*>Df zPGH&Bu1$b**CoQ~oXF;~{k&aVUM~`kY>MZpyJ_)U4+Q#jef0aCQC;s1wCJARpW|uT zlbOur=&@tb%b85_&m1fsp4h$GdEzS$@KGtB-rLXqo_2#38`R|lIKtme%@cflbxvt} z)35791e{EcB56{Wiwu4jls4WWiX@)krD6v#rvfT&xji zUF;=Zcd?IOy?i+-e%hy9+=e5#EvMDnN=pYl4)zciU91t0x!6m5)5Si7!LL$r{j|@x zxDCU2S}Is&%c`J+0xXodEL7&q>RQRF#8QJpyr+pO))+ts^Q16{p?z8V8fVPn`ifCy zQIn0XV+97P`$mpim0GN5R?MnVGZ$kEo3Y7)b)LNqVqDd(v3ikdDyky`HJ*#KEnt)T Tq`JU)=K}1}`G-&sx&QVHbuZK+ literal 0 HcmV?d00001 diff --git a/src/assets/fonts/icomoon/icomoon.woff b/src/assets/fonts/icomoon/icomoon.woff new file mode 100644 index 0000000000000000000000000000000000000000..94be51add4bf910c2e254260109772b42c9da5e9 GIT binary patch literal 1456 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 M07*qoM6N<$f~AVIoB#j- literal 0 HcmV?d00001 diff --git a/src/assets/images/transparent.png b/src/assets/images/transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..909c66db1740b7c1b41eb4db6c414a7ab5bb6a23 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcwN$DG5Lh8v~O;;{|;n Oi^0>?&t;ucLK6U5DhwL{ literal 0 HcmV?d00001 diff --git a/src/scripts/_libs/.gitkeep b/src/scripts/_libs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/scripts/_libs/jquery.fullPage.min.js b/src/scripts/_libs/jquery.fullPage.min.js new file mode 100644 index 0000000..1484139 --- /dev/null +++ b/src/scripts/_libs/jquery.fullPage.min.js @@ -0,0 +1,55 @@ +/** + * fullPage 2.6.6 + * https://github.com/alvarotrigo/fullPage.js + * MIT licensed + * + * Copyright (C) 2015 alvarotrigo.com - A project by Alvaro Trigo + */ +(function(c,l){"function"===typeof define&&define.amd?define(["jquery"],function(k){return l(k,c,c.document,c.Math)}):"undefined"!==typeof exports?module.exports=l(require("jquery"),c,c.document,c.Math):l(jQuery,c,c.document,c.Math)})("undefined"!==typeof window?window:this,function(c,l,k,n,H){var m=c(l),r=c(k);c.fn.fullpage=function(d){function Ia(a){a.find(".fp-slides").after('
');"#fff"!=d.controlArrowColor&&(a.find(".fp-controlArrow.fp-next").css("border-color", +"transparent transparent transparent "+d.controlArrowColor),a.find(".fp-controlArrow.fp-prev").css("border-color","transparent "+d.controlArrowColor+" transparent transparent"));d.loopHorizontal||a.find(".fp-controlArrow.fp-prev").hide()}function Ja(){p.append('
    ');z=c("#fp-nav");z.addClass(function(){return d.showActiveTooltip?"fp-show-active "+d.navigationPosition:d.navigationPosition});for(var a=0;a',g=d.navigationTooltips[a];"undefined"!==typeof g&&""!==g&&(b+='
    '+g+"
    ");b+="";z.find("ul").append(b)}}function da(){c(".fp-section").each(function(){var a=c(this).find(".fp-slide");a.length?a.each(function(){I(c(this))}):I(c(this))});ea()}function ea(){var a=c(".fp-section.active"),b=a.find("SLIDES_WRAPPER"),g=a.find(".fp-scrollable");b.length&&(g=b.find(".fp-slide.active"));g.mouseover();fa(a); +c.isFunction(d.afterLoad)&&d.afterLoad.call(a,a.data("anchor"),a.index(".fp-section")+1);c.isFunction(d.afterRender)&&d.afterRender.call(this)}function ga(){var a;if(!d.autoScrolling||d.scrollBar){for(var b=m.scrollTop(),g=0,J=n.abs(b-k.querySelectorAll(".fp-section")[0].offsetTop),e=k.querySelectorAll(".fp-section"),f=0;f=b[0].scrollHeight:void 0,d)c();else return!0;else c()}}function Ka(a){var b=a.originalEvent;if(!ka(a.target)&&X(b)){d.autoScrolling&&a.preventDefault();a=c(".fp-section.active");var g= +ja(a);u&&!w&&(b=la(b),D=b.y,M=b.x,a.find(".fp-slides").length&&n.abs(N-M)>n.abs(E-D)?n.abs(N-M)>m.width()/100*d.touchSensitivity&&(N>M?h.m.right&&e.moveSlideRight():h.m.left&&e.moveSlideLeft()):d.autoScrolling&&n.abs(E-D)>m.height()/100*d.touchSensitivity&&(E>D?L("down",g):D>E&&L("up",g)))}}function ka(a,b){b=b||0;var g=c(a).parent();return b=g&&(0>e?L("down",a):L("up",a)));return!1}d.fitToSection&&x.stop()}function oa(a){var b=c(".fp-section.active").find(".fp-slides"),g=b.find(".fp-slide").length;if(!(!b.length||w||2>g)){var g=b.find(".fp-slide.active"),e=null,e="prev"===a?g.prev(".fp-slide"):g.next(".fp-slide");if(!e.length){if(!d.loopHorizontal)return;e="prev"===a?g.siblings(":last"):g.siblings(":first")}w=!0;F(b,e)}}function pa(){c(".fp-slide.active").each(function(){Y(c(this), +"internal")})}function B(a,b,g){var e=a.position();if("undefined"!==typeof e&&(b={element:a,callback:b,isMovementUp:g,dest:e,dtop:e.top,yMovement:V(a),anchorLink:a.data("anchor"),sectionIndex:a.index(".fp-section"),activeSlide:a.find(".fp-slide.active"),activeSection:c(".fp-section.active"),leavingSection:c(".fp-section.active").index(".fp-section")+1,localIsResizing:v},!(b.activeSection.is(a)&&!v||d.scrollBar&&m.scrollTop()===b.dtop))){if(b.activeSlide.length)var f=b.activeSlide.data("anchor"),h= +b.activeSlide.index();d.autoScrolling&&d.continuousVertical&&"undefined"!==typeof b.isMovementUp&&(!b.isMovementUp&&"up"==b.yMovement||b.isMovementUp&&"down"==b.yMovement)&&(b.isMovementUp?c(".fp-section.active").before(b.activeSection.nextAll(".fp-section")):c(".fp-section.active").after(b.activeSection.prevAll(".fp-section").get().reverse()),y(c(".fp-section.active").position().top),pa(),b.wrapAroundElements=b.activeSection,b.dest=b.element.position(),b.dtop=b.dest.top,b.yMovement=V(b.element)); +if(c.isFunction(d.onLeave)&&!b.localIsResizing){if(!1===d.onLeave.call(b.activeSection,b.leavingSection,b.sectionIndex+1,b.yMovement))return;Ma(b.activeSection)}a.addClass("active").siblings().removeClass("active");u=!1;W(h,f,b.anchorLink,b.sectionIndex);Na(b);A=b.anchorLink;K(b.anchorLink,b.sectionIndex)}}function Na(a){if(d.css3&&d.autoScrolling&&!d.scrollBar)qa("translate3d(0px, -"+a.dtop+"px, 0px)",!0),d.scrollingSpeed?setTimeout(function(){Z(a)},d.scrollingSpeed):Z(a);else{var b=Oa(a);c(b.element).animate(b.options, +d.scrollingSpeed,d.easing).promise().done(function(){Z(a)})}}function Oa(a){var b={};d.autoScrolling&&!d.scrollBar?(b.options={top:-a.dtop},b.element=".fullpage-wrapper"):(b.options={scrollTop:a.dtop},b.element="html, body");return b}function Z(a){a.wrapAroundElements&&a.wrapAroundElements.length&&(a.isMovementUp?c(".fp-section:first").before(a.wrapAroundElements):c(".fp-section:last").after(a.wrapAroundElements),y(c(".fp-section.active").position().top),pa());a.element.find(".fp-scrollable").mouseover(); +c.isFunction(d.afterLoad)&&!a.localIsResizing&&d.afterLoad.call(a.element,a.anchorLink,a.sectionIndex+1);fa(a.element);Pa(a.element);u=!0;c.isFunction(a.callback)&&a.callback.call(this)}function fa(a){a.find("img[data-src], video[data-src], audio[data-src]").each(function(){c(this).attr("src",c(this).data("src"));c(this).removeAttr("data-src")})}function Pa(a){a.find("video, audio").each(function(){var a=c(this).get(0);a.hasAttribute("autoplay")&&"function"===typeof a.play&&a.play()})}function Ma(a){a.find("video, audio").each(function(){var a= +c(this).get(0);a.hasAttribute("data-ignore")||"function"!==typeof a.pause||a.pause()})}function ra(){if(!U&&!d.lockAnchors){var a=l.location.hash.replace("#","").split("/"),b=a[0],a=a[1];if(b.length){var c="undefined"===typeof A,e="undefined"===typeof A&&"undefined"===typeof a&&!w;(b&&b!==A&&!c||e||!w&&aa!=a)&&ba(b,a)}}}function Qa(a){u&&(a.pageYP&&e.moveSectionDown());P=a.pageY}function F(a,b){var g=b.position(),e=b.index(),f=a.closest(".fp-section"),h=f.index(".fp-section"), +k=f.data("anchor"),l=f.find(".fp-slidesNav"),m=sa(b),p=v;if(d.onSlideLeave){var t=f.find(".fp-slide.active"),q=t.index(),r;r=q==e?"none":q>e?"left":"right";if(!p&&"none"!==r&&c.isFunction(d.onSlideLeave)&&!1===d.onSlideLeave.call(t,k,h+1,q,r,e)){w=!1;return}}b.addClass("active").siblings().removeClass("active");!d.loopHorizontal&&d.controlArrows&&(f.find(".fp-controlArrow.fp-prev").toggle(0!==e),f.find(".fp-controlArrow.fp-next").toggle(!b.is(":last-child")));f.hasClass("active")&&W(e,m,k,h);var u= +function(){p||c.isFunction(d.afterSlideLoad)&&d.afterSlideLoad.call(b,k,h+1,m,e);w=!1};d.css3?(g="translate3d(-"+n.round(g.left)+"px, 0px, 0px)",ta(a.find(".fp-slidesContainer"),020*n.max(ca,a)/100&&(e.reBuild(!0),ca=a))}else clearTimeout(xa),xa=setTimeout(function(){e.reBuild(!0)},350)}function wa(){var a=d.responsive||d.responsiveWidth,b=d.responsiveHeight;a&&e.setResponsive(m.width() +a||900>b){var d=n.min(100*a/825,100*b/900).toFixed(2);p.css("font-size",d+"%")}else p.css("font-size","100%")}function K(a,b){d.menu&&(c(d.menu).find(".active").removeClass("active"),c(d.menu).find('[data-menuanchor="'+a+'"]').addClass("active"));d.navigation&&(c("#fp-nav").find(".active").removeClass("active"),a?c("#fp-nav").find('a[href="#'+a+'"]').addClass("active"):c("#fp-nav").find("li").eq(b).find("a").addClass("active"))}function V(a){var b=c(".fp-section.active").index(".fp-section");a=a.index(".fp-section"); +return b==a?"none":b>a?"up":"down"}function I(a){a.css("overflow","hidden");var b=a.closest(".fp-section"),c=a.find(".fp-scrollable"),e;c.length?e=c.get(0).scrollHeight:(e=a.get(0).scrollHeight,d.verticalCentered&&(e=a.find(".fp-tableCell").get(0).scrollHeight));b=q-parseInt(b.css("padding-bottom"))-parseInt(b.css("padding-top"));e>b?c.length?c.css("height",b+"px").parent().css("height",b+"px"):(d.verticalCentered?a.find(".fp-tableCell").wrapInner('
    '):a.wrapInner('
    '), +a.find(".fp-scrollable").slimScroll({allowPageScroll:!0,height:b+"px",size:"10px",alwaysVisible:!0})):ya(a);a.css("overflow","")}function ya(a){a.find(".fp-scrollable").children().first().unwrap().unwrap();a.find(".slimScrollBar").remove();a.find(".slimScrollRail").remove()}function za(a){a.addClass("fp-table").wrapInner('
    ')}function Aa(a){var b=q;if(d.paddingTop||d.paddingBottom)b=a,b.hasClass("fp-section")||(b=a.closest(".fp-section")),a=parseInt(b.css("padding-top"))+ +parseInt(b.css("padding-bottom")),b=q-a;return b}function qa(a,b){b?ta(f):f.addClass("fp-notransition");f.css(ua(a));setTimeout(function(){f.removeClass("fp-notransition")},10)}function Ba(a){var b=c('.fp-section[data-anchor="'+a+'"]');b.length||(b=c(".fp-section").eq(a-1));return b}function ba(a,b){var d=Ba(a);"undefined"===typeof b&&(b=0);a===A||d.hasClass("active")?Ca(d,b):B(d,function(){Ca(d,b)})}function Ca(a,b){if("undefined"!==typeof b){var d=a.find(".fp-slides"),c;c=a.find(".fp-slides");var e= +c.find('.fp-slide[data-anchor="'+b+'"]');e.length||(e=c.find(".fp-slide").eq(b));c=e;c.length&&F(d,c)}}function Sa(a,b){a.append('
      ');var c=a.find(".fp-slidesNav");c.addClass(d.slidesNavPosition);for(var e=0;e');c.css("margin-left","-"+c.width()/2+"px");c.find("li").first().find("a").addClass("active")}function W(a,b,c,e){e="";d.anchors.length&&!d.lockAnchors&&(a?("undefined"!==typeof c&&(e=c), +"undefined"===typeof b&&(b=a),aa=b,Da(e+"/"+b)):("undefined"!==typeof a&&(aa=b),Da(c)));Ea()}function Da(a){if(d.recordHistory)location.hash=a;else if(Q||R)history.replaceState(H,H,"#"+a);else{var b=l.location.href.split("#")[0];l.location.replace(b+"#"+a)}}function sa(a){var b=a.data("anchor");a=a.index();"undefined"===typeof b&&(b=a);return b}function Ea(){var a=c(".fp-section.active"),b=a.find(".fp-slide.active"),e=a.data("anchor"),f=sa(b),a=a.index(".fp-section"),a=String(a);d.anchors.length&& +(a=e);b.length&&(a=a+"-"+f);a=a.replace("/","-").replace("#","");p[0].className=p[0].className.replace(RegExp("\\b\\s?fp-viewing-[^\\s]+\\b","g"),"");p.addClass("fp-viewing-"+a)}function Ta(){var a=k.createElement("p"),b,c={webkitTransform:"-webkit-transform",OTransform:"-o-transform",msTransform:"-ms-transform",MozTransform:"-moz-transform",transform:"transform"};k.body.insertBefore(a,null);for(var d in c)a.style[d]!==H&&(a.style[d]="translate3d(1px,1px,1px)",b=l.getComputedStyle(a).getPropertyValue(c[d])); +k.body.removeChild(a);return b!==H&&0(b/=e/2)?d/2*b*b*b+c:d/2*((b-=2)*b*b+2)+c}});c.extend(c.easing,{easeInQuart:function(a, +b,c,d,e){return d*(b/=e)*b*b*b+c}});e.setAutoScrolling=function(a,b){S("autoScrolling",a,b);var g=c(".fp-section.active");d.autoScrolling&&!d.scrollBar?(x.css({overflow:"hidden",height:"100%"}),e.setRecordHistory(d.recordHistory,"internal"),f.css({"-ms-touch-action":"none","touch-action":"none"}),g.length&&y(g.position().top)):(x.css({overflow:"visible",height:"initial"}),e.setRecordHistory(!1,"internal"),f.css({"-ms-touch-action":"","touch-action":""}),y(0),g.length&&x.scrollTop(g.position().top))}; +e.setRecordHistory=function(a,b){S("recordHistory",a,b)};e.setScrollingSpeed=function(a,b){S("scrollingSpeed",a,b)};e.setFitToSection=function(a,b){S("fitToSection",a,b)};e.setLockAnchors=function(a){d.lockAnchors=a};e.setMouseWheelScrolling=function(a){a?k.addEventListener?(k.addEventListener("mousewheel",t,!1),k.addEventListener("wheel",t,!1),k.addEventListener("DOMMouseScroll",t,!1)):k.attachEvent("onmousewheel",t):k.addEventListener?(k.removeEventListener("mousewheel",t,!1),k.removeEventListener("wheel", +t,!1),k.removeEventListener("DOMMouseScroll",t,!1)):k.detachEvent("onmousewheel",t)};e.setAllowScrolling=function(a,b){"undefined"!==typeof b?(b=b.replace(/ /g,"").split(","),c.each(b,function(b,c){Ga(a,c,"m")})):a?(e.setMouseWheelScrolling(!0),Ua()):(e.setMouseWheelScrolling(!1),Va())};e.setKeyboardScrolling=function(a,b){"undefined"!==typeof b?(b=b.replace(/ /g,"").split(","),c.each(b,function(b,c){Ga(a,c,"k")})):d.keyboardScrolling=a};e.moveSectionUp=function(){var a=c(".fp-section.active").prev(".fp-section"); +a.length||!d.loopTop&&!d.continuousVertical||(a=c(".fp-section").last());a.length&&B(a,null,!0)};e.moveSectionDown=function(){var a=c(".fp-section.active").next(".fp-section");a.length||!d.loopBottom&&!d.continuousVertical||(a=c(".fp-section").first());!a.length||d.onBeforeMoveSection&&c.isFunction(d.onBeforeMoveSection)&&!1===d.onBeforeMoveSection.call(this,direction,currentSlide,destiny,slides,activeSection)||B(a,null,!1)};e.silentMoveTo=function(a,b){e.setScrollingSpeed(0,"internal");e.moveTo(a, +b);e.setScrollingSpeed(G.scrollingSpeed,"internal")};e.moveTo=function(a,b){var c=Ba(a);"undefined"!==typeof b?ba(a,b):0'); +e.parent().wrap('
      ');c(this).find(".fp-slidesContainer").css("width",a+"%");1` element. This + * information allows you to progressively enhance your pages with a granular level + * of control over the experience. +*/ + +;(function(window, document, undefined){ + var classes = []; + + + var tests = []; + + + /** + * + * ModernizrProto is the constructor for Modernizr + * + * @class + * @access public + */ + + var ModernizrProto = { + // The current version, dummy + _version: '3.6.0', + + // Any settings that don't work as separate modules + // can go in here as configuration. + _config: { + 'classPrefix': '', + 'enableClasses': true, + 'enableJSClass': true, + 'usePrefixes': true + }, + + // Queue of tests + _q: [], + + // Stub these for people who are listening + on: function(test, cb) { + // I don't really think people should do this, but we can + // safe guard it a bit. + // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests. + // This is in case people listen to synchronous tests. I would leave it out, + // but the code to *disallow* sync tests in the real version of this + // function is actually larger than this. + var self = this; + setTimeout(function() { + cb(self[test]); + }, 0); + }, + + addTest: function(name, fn, options) { + tests.push({name: name, fn: fn, options: options}); + }, + + addAsyncTest: function(fn) { + tests.push({name: null, fn: fn}); + } + }; + + + + // Fake some of Object.create so we can force non test results to be non "own" properties. + var Modernizr = function() {}; + Modernizr.prototype = ModernizrProto; + + // Leak modernizr globally when you `require` it rather than force it here. + // Overwrite name so constructor name is nicer :D + Modernizr = new Modernizr(); + + + + /** + * is returns a boolean if the typeof an obj is exactly type. + * + * @access private + * @function is + * @param {*} obj - A thing we want to check the type of + * @param {string} type - A string to compare the typeof against + * @returns {boolean} + */ + + function is(obj, type) { + return typeof obj === type; + } + ; + + /** + * Run through all tests and detect their support in the current UA. + * + * @access private + */ + + function testRunner() { + var featureNames; + var feature; + var aliasIdx; + var result; + var nameIdx; + var featureName; + var featureNameSplit; + + for (var featureIdx in tests) { + if (tests.hasOwnProperty(featureIdx)) { + featureNames = []; + feature = tests[featureIdx]; + // run the test, throw the return value into the Modernizr, + // then based on that boolean, define an appropriate className + // and push it into an array of classes we'll join later. + // + // If there is no name, it's an 'async' test that is run, + // but not directly added to the object. That should + // be done with a post-run addTest call. + if (feature.name) { + featureNames.push(feature.name.toLowerCase()); + + if (feature.options && feature.options.aliases && feature.options.aliases.length) { + // Add all the aliases into the names list + for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) { + featureNames.push(feature.options.aliases[aliasIdx].toLowerCase()); + } + } + } + + // Run the test, or use the raw value if it's not a function + result = is(feature.fn, 'function') ? feature.fn() : feature.fn; + + + // Set each of the names on the Modernizr object + for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) { + featureName = featureNames[nameIdx]; + // Support dot properties as sub tests. We don't do checking to make sure + // that the implied parent tests have been added. You must call them in + // order (either in the test, or make the parent test a dependency). + // + // Cap it to TWO to make the logic simple and because who needs that kind of subtesting + // hashtag famous last words + featureNameSplit = featureName.split('.'); + + if (featureNameSplit.length === 1) { + Modernizr[featureNameSplit[0]] = result; + } else { + // cast to a Boolean, if not one already + if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { + Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); + } + + Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result; + } + + classes.push((result ? '' : 'no-') + featureNameSplit.join('-')); + } + } + } + } + ; + + /** + * docElement is a convenience wrapper to grab the root element of the document + * + * @access private + * @returns {HTMLElement|SVGElement} The root element of the document + */ + + var docElement = document.documentElement; + + + /** + * A convenience helper to check if the document we are running in is an SVG document + * + * @access private + * @returns {boolean} + */ + + var isSVG = docElement.nodeName.toLowerCase() === 'svg'; + + + /** + * setClasses takes an array of class names and adds them to the root element + * + * @access private + * @function setClasses + * @param {string[]} classes - Array of class names + */ + + // Pass in an and array of class names, e.g.: + // ['no-webp', 'borderradius', ...] + function setClasses(classes) { + var className = docElement.className; + var classPrefix = Modernizr._config.classPrefix || ''; + + if (isSVG) { + className = className.baseVal; + } + + // Change `no-js` to `js` (independently of the `enableClasses` option) + // Handle classPrefix on this too + if (Modernizr._config.enableJSClass) { + var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)'); + className = className.replace(reJS, '$1' + classPrefix + 'js$2'); + } + + if (Modernizr._config.enableClasses) { + // Add the new classes + className += ' ' + classPrefix + classes.join(' ' + classPrefix); + if (isSVG) { + docElement.className.baseVal = className; + } else { + docElement.className = className; + } + } + + } + + ; + + /** + * hasOwnProp is a shim for hasOwnProperty that is needed for Safari 2.0 support + * + * @author kangax + * @access private + * @function hasOwnProp + * @param {object} object - The object to check for a property + * @param {string} property - The property to check for + * @returns {boolean} + */ + + // hasOwnProperty shim by kangax needed for Safari 2.0 support + var hasOwnProp; + + (function() { + var _hasOwnProperty = ({}).hasOwnProperty; + /* istanbul ignore else */ + /* we have no way of testing IE 5.5 or safari 2, + * so just assume the else gets hit */ + if (!is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined')) { + hasOwnProp = function(object, property) { + return _hasOwnProperty.call(object, property); + }; + } + else { + hasOwnProp = function(object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */ + return ((property in object) && is(object.constructor.prototype[property], 'undefined')); + }; + } + })(); + + + + + // _l tracks listeners for async tests, as well as tests that execute after the initial run + ModernizrProto._l = {}; + + /** + * Modernizr.on is a way to listen for the completion of async tests. Being + * asynchronous, they may not finish before your scripts run. As a result you + * will get a possibly false negative `undefined` value. + * + * @memberof Modernizr + * @name Modernizr.on + * @access public + * @function on + * @param {string} feature - String name of the feature detect + * @param {function} cb - Callback function returning a Boolean - true if feature is supported, false if not + * @example + * + * ```js + * Modernizr.on('flash', function( result ) { + * if (result) { + * // the browser has flash + * } else { + * // the browser does not have flash + * } + * }); + * ``` + */ + + ModernizrProto.on = function(feature, cb) { + // Create the list of listeners if it doesn't exist + if (!this._l[feature]) { + this._l[feature] = []; + } + + // Push this test on to the listener list + this._l[feature].push(cb); + + // If it's already been resolved, trigger it on next tick + if (Modernizr.hasOwnProperty(feature)) { + // Next Tick + setTimeout(function() { + Modernizr._trigger(feature, Modernizr[feature]); + }, 0); + } + }; + + /** + * _trigger is the private function used to signal test completion and run any + * callbacks registered through [Modernizr.on](#modernizr-on) + * + * @memberof Modernizr + * @name Modernizr._trigger + * @access private + * @function _trigger + * @param {string} feature - string name of the feature detect + * @param {function|boolean} [res] - A feature detection function, or the boolean = + * result of a feature detection function + */ + + ModernizrProto._trigger = function(feature, res) { + if (!this._l[feature]) { + return; + } + + var cbs = this._l[feature]; + + // Force async + setTimeout(function() { + var i, cb; + for (i = 0; i < cbs.length; i++) { + cb = cbs[i]; + cb(res); + } + }, 0); + + // Don't trigger these again + delete this._l[feature]; + }; + + /** + * addTest allows you to define your own feature detects that are not currently + * included in Modernizr (under the covers it's the exact same code Modernizr + * uses for its own [feature detections](https://github.com/Modernizr/Modernizr/tree/master/feature-detects)). Just like the offical detects, the result + * will be added onto the Modernizr object, as well as an appropriate className set on + * the html element when configured to do so + * + * @memberof Modernizr + * @name Modernizr.addTest + * @optionName Modernizr.addTest() + * @optionProp addTest + * @access public + * @function addTest + * @param {string|object} feature - The string name of the feature detect, or an + * object of feature detect names and test + * @param {function|boolean} test - Function returning true if feature is supported, + * false if not. Otherwise a boolean representing the results of a feature detection + * @example + * + * The most common way of creating your own feature detects is by calling + * `Modernizr.addTest` with a string (preferably just lowercase, without any + * punctuation), and a function you want executed that will return a boolean result + * + * ```js + * Modernizr.addTest('itsTuesday', function() { + * var d = new Date(); + * return d.getDay() === 2; + * }); + * ``` + * + * When the above is run, it will set Modernizr.itstuesday to `true` when it is tuesday, + * and to `false` every other day of the week. One thing to notice is that the names of + * feature detect functions are always lowercased when added to the Modernizr object. That + * means that `Modernizr.itsTuesday` will not exist, but `Modernizr.itstuesday` will. + * + * + * Since we only look at the returned value from any feature detection function, + * you do not need to actually use a function. For simple detections, just passing + * in a statement that will return a boolean value works just fine. + * + * ```js + * Modernizr.addTest('hasJquery', 'jQuery' in window); + * ``` + * + * Just like before, when the above runs `Modernizr.hasjquery` will be true if + * jQuery has been included on the page. Not using a function saves a small amount + * of overhead for the browser, as well as making your code much more readable. + * + * Finally, you also have the ability to pass in an object of feature names and + * their tests. This is handy if you want to add multiple detections in one go. + * The keys should always be a string, and the value can be either a boolean or + * function that returns a boolean. + * + * ```js + * var detects = { + * 'hasjquery': 'jQuery' in window, + * 'itstuesday': function() { + * var d = new Date(); + * return d.getDay() === 2; + * } + * } + * + * Modernizr.addTest(detects); + * ``` + * + * There is really no difference between the first methods and this one, it is + * just a convenience to let you write more readable code. + */ + + function addTest(feature, test) { + + if (typeof feature == 'object') { + for (var key in feature) { + if (hasOwnProp(feature, key)) { + addTest(key, feature[ key ]); + } + } + } else { + + feature = feature.toLowerCase(); + var featureNameSplit = feature.split('.'); + var last = Modernizr[featureNameSplit[0]]; + + // Again, we don't check for parent test existence. Get that right, though. + if (featureNameSplit.length == 2) { + last = last[featureNameSplit[1]]; + } + + if (typeof last != 'undefined') { + // we're going to quit if you're trying to overwrite an existing test + // if we were to allow it, we'd do this: + // var re = new RegExp("\\b(no-)?" + feature + "\\b"); + // docElement.className = docElement.className.replace( re, '' ); + // but, no rly, stuff 'em. + return Modernizr; + } + + test = typeof test == 'function' ? test() : test; + + // Set the value (this is the magic, right here). + if (featureNameSplit.length == 1) { + Modernizr[featureNameSplit[0]] = test; + } else { + // cast to a Boolean, if not one already + if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { + Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); + } + + Modernizr[featureNameSplit[0]][featureNameSplit[1]] = test; + } + + // Set a single class (either `feature` or `no-feature`) + setClasses([(!!test && test != false ? '' : 'no-') + featureNameSplit.join('-')]); + + // Trigger the event + Modernizr._trigger(feature, test); + } + + return Modernizr; // allow chaining. + } + + // After all the tests are run, add self to the Modernizr prototype + Modernizr._q.push(function() { + ModernizrProto.addTest = addTest; + }); + + + + + /** + * List of property values to set for css tests. See ticket #21 + * http://git.io/vUGl4 + * + * @memberof Modernizr + * @name Modernizr._prefixes + * @optionName Modernizr._prefixes + * @optionProp prefixes + * @access public + * @example + * + * Modernizr._prefixes is the internal list of prefixes that we test against + * inside of things like [prefixed](#modernizr-prefixed) and [prefixedCSS](#-code-modernizr-prefixedcss). It is simply + * an array of kebab-case vendor prefixes you can use within your code. + * + * Some common use cases include + * + * Generating all possible prefixed version of a CSS property + * ```js + * var rule = Modernizr._prefixes.join('transform: rotate(20deg); '); + * + * rule === 'transform: rotate(20deg); webkit-transform: rotate(20deg); moz-transform: rotate(20deg); o-transform: rotate(20deg); ms-transform: rotate(20deg);' + * ``` + * + * Generating all possible prefixed version of a CSS value + * ```js + * rule = 'display:' + Modernizr._prefixes.join('flex; display:') + 'flex'; + * + * rule === 'display:flex; display:-webkit-flex; display:-moz-flex; display:-o-flex; display:-ms-flex; display:flex' + * ``` + */ + + // we use ['',''] rather than an empty array in order to allow a pattern of .`join()`ing prefixes to test + // values in feature detects to continue to work + var prefixes = (ModernizrProto._config.usePrefixes ? ' -webkit- -moz- -o- -ms- '.split(' ') : ['','']); + + // expose these for the plugin API. Look in the source for how to join() them against your input + ModernizrProto._prefixes = prefixes; + + + + /** + * createElement is a convenience wrapper around document.createElement. Since we + * use createElement all over the place, this allows for (slightly) smaller code + * as well as abstracting away issues with creating elements in contexts other than + * HTML documents (e.g. SVG documents). + * + * @access private + * @function createElement + * @returns {HTMLElement|SVGElement} An HTML or SVG element + */ + + function createElement() { + if (typeof document.createElement !== 'function') { + // This is the case in IE7, where the type of createElement is "object". + // For this reason, we cannot call apply() as Object is not a Function. + return document.createElement(arguments[0]); + } else if (isSVG) { + return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]); + } else { + return document.createElement.apply(document, arguments); + } + } + + ; + + /** + * getBody returns the body of a document, or an element that can stand in for + * the body if a real body does not exist + * + * @access private + * @function getBody + * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an + * artificially created element that stands in for the body + */ + + function getBody() { + // After page load injecting a fake body doesn't work so check if body exists + var body = document.body; + + if (!body) { + // Can't use the real body create a fake one. + body = createElement(isSVG ? 'svg' : 'body'); + body.fake = true; + } + + return body; + } + + ; + + /** + * injectElementWithStyles injects an element with style element and some CSS rules + * + * @access private + * @function injectElementWithStyles + * @param {string} rule - String representing a css rule + * @param {function} callback - A function that is used to test the injected element + * @param {number} [nodes] - An integer representing the number of additional nodes you want injected + * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes + * @returns {boolean} + */ + + function injectElementWithStyles(rule, callback, nodes, testnames) { + var mod = 'modernizr'; + var style; + var ret; + var node; + var docOverflow; + var div = createElement('div'); + var body = getBody(); + + if (parseInt(nodes, 10)) { + // In order not to give false positives we create a node for each test + // This also allows the method to scale for unspecified uses + while (nodes--) { + node = createElement('div'); + node.id = testnames ? testnames[nodes] : mod + (nodes + 1); + div.appendChild(node); + } + } + + style = createElement('style'); + style.type = 'text/css'; + style.id = 's' + mod; + + // IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody. + // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 + (!body.fake ? div : body).appendChild(style); + body.appendChild(div); + + if (style.styleSheet) { + style.styleSheet.cssText = rule; + } else { + style.appendChild(document.createTextNode(rule)); + } + div.id = mod; + + if (body.fake) { + //avoid crashing IE8, if background image is used + body.style.background = ''; + //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible + body.style.overflow = 'hidden'; + docOverflow = docElement.style.overflow; + docElement.style.overflow = 'hidden'; + docElement.appendChild(body); + } + + ret = callback(div, rule); + // If this is done after page load we don't want to remove the body so check if body exists + if (body.fake) { + body.parentNode.removeChild(body); + docElement.style.overflow = docOverflow; + // Trigger layout so kinetic scrolling isn't disabled in iOS6+ + // eslint-disable-next-line + docElement.offsetHeight; + } else { + div.parentNode.removeChild(div); + } + + return !!ret; + + } + + ; + + /** + * testStyles injects an element with style element and some CSS rules + * + * @memberof Modernizr + * @name Modernizr.testStyles + * @optionName Modernizr.testStyles() + * @optionProp testStyles + * @access public + * @function testStyles + * @param {string} rule - String representing a css rule + * @param {function} callback - A function that is used to test the injected element + * @param {number} [nodes] - An integer representing the number of additional nodes you want injected + * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes + * @returns {boolean} + * @example + * + * `Modernizr.testStyles` takes a CSS rule and injects it onto the current page + * along with (possibly multiple) DOM elements. This lets you check for features + * that can not be detected by simply checking the [IDL](https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Interface_development_guide/IDL_interface_rules). + * + * ```js + * Modernizr.testStyles('#modernizr { width: 9px; color: papayawhip; }', function(elem, rule) { + * // elem is the first DOM node in the page (by default #modernizr) + * // rule is the first argument you supplied - the CSS rule in string form + * + * addTest('widthworks', elem.style.width === '9px') + * }); + * ``` + * + * If your test requires multiple nodes, you can include a third argument + * indicating how many additional div elements to include on the page. The + * additional nodes are injected as children of the `elem` that is returned as + * the first argument to the callback. + * + * ```js + * Modernizr.testStyles('#modernizr {width: 1px}; #modernizr2 {width: 2px}', function(elem) { + * document.getElementById('modernizr').style.width === '1px'; // true + * document.getElementById('modernizr2').style.width === '2px'; // true + * elem.firstChild === document.getElementById('modernizr2'); // true + * }, 1); + * ``` + * + * By default, all of the additional elements have an ID of `modernizr[n]`, where + * `n` is its index (e.g. the first additional, second overall is `#modernizr2`, + * the second additional is `#modernizr3`, etc.). + * If you want to have more meaningful IDs for your function, you can provide + * them as the fourth argument, as an array of strings + * + * ```js + * Modernizr.testStyles('#foo {width: 10px}; #bar {height: 20px}', function(elem) { + * elem.firstChild === document.getElementById('foo'); // true + * elem.lastChild === document.getElementById('bar'); // true + * }, 2, ['foo', 'bar']); + * ``` + * + */ + + var testStyles = ModernizrProto.testStyles = injectElementWithStyles; + +/*! +{ + "name": "Touch Events", + "property": "touchevents", + "caniuse" : "touch", + "tags": ["media", "attribute"], + "notes": [{ + "name": "Touch Events spec", + "href": "https://www.w3.org/TR/2013/WD-touch-events-20130124/" + }], + "warnings": [ + "Indicates if the browser supports the Touch Events spec, and does not necessarily reflect a touchscreen device" + ], + "knownBugs": [ + "False-positive on some configurations of Nokia N900", + "False-positive on some BlackBerry 6.0 builds – https://github.com/Modernizr/Modernizr/issues/372#issuecomment-3112695" + ] +} +!*/ +/* DOC +Indicates if the browser supports the W3C Touch Events API. + +This *does not* necessarily reflect a touchscreen device: + +* Older touchscreen devices only emulate mouse events +* Modern IE touch devices implement the Pointer Events API instead: use `Modernizr.pointerevents` to detect support for that +* Some browsers & OS setups may enable touch APIs when no touchscreen is connected +* Future browsers may implement other event models for touch interactions + +See this article: [You Can't Detect A Touchscreen](http://www.stucox.com/blog/you-cant-detect-a-touchscreen/). + +It's recommended to bind both mouse and touch/pointer events simultaneously – see [this HTML5 Rocks tutorial](http://www.html5rocks.com/en/mobile/touchandmouse/). + +This test will also return `true` for Firefox 4 Multitouch support. +*/ + + // Chrome (desktop) used to lie about its support on this, but that has since been rectified: http://crbug.com/36415 + Modernizr.addTest('touchevents', function() { + var bool; + if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) { + bool = true; + } else { + // include the 'heartz' as a way to have a non matching MQ to help terminate the join + // https://git.io/vznFH + var query = ['@media (', prefixes.join('touch-enabled),('), 'heartz', ')', '{#modernizr{top:9px;position:absolute}}'].join(''); + testStyles(query, function(node) { + bool = node.offsetTop === 9; + }); + } + return bool; + }); + + + // Run each test + testRunner(); + + // Remove the "no-js" class if it exists + setClasses(classes); + + delete ModernizrProto.addTest; + delete ModernizrProto.addAsyncTest; + + // Run the things that are supposed to run after the tests + for (var i = 0; i < Modernizr._q.length; i++) { + Modernizr._q[i](); + } + + // Leak Modernizr namespace + window.Modernizr = Modernizr; + + +; + +})(window, document); \ No newline at end of file diff --git a/src/scripts/_libs/select2.js b/src/scripts/_libs/select2.js new file mode 100644 index 0000000..0168034 --- /dev/null +++ b/src/scripts/_libs/select2.js @@ -0,0 +1,6210 @@ +/*! + * Select2 4.1.0-rc.0 + * https://select2.github.io + * + * Released under the MIT license + * https://github.com/select2/select2/blob/master/LICENSE.md + */ +;(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else if (typeof module === 'object' && module.exports) { + // Node/CommonJS + module.exports = function (root, jQuery) { + if (jQuery === undefined) { + // require('jQuery') returns a factory that requires window to + // build a jQuery instance, we normalize how we use modules + // that require this pattern but the window provided is a noop + // if it's defined (how jquery works) + if (typeof window !== 'undefined') { + jQuery = require('jquery'); + } + else { + jQuery = require('jquery')(root); + } + } + factory(jQuery); + return jQuery; + }; + } else { + // Browser globals + factory(jQuery); + } +} (function (jQuery) { + // This is needed so we can catch the AMD loader configuration and use it + // The inner file should be wrapped (by `banner.start.js`) in a function that + // returns the AMD loader references. + var S2 =(function () { + // Restore the Select2 AMD loader so it can be used + // Needed mostly in the language files, where the loader is not inserted + if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) { + var S2 = jQuery.fn.select2.amd; + } +var S2;(function () { if (!S2 || !S2.requirejs) { +if (!S2) { S2 = {}; } else { require = S2; } +/** + * @license almond 0.3.3 Copyright jQuery Foundation and other contributors. + * Released under MIT license, http://github.com/requirejs/almond/LICENSE + */ +//Going sloppy to avoid 'use strict' string cost, but strict practices should +//be followed. +/*global setTimeout: false */ + +var requirejs, require, define; +(function (undef) { + var main, req, makeMap, handlers, + defined = {}, + waiting = {}, + config = {}, + defining = {}, + hasOwn = Object.prototype.hasOwnProperty, + aps = [].slice, + jsSuffixRegExp = /\.js$/; + + function hasProp(obj, prop) { + return hasOwn.call(obj, prop); + } + + /** + * Given a relative module name, like ./something, normalize it to + * a real name that can be mapped to a path. + * @param {String} name the relative name + * @param {String} baseName a real name that the name arg is relative + * to. + * @returns {String} normalized name + */ + function normalize(name, baseName) { + var nameParts, nameSegment, mapValue, foundMap, lastIndex, + foundI, foundStarMap, starI, i, j, part, normalizedBaseParts, + baseParts = baseName && baseName.split("/"), + map = config.map, + starMap = (map && map['*']) || {}; + + //Adjust any relative paths. + if (name) { + name = name.split('/'); + lastIndex = name.length - 1; + + // If wanting node ID compatibility, strip .js from end + // of IDs. Have to do this here, and not in nameToUrl + // because node allows either .js or non .js to map + // to same file. + if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { + name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); + } + + // Starts with a '.' so need the baseName + if (name[0].charAt(0) === '.' && baseParts) { + //Convert baseName to array, and lop off the last part, + //so that . matches that 'directory' and not name of the baseName's + //module. For instance, baseName of 'one/two/three', maps to + //'one/two/three.js', but we want the directory, 'one/two' for + //this normalization. + normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); + name = normalizedBaseParts.concat(name); + } + + //start trimDots + for (i = 0; i < name.length; i++) { + part = name[i]; + if (part === '.') { + name.splice(i, 1); + i -= 1; + } else if (part === '..') { + // If at the start, or previous value is still .., + // keep them so that when converted to a path it may + // still work when converted to a path, even though + // as an ID it is less than ideal. In larger point + // releases, may be better to just kick out an error. + if (i === 0 || (i === 1 && name[2] === '..') || name[i - 1] === '..') { + continue; + } else if (i > 0) { + name.splice(i - 1, 2); + i -= 2; + } + } + } + //end trimDots + + name = name.join('/'); + } + + //Apply map config if available. + if ((baseParts || starMap) && map) { + nameParts = name.split('/'); + + for (i = nameParts.length; i > 0; i -= 1) { + nameSegment = nameParts.slice(0, i).join("/"); + + if (baseParts) { + //Find the longest baseName segment match in the config. + //So, do joins on the biggest to smallest lengths of baseParts. + for (j = baseParts.length; j > 0; j -= 1) { + mapValue = map[baseParts.slice(0, j).join('/')]; + + //baseName segment has config, find if it has one for + //this name. + if (mapValue) { + mapValue = mapValue[nameSegment]; + if (mapValue) { + //Match, update name to the new value. + foundMap = mapValue; + foundI = i; + break; + } + } + } + } + + if (foundMap) { + break; + } + + //Check for a star map match, but just hold on to it, + //if there is a shorter segment match later in a matching + //config, then favor over this star map. + if (!foundStarMap && starMap && starMap[nameSegment]) { + foundStarMap = starMap[nameSegment]; + starI = i; + } + } + + if (!foundMap && foundStarMap) { + foundMap = foundStarMap; + foundI = starI; + } + + if (foundMap) { + nameParts.splice(0, foundI, foundMap); + name = nameParts.join('/'); + } + } + + return name; + } + + function makeRequire(relName, forceSync) { + return function () { + //A version of a require function that passes a moduleName + //value for items that may need to + //look up paths relative to the moduleName + var args = aps.call(arguments, 0); + + //If first arg is not require('string'), and there is only + //one arg, it is the array form without a callback. Insert + //a null so that the following concat is correct. + if (typeof args[0] !== 'string' && args.length === 1) { + args.push(null); + } + return req.apply(undef, args.concat([relName, forceSync])); + }; + } + + function makeNormalize(relName) { + return function (name) { + return normalize(name, relName); + }; + } + + function makeLoad(depName) { + return function (value) { + defined[depName] = value; + }; + } + + function callDep(name) { + if (hasProp(waiting, name)) { + var args = waiting[name]; + delete waiting[name]; + defining[name] = true; + main.apply(undef, args); + } + + if (!hasProp(defined, name) && !hasProp(defining, name)) { + throw new Error('No ' + name); + } + return defined[name]; + } + + //Turns a plugin!resource to [plugin, resource] + //with the plugin being undefined if the name + //did not have a plugin prefix. + function splitPrefix(name) { + var prefix, + index = name ? name.indexOf('!') : -1; + if (index > -1) { + prefix = name.substring(0, index); + name = name.substring(index + 1, name.length); + } + return [prefix, name]; + } + + //Creates a parts array for a relName where first part is plugin ID, + //second part is resource ID. Assumes relName has already been normalized. + function makeRelParts(relName) { + return relName ? splitPrefix(relName) : []; + } + + /** + * Makes a name map, normalizing the name, and using a plugin + * for normalization if necessary. Grabs a ref to plugin + * too, as an optimization. + */ + makeMap = function (name, relParts) { + var plugin, + parts = splitPrefix(name), + prefix = parts[0], + relResourceName = relParts[1]; + + name = parts[1]; + + if (prefix) { + prefix = normalize(prefix, relResourceName); + plugin = callDep(prefix); + } + + //Normalize according + if (prefix) { + if (plugin && plugin.normalize) { + name = plugin.normalize(name, makeNormalize(relResourceName)); + } else { + name = normalize(name, relResourceName); + } + } else { + name = normalize(name, relResourceName); + parts = splitPrefix(name); + prefix = parts[0]; + name = parts[1]; + if (prefix) { + plugin = callDep(prefix); + } + } + + //Using ridiculous property names for space reasons + return { + f: prefix ? prefix + '!' + name : name, //fullName + n: name, + pr: prefix, + p: plugin + }; + }; + + function makeConfig(name) { + return function () { + return (config && config.config && config.config[name]) || {}; + }; + } + + handlers = { + require: function (name) { + return makeRequire(name); + }, + exports: function (name) { + var e = defined[name]; + if (typeof e !== 'undefined') { + return e; + } else { + return (defined[name] = {}); + } + }, + module: function (name) { + return { + id: name, + uri: '', + exports: defined[name], + config: makeConfig(name) + }; + } + }; + + main = function (name, deps, callback, relName) { + var cjsModule, depName, ret, map, i, relParts, + args = [], + callbackType = typeof callback, + usingExports; + + //Use name if no relName + relName = relName || name; + relParts = makeRelParts(relName); + + //Call the callback to define the module, if necessary. + if (callbackType === 'undefined' || callbackType === 'function') { + //Pull out the defined dependencies and pass the ordered + //values to the callback. + //Default to [require, exports, module] if no deps + deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; + for (i = 0; i < deps.length; i += 1) { + map = makeMap(deps[i], relParts); + depName = map.f; + + //Fast path CommonJS standard dependencies. + if (depName === "require") { + args[i] = handlers.require(name); + } else if (depName === "exports") { + //CommonJS module spec 1.1 + args[i] = handlers.exports(name); + usingExports = true; + } else if (depName === "module") { + //CommonJS module spec 1.1 + cjsModule = args[i] = handlers.module(name); + } else if (hasProp(defined, depName) || + hasProp(waiting, depName) || + hasProp(defining, depName)) { + args[i] = callDep(depName); + } else if (map.p) { + map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); + args[i] = defined[depName]; + } else { + throw new Error(name + ' missing ' + depName); + } + } + + ret = callback ? callback.apply(defined[name], args) : undefined; + + if (name) { + //If setting exports via "module" is in play, + //favor that over return value and exports. After that, + //favor a non-undefined return value over exports use. + if (cjsModule && cjsModule.exports !== undef && + cjsModule.exports !== defined[name]) { + defined[name] = cjsModule.exports; + } else if (ret !== undef || !usingExports) { + //Use the return value from the function. + defined[name] = ret; + } + } + } else if (name) { + //May just be an object definition for the module. Only + //worry about defining if have a module name. + defined[name] = callback; + } + }; + + requirejs = require = req = function (deps, callback, relName, forceSync, alt) { + if (typeof deps === "string") { + if (handlers[deps]) { + //callback in this case is really relName + return handlers[deps](callback); + } + //Just return the module wanted. In this scenario, the + //deps arg is the module name, and second arg (if passed) + //is just the relName. + //Normalize module name, if it contains . or .. + return callDep(makeMap(deps, makeRelParts(callback)).f); + } else if (!deps.splice) { + //deps is a config object, not an array. + config = deps; + if (config.deps) { + req(config.deps, config.callback); + } + if (!callback) { + return; + } + + if (callback.splice) { + //callback is an array, which means it is a dependency list. + //Adjust args if there are dependencies + deps = callback; + callback = relName; + relName = null; + } else { + deps = undef; + } + } + + //Support require(['a']) + callback = callback || function () {}; + + //If relName is a function, it is an errback handler, + //so remove it. + if (typeof relName === 'function') { + relName = forceSync; + forceSync = alt; + } + + //Simulate async callback; + if (forceSync) { + main(undef, deps, callback, relName); + } else { + //Using a non-zero value because of concern for what old browsers + //do, and latest browsers "upgrade" to 4 if lower value is used: + //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout: + //If want a value immediately, use require('id') instead -- something + //that works in almond on the global level, but not guaranteed and + //unlikely to work in other AMD implementations. + setTimeout(function () { + main(undef, deps, callback, relName); + }, 4); + } + + return req; + }; + + /** + * Just drops the config on the floor, but returns req in case + * the config return value is used. + */ + req.config = function (cfg) { + return req(cfg); + }; + + /** + * Expose module registry for debugging and tooling + */ + requirejs._defined = defined; + + define = function (name, deps, callback) { + if (typeof name !== 'string') { + throw new Error('See almond README: incorrect module build, no module name'); + } + + //This module may not have dependencies + if (!deps.splice) { + //deps is not an array, so probably means + //an object literal or factory function for + //the value. Adjust args. + callback = deps; + deps = []; + } + + if (!hasProp(defined, name) && !hasProp(waiting, name)) { + waiting[name] = [name, deps, callback]; + } + }; + + define.amd = { + jQuery: true + }; +}()); + +S2.requirejs = requirejs;S2.require = require;S2.define = define; +} +}()); +S2.define("almond", function(){}); + +/* global jQuery:false, $:false */ +S2.define('jquery',[],function () { + var _$ = jQuery || $; + + if (_$ == null && console && console.error) { + console.error( + 'Select2: An instance of jQuery or a jQuery-compatible library was not ' + + 'found. Make sure that you are including jQuery before Select2 on your ' + + 'web page.' + ); + } + + return _$; +}); + +S2.define('select2/utils',[ + 'jquery' +], function ($) { + var Utils = {}; + + Utils.Extend = function (ChildClass, SuperClass) { + var __hasProp = {}.hasOwnProperty; + + function BaseConstructor () { + this.constructor = ChildClass; + } + + for (var key in SuperClass) { + if (__hasProp.call(SuperClass, key)) { + ChildClass[key] = SuperClass[key]; + } + } + + BaseConstructor.prototype = SuperClass.prototype; + ChildClass.prototype = new BaseConstructor(); + ChildClass.__super__ = SuperClass.prototype; + + return ChildClass; + }; + + function getMethods (theClass) { + var proto = theClass.prototype; + + var methods = []; + + for (var methodName in proto) { + var m = proto[methodName]; + + if (typeof m !== 'function') { + continue; + } + + if (methodName === 'constructor') { + continue; + } + + methods.push(methodName); + } + + return methods; + } + + Utils.Decorate = function (SuperClass, DecoratorClass) { + var decoratedMethods = getMethods(DecoratorClass); + var superMethods = getMethods(SuperClass); + + function DecoratedClass () { + var unshift = Array.prototype.unshift; + + var argCount = DecoratorClass.prototype.constructor.length; + + var calledConstructor = SuperClass.prototype.constructor; + + if (argCount > 0) { + unshift.call(arguments, SuperClass.prototype.constructor); + + calledConstructor = DecoratorClass.prototype.constructor; + } + + calledConstructor.apply(this, arguments); + } + + DecoratorClass.displayName = SuperClass.displayName; + + function ctr () { + this.constructor = DecoratedClass; + } + + DecoratedClass.prototype = new ctr(); + + for (var m = 0; m < superMethods.length; m++) { + var superMethod = superMethods[m]; + + DecoratedClass.prototype[superMethod] = + SuperClass.prototype[superMethod]; + } + + var calledMethod = function (methodName) { + // Stub out the original method if it's not decorating an actual method + var originalMethod = function () {}; + + if (methodName in DecoratedClass.prototype) { + originalMethod = DecoratedClass.prototype[methodName]; + } + + var decoratedMethod = DecoratorClass.prototype[methodName]; + + return function () { + var unshift = Array.prototype.unshift; + + unshift.call(arguments, originalMethod); + + return decoratedMethod.apply(this, arguments); + }; + }; + + for (var d = 0; d < decoratedMethods.length; d++) { + var decoratedMethod = decoratedMethods[d]; + + DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod); + } + + return DecoratedClass; + }; + + var Observable = function () { + this.listeners = {}; + }; + + Observable.prototype.on = function (event, callback) { + this.listeners = this.listeners || {}; + + if (event in this.listeners) { + this.listeners[event].push(callback); + } else { + this.listeners[event] = [callback]; + } + }; + + Observable.prototype.trigger = function (event) { + var slice = Array.prototype.slice; + var params = slice.call(arguments, 1); + + this.listeners = this.listeners || {}; + + // Params should always come in as an array + if (params == null) { + params = []; + } + + // If there are no arguments to the event, use a temporary object + if (params.length === 0) { + params.push({}); + } + + // Set the `_type` of the first object to the event + params[0]._type = event; + + if (event in this.listeners) { + this.invoke(this.listeners[event], slice.call(arguments, 1)); + } + + if ('*' in this.listeners) { + this.invoke(this.listeners['*'], arguments); + } + }; + + Observable.prototype.invoke = function (listeners, params) { + for (var i = 0, len = listeners.length; i < len; i++) { + listeners[i].apply(this, params); + } + }; + + Utils.Observable = Observable; + + Utils.generateChars = function (length) { + var chars = ''; + + for (var i = 0; i < length; i++) { + var randomChar = Math.floor(Math.random() * 36); + chars += randomChar.toString(36); + } + + return chars; + }; + + Utils.bind = function (func, context) { + return function () { + func.apply(context, arguments); + }; + }; + + Utils._convertData = function (data) { + for (var originalKey in data) { + var keys = originalKey.split('-'); + + var dataLevel = data; + + if (keys.length === 1) { + continue; + } + + for (var k = 0; k < keys.length; k++) { + var key = keys[k]; + + // Lowercase the first letter + // By default, dash-separated becomes camelCase + key = key.substring(0, 1).toLowerCase() + key.substring(1); + + if (!(key in dataLevel)) { + dataLevel[key] = {}; + } + + if (k == keys.length - 1) { + dataLevel[key] = data[originalKey]; + } + + dataLevel = dataLevel[key]; + } + + delete data[originalKey]; + } + + return data; + }; + + Utils.hasScroll = function (index, el) { + // Adapted from the function created by @ShadowScripter + // and adapted by @BillBarry on the Stack Exchange Code Review website. + // The original code can be found at + // http://codereview.stackexchange.com/q/13338 + // and was designed to be used with the Sizzle selector engine. + + var $el = $(el); + var overflowX = el.style.overflowX; + var overflowY = el.style.overflowY; + + //Check both x and y declarations + if (overflowX === overflowY && + (overflowY === 'hidden' || overflowY === 'visible')) { + return false; + } + + if (overflowX === 'scroll' || overflowY === 'scroll') { + return true; + } + + return ($el.innerHeight() < el.scrollHeight || + $el.innerWidth() < el.scrollWidth); + }; + + Utils.escapeMarkup = function (markup) { + var replaceMap = { + '\\': '\', + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''', + '/': '/' + }; + + // Do not try to escape the markup if it's not a string + if (typeof markup !== 'string') { + return markup; + } + + return String(markup).replace(/[&<>"'\/\\]/g, function (match) { + return replaceMap[match]; + }); + }; + + // Cache objects in Utils.__cache instead of $.data (see #4346) + Utils.__cache = {}; + + var id = 0; + Utils.GetUniqueElementId = function (element) { + // Get a unique element Id. If element has no id, + // creates a new unique number, stores it in the id + // attribute and returns the new id with a prefix. + // If an id already exists, it simply returns it with a prefix. + + var select2Id = element.getAttribute('data-select2-id'); + + if (select2Id != null) { + return select2Id; + } + + // If element has id, use it. + if (element.id) { + select2Id = 'select2-data-' + element.id; + } else { + select2Id = 'select2-data-' + (++id).toString() + + '-' + Utils.generateChars(4); + } + + element.setAttribute('data-select2-id', select2Id); + + return select2Id; + }; + + Utils.StoreData = function (element, name, value) { + // Stores an item in the cache for a specified element. + // name is the cache key. + var id = Utils.GetUniqueElementId(element); + if (!Utils.__cache[id]) { + Utils.__cache[id] = {}; + } + + Utils.__cache[id][name] = value; + }; + + Utils.GetData = function (element, name) { + // Retrieves a value from the cache by its key (name) + // name is optional. If no name specified, return + // all cache items for the specified element. + // and for a specified element. + var id = Utils.GetUniqueElementId(element); + if (name) { + if (Utils.__cache[id]) { + if (Utils.__cache[id][name] != null) { + return Utils.__cache[id][name]; + } + return $(element).data(name); // Fallback to HTML5 data attribs. + } + return $(element).data(name); // Fallback to HTML5 data attribs. + } else { + return Utils.__cache[id]; + } + }; + + Utils.RemoveData = function (element) { + // Removes all cached items for a specified element. + var id = Utils.GetUniqueElementId(element); + if (Utils.__cache[id] != null) { + delete Utils.__cache[id]; + } + + element.removeAttribute('data-select2-id'); + }; + + Utils.copyNonInternalCssClasses = function (dest, src) { + var classes; + + var destinationClasses = dest.getAttribute('class').trim().split(/\s+/); + + destinationClasses = destinationClasses.filter(function (clazz) { + // Save all Select2 classes + return clazz.indexOf('select2-') === 0; + }); + + var sourceClasses = src.getAttribute('class').trim().split(/\s+/); + + sourceClasses = sourceClasses.filter(function (clazz) { + // Only copy non-Select2 classes + return clazz.indexOf('select2-') !== 0; + }); + + var replacements = destinationClasses.concat(sourceClasses); + + dest.setAttribute('class', replacements.join(' ')); + }; + + return Utils; +}); + +S2.define('select2/results',[ + 'jquery', + './utils' +], function ($, Utils) { + function Results ($element, options, dataAdapter) { + this.$element = $element; + this.data = dataAdapter; + this.options = options; + + Results.__super__.constructor.call(this); + } + + Utils.Extend(Results, Utils.Observable); + + Results.prototype.render = function () { + var $results = $( + '
        ' + ); + + if (this.options.get('multiple')) { + $results.attr('aria-multiselectable', 'true'); + } + + this.$results = $results; + + return $results; + }; + + Results.prototype.clear = function () { + this.$results.empty(); + }; + + Results.prototype.displayMessage = function (params) { + var escapeMarkup = this.options.get('escapeMarkup'); + + this.clear(); + this.hideLoading(); + + var $message = $( + '' + ); + + var message = this.options.get('translations').get(params.message); + + $message.append( + escapeMarkup( + message(params.args) + ) + ); + + $message[0].className += ' select2-results__message'; + + this.$results.append($message); + }; + + Results.prototype.hideMessages = function () { + this.$results.find('.select2-results__message').remove(); + }; + + Results.prototype.append = function (data) { + this.hideLoading(); + + var $options = []; + + if (data.results == null || data.results.length === 0) { + if (this.$results.children().length === 0) { + this.trigger('results:message', { + message: 'noResults' + }); + } + + return; + } + + data.results = this.sort(data.results); + + for (var d = 0; d < data.results.length; d++) { + var item = data.results[d]; + + var $option = this.option(item); + + $options.push($option); + } + + this.$results.append($options); + }; + + Results.prototype.position = function ($results, $dropdown) { + var $resultsContainer = $dropdown.find('.select2-results'); + $resultsContainer.append($results); + }; + + Results.prototype.sort = function (data) { + var sorter = this.options.get('sorter'); + + return sorter(data); + }; + + Results.prototype.highlightFirstItem = function () { + var $options = this.$results + .find('.select2-results__option--selectable'); + + var $selected = $options.filter('.select2-results__option--selected'); + + // Check if there are any selected options + if ($selected.length > 0) { + // If there are selected options, highlight the first + $selected.first().trigger('mouseenter'); + } else { + // If there are no selected options, highlight the first option + // in the dropdown + $options.first().trigger('mouseenter'); + } + + this.ensureHighlightVisible(); + }; + + Results.prototype.setClasses = function () { + var self = this; + + this.data.current(function (selected) { + var selectedIds = selected.map(function (s) { + return s.id.toString(); + }); + + var $options = self.$results + .find('.select2-results__option--selectable'); + + $options.each(function () { + var $option = $(this); + + var item = Utils.GetData(this, 'data'); + + // id needs to be converted to a string when comparing + var id = '' + item.id; + + if ((item.element != null && item.element.selected) || + (item.element == null && selectedIds.indexOf(id) > -1)) { + this.classList.add('select2-results__option--selected'); + $option.attr('aria-selected', 'true'); + } else { + this.classList.remove('select2-results__option--selected'); + $option.attr('aria-selected', 'false'); + } + }); + + }); + }; + + Results.prototype.showLoading = function (params) { + this.hideLoading(); + + var loadingMore = this.options.get('translations').get('searching'); + + var loading = { + disabled: true, + loading: true, + text: loadingMore(params) + }; + var $loading = this.option(loading); + $loading.className += ' loading-results'; + + this.$results.prepend($loading); + }; + + Results.prototype.hideLoading = function () { + this.$results.find('.loading-results').remove(); + }; + + Results.prototype.option = function (data) { + var option = document.createElement('li'); + option.classList.add('select2-results__option'); + option.classList.add('select2-results__option--selectable'); + + var attrs = { + 'role': 'option' + }; + + var matches = window.Element.prototype.matches || + window.Element.prototype.msMatchesSelector || + window.Element.prototype.webkitMatchesSelector; + + if ((data.element != null && matches.call(data.element, ':disabled')) || + (data.element == null && data.disabled)) { + attrs['aria-disabled'] = 'true'; + + option.classList.remove('select2-results__option--selectable'); + option.classList.add('select2-results__option--disabled'); + } + + if (data.id == null) { + option.classList.remove('select2-results__option--selectable'); + } + + if (data._resultId != null) { + option.id = data._resultId; + } + + if (data.title) { + option.title = data.title; + } + + if (data.children) { + attrs.role = 'group'; + attrs['aria-label'] = data.text; + + option.classList.remove('select2-results__option--selectable'); + option.classList.add('select2-results__option--group'); + } + + for (var attr in attrs) { + var val = attrs[attr]; + + option.setAttribute(attr, val); + } + + if (data.children) { + var $option = $(option); + + var label = document.createElement('strong'); + label.className = 'select2-results__group'; + + this.template(data, label); + + var $children = []; + + for (var c = 0; c < data.children.length; c++) { + var child = data.children[c]; + + var $child = this.option(child); + + $children.push($child); + } + + var $childrenContainer = $('
          ', { + 'class': 'select2-results__options select2-results__options--nested', + 'role': 'none' + }); + + $childrenContainer.append($children); + + $option.append(label); + $option.append($childrenContainer); + } else { + this.template(data, option); + } + + Utils.StoreData(option, 'data', data); + + return option; + }; + + Results.prototype.bind = function (container, $container) { + var self = this; + + var id = container.id + '-results'; + + this.$results.attr('id', id); + + container.on('results:all', function (params) { + self.clear(); + self.append(params.data); + + if (container.isOpen()) { + self.setClasses(); + self.highlightFirstItem(); + } + }); + + container.on('results:append', function (params) { + self.append(params.data); + + if (container.isOpen()) { + self.setClasses(); + } + }); + + container.on('query', function (params) { + self.hideMessages(); + self.showLoading(params); + }); + + container.on('select', function () { + if (!container.isOpen()) { + return; + } + + self.setClasses(); + + if (self.options.get('scrollAfterSelect')) { + self.highlightFirstItem(); + } + }); + + container.on('unselect', function () { + if (!container.isOpen()) { + return; + } + + self.setClasses(); + + if (self.options.get('scrollAfterSelect')) { + self.highlightFirstItem(); + } + }); + + container.on('open', function () { + // When the dropdown is open, aria-expended="true" + self.$results.attr('aria-expanded', 'true'); + self.$results.attr('aria-hidden', 'false'); + + self.setClasses(); + self.ensureHighlightVisible(); + }); + + container.on('close', function () { + // When the dropdown is closed, aria-expended="false" + self.$results.attr('aria-expanded', 'false'); + self.$results.attr('aria-hidden', 'true'); + self.$results.removeAttr('aria-activedescendant'); + }); + + container.on('results:toggle', function () { + var $highlighted = self.getHighlightedResults(); + + if ($highlighted.length === 0) { + return; + } + + $highlighted.trigger('mouseup'); + }); + + container.on('results:select', function () { + var $highlighted = self.getHighlightedResults(); + + if ($highlighted.length === 0) { + return; + } + + var data = Utils.GetData($highlighted[0], 'data'); + + if ($highlighted.hasClass('select2-results__option--selected')) { + self.trigger('close', {}); + } else { + self.trigger('select', { + data: data + }); + } + }); + + container.on('results:previous', function () { + var $highlighted = self.getHighlightedResults(); + + var $options = self.$results.find('.select2-results__option--selectable'); + + var currentIndex = $options.index($highlighted); + + // If we are already at the top, don't move further + // If no options, currentIndex will be -1 + if (currentIndex <= 0) { + return; + } + + var nextIndex = currentIndex - 1; + + // If none are highlighted, highlight the first + if ($highlighted.length === 0) { + nextIndex = 0; + } + + var $next = $options.eq(nextIndex); + + $next.trigger('mouseenter'); + + var currentOffset = self.$results.offset().top; + var nextTop = $next.offset().top; + var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset); + + if (nextIndex === 0) { + self.$results.scrollTop(0); + } else if (nextTop - currentOffset < 0) { + self.$results.scrollTop(nextOffset); + } + }); + + container.on('results:next', function () { + var $highlighted = self.getHighlightedResults(); + + var $options = self.$results.find('.select2-results__option--selectable'); + + var currentIndex = $options.index($highlighted); + + var nextIndex = currentIndex + 1; + + // If we are at the last option, stay there + if (nextIndex >= $options.length) { + return; + } + + var $next = $options.eq(nextIndex); + + $next.trigger('mouseenter'); + + var currentOffset = self.$results.offset().top + + self.$results.outerHeight(false); + var nextBottom = $next.offset().top + $next.outerHeight(false); + var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset; + + if (nextIndex === 0) { + self.$results.scrollTop(0); + } else if (nextBottom > currentOffset) { + self.$results.scrollTop(nextOffset); + } + }); + + container.on('results:focus', function (params) { + params.element[0].classList.add('select2-results__option--highlighted'); + params.element[0].setAttribute('aria-selected', 'true'); + }); + + container.on('results:message', function (params) { + self.displayMessage(params); + }); + + if ($.fn.mousewheel) { + this.$results.on('mousewheel', function (e) { + var top = self.$results.scrollTop(); + + var bottom = self.$results.get(0).scrollHeight - top + e.deltaY; + + var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0; + var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height(); + + if (isAtTop) { + self.$results.scrollTop(0); + + e.preventDefault(); + e.stopPropagation(); + } else if (isAtBottom) { + self.$results.scrollTop( + self.$results.get(0).scrollHeight - self.$results.height() + ); + + e.preventDefault(); + e.stopPropagation(); + } + }); + } + + this.$results.on('mouseup', '.select2-results__option--selectable', + function (evt) { + var $this = $(this); + + var data = Utils.GetData(this, 'data'); + + if ($this.hasClass('select2-results__option--selected')) { + if (self.options.get('multiple')) { + self.trigger('unselect', { + originalEvent: evt, + data: data + }); + } else { + self.trigger('close', {}); + } + + return; + } + + self.trigger('select', { + originalEvent: evt, + data: data + }); + }); + + this.$results.on('mouseenter', '.select2-results__option--selectable', + function (evt) { + var data = Utils.GetData(this, 'data'); + + self.getHighlightedResults() + .removeClass('select2-results__option--highlighted') + .attr('aria-selected', 'false'); + + self.trigger('results:focus', { + data: data, + element: $(this) + }); + }); + }; + + Results.prototype.getHighlightedResults = function () { + var $highlighted = this.$results + .find('.select2-results__option--highlighted'); + + return $highlighted; + }; + + Results.prototype.destroy = function () { + this.$results.remove(); + }; + + Results.prototype.ensureHighlightVisible = function () { + var $highlighted = this.getHighlightedResults(); + + if ($highlighted.length === 0) { + return; + } + + var $options = this.$results.find('.select2-results__option--selectable'); + + var currentIndex = $options.index($highlighted); + + var currentOffset = this.$results.offset().top; + var nextTop = $highlighted.offset().top; + var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset); + + var offsetDelta = nextTop - currentOffset; + nextOffset -= $highlighted.outerHeight(false) * 2; + + if (currentIndex <= 2) { + this.$results.scrollTop(0); + } else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) { + this.$results.scrollTop(nextOffset); + } + }; + + Results.prototype.template = function (result, container) { + var template = this.options.get('templateResult'); + var escapeMarkup = this.options.get('escapeMarkup'); + + var content = template(result, container); + + if (content == null) { + container.style.display = 'none'; + } else if (typeof content === 'string') { + container.innerHTML = escapeMarkup(content); + } else { + $(container).append(content); + } + }; + + return Results; +}); + +S2.define('select2/keys',[ + +], function () { + var KEYS = { + BACKSPACE: 8, + TAB: 9, + ENTER: 13, + SHIFT: 16, + CTRL: 17, + ALT: 18, + ESC: 27, + SPACE: 32, + PAGE_UP: 33, + PAGE_DOWN: 34, + END: 35, + HOME: 36, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + DELETE: 46 + }; + + return KEYS; +}); + +S2.define('select2/selection/base',[ + 'jquery', + '../utils', + '../keys' +], function ($, Utils, KEYS) { + function BaseSelection ($element, options) { + this.$element = $element; + this.options = options; + + BaseSelection.__super__.constructor.call(this); + } + + Utils.Extend(BaseSelection, Utils.Observable); + + BaseSelection.prototype.render = function () { + var $selection = $( + '' + ); + + this._tabindex = 0; + + if (Utils.GetData(this.$element[0], 'old-tabindex') != null) { + this._tabindex = Utils.GetData(this.$element[0], 'old-tabindex'); + } else if (this.$element.attr('tabindex') != null) { + this._tabindex = this.$element.attr('tabindex'); + } + + $selection.attr('title', this.$element.attr('title')); + $selection.attr('tabindex', this._tabindex); + $selection.attr('aria-disabled', 'false'); + + this.$selection = $selection; + + return $selection; + }; + + BaseSelection.prototype.bind = function (container, $container) { + var self = this; + + var resultsId = container.id + '-results'; + + this.container = container; + + this.$selection.on('focus', function (evt) { + self.trigger('focus', evt); + }); + + this.$selection.on('blur', function (evt) { + self._handleBlur(evt); + }); + + this.$selection.on('keydown', function (evt) { + self.trigger('keypress', evt); + + if (evt.which === KEYS.SPACE) { + evt.preventDefault(); + } + }); + + container.on('results:focus', function (params) { + self.$selection.attr('aria-activedescendant', params.data._resultId); + }); + + container.on('selection:update', function (params) { + self.update(params.data); + }); + + container.on('open', function () { + // When the dropdown is open, aria-expanded="true" + self.$selection.attr('aria-expanded', 'true'); + self.$selection.attr('aria-owns', resultsId); + + self._attachCloseHandler(container); + }); + + container.on('close', function () { + // When the dropdown is closed, aria-expanded="false" + self.$selection.attr('aria-expanded', 'false'); + self.$selection.removeAttr('aria-activedescendant'); + self.$selection.removeAttr('aria-owns'); + + self.$selection.trigger('focus'); + + self._detachCloseHandler(container); + }); + + container.on('enable', function () { + self.$selection.attr('tabindex', self._tabindex); + self.$selection.attr('aria-disabled', 'false'); + }); + + container.on('disable', function () { + self.$selection.attr('tabindex', '-1'); + self.$selection.attr('aria-disabled', 'true'); + }); + }; + + BaseSelection.prototype._handleBlur = function (evt) { + var self = this; + + // This needs to be delayed as the active element is the body when the tab + // key is pressed, possibly along with others. + window.setTimeout(function () { + // Don't trigger `blur` if the focus is still in the selection + if ( + (document.activeElement == self.$selection[0]) || + ($.contains(self.$selection[0], document.activeElement)) + ) { + return; + } + + self.trigger('blur', evt); + }, 1); + }; + + BaseSelection.prototype._attachCloseHandler = function (container) { + + $(document.body).on('mousedown.select2.' + container.id, function (e) { + var $target = $(e.target); + + var $select = $target.closest('.select2'); + + var $all = $('.select2.select2-container--open'); + + $all.each(function () { + if (this == $select[0]) { + return; + } + + var $element = Utils.GetData(this, 'element'); + + $element.select2('close'); + }); + }); + }; + + BaseSelection.prototype._detachCloseHandler = function (container) { + $(document.body).off('mousedown.select2.' + container.id); + }; + + BaseSelection.prototype.position = function ($selection, $container) { + var $selectionContainer = $container.find('.selection'); + $selectionContainer.append($selection); + }; + + BaseSelection.prototype.destroy = function () { + this._detachCloseHandler(this.container); + }; + + BaseSelection.prototype.update = function (data) { + throw new Error('The `update` method must be defined in child classes.'); + }; + + /** + * Helper method to abstract the "enabled" (not "disabled") state of this + * object. + * + * @return {true} if the instance is not disabled. + * @return {false} if the instance is disabled. + */ + BaseSelection.prototype.isEnabled = function () { + return !this.isDisabled(); + }; + + /** + * Helper method to abstract the "disabled" state of this object. + * + * @return {true} if the disabled option is true. + * @return {false} if the disabled option is false. + */ + BaseSelection.prototype.isDisabled = function () { + return this.options.get('disabled'); + }; + + return BaseSelection; +}); + +S2.define('select2/selection/single',[ + 'jquery', + './base', + '../utils', + '../keys' +], function ($, BaseSelection, Utils, KEYS) { + function SingleSelection () { + SingleSelection.__super__.constructor.apply(this, arguments); + } + + Utils.Extend(SingleSelection, BaseSelection); + + SingleSelection.prototype.render = function () { + var $selection = SingleSelection.__super__.render.call(this); + + $selection[0].classList.add('select2-selection--single'); + + $selection.html( + ''+ this.placeholder.text +'' + + '' + + '' + + '' + + '' + ); + + return $selection; + }; + + SingleSelection.prototype.bind = function (container, $container) { + var self = this; + + SingleSelection.__super__.bind.apply(this, arguments); + + var id = container.id + '-container'; + + this.$selection.find('.select2-selection__rendered') + .attr('id', id) + .attr('role', 'textbox') + .attr('aria-readonly', 'true'); + this.$selection.attr('aria-labelledby', id); + this.$selection.attr('aria-controls', id); + + this.$selection.on('mousedown', function (evt) { + // Only respond to left clicks + if (evt.which !== 1) { + return; + } + + self.trigger('toggle', { + originalEvent: evt + }); + }); + + this.$selection.on('focus', function (evt) { + // User focuses on the container + }); + + this.$selection.on('blur', function (evt) { + // User exits the container + }); + + container.on('focus', function (evt) { + if (!container.isOpen()) { + self.$selection.trigger('focus'); + } + }); + }; + + SingleSelection.prototype.clear = function () { + var $rendered = this.$selection.find('.select2-selection__rendered'); + $rendered.empty(); + $rendered.removeAttr('title'); // clear tooltip on empty + }; + + SingleSelection.prototype.display = function (data, container) { + var template = this.options.get('templateSelection'); + var escapeMarkup = this.options.get('escapeMarkup'); + + return escapeMarkup(template(data, container)); + }; + + SingleSelection.prototype.selectionContainer = function () { + return $(''); + }; + + SingleSelection.prototype.update = function (data) { + if (data.length === 0) { + this.clear(); + return; + } + + var selection = data[0]; + + var $rendered = this.$selection.find('.select2-selection__rendered'); + var formatted = this.display(selection, $rendered); + + $rendered.empty().append(formatted); + + var title = selection.title || selection.text; + + if (title) { + $rendered.attr('title', title); + } else { + $rendered.removeAttr('title'); + } + }; + + return SingleSelection; +}); + +S2.define('select2/selection/multiple',[ + 'jquery', + './base', + '../utils' +], function ($, BaseSelection, Utils) { + function MultipleSelection ($element, options) { + MultipleSelection.__super__.constructor.apply(this, arguments); + } + + Utils.Extend(MultipleSelection, BaseSelection); + + MultipleSelection.prototype.render = function () { + var $selection = MultipleSelection.__super__.render.call(this); + + $selection[0].classList.add('select2-selection--multiple'); + + $selection.html( + '
            ' + ); + + return $selection; + }; + + MultipleSelection.prototype.bind = function (container, $container) { + var self = this; + + MultipleSelection.__super__.bind.apply(this, arguments); + + var id = container.id + '-container'; + this.$selection.find('.select2-selection__rendered').attr('id', id); + + this.$selection.on('click', function (evt) { + self.trigger('toggle', { + originalEvent: evt + }); + }); + + this.$selection.on( + 'click', + '.select2-selection__choice__remove', + function (evt) { + // Ignore the event if it is disabled + if (self.isDisabled()) { + return; + } + + var $remove = $(this); + var $selection = $remove.parent(); + + var data = Utils.GetData($selection[0], 'data'); + + self.trigger('unselect', { + originalEvent: evt, + data: data + }); + } + ); + + this.$selection.on( + 'keydown', + '.select2-selection__choice__remove', + function (evt) { + // Ignore the event if it is disabled + if (self.isDisabled()) { + return; + } + + evt.stopPropagation(); + } + ); + }; + + MultipleSelection.prototype.clear = function () { + var $rendered = this.$selection.find('.select2-selection__rendered'); + $rendered.empty(); + $rendered.removeAttr('title'); + }; + + MultipleSelection.prototype.display = function (data, container) { + var template = this.options.get('templateSelection'); + var escapeMarkup = this.options.get('escapeMarkup'); + + return escapeMarkup(template(data, container)); + }; + + MultipleSelection.prototype.selectionContainer = function () { + var $container = $( + '
          • ' + + '' + + '' + + '
          • ' + ); + + return $container; + }; + + MultipleSelection.prototype.update = function (data) { + this.clear(); + + if (data.length === 0) { + return; + } + + var $selections = []; + + var selectionIdPrefix = this.$selection.find('.select2-selection__rendered') + .attr('id') + '-choice-'; + + for (var d = 0; d < data.length; d++) { + var selection = data[d]; + + var $selection = this.selectionContainer(); + var formatted = this.display(selection, $selection); + + var selectionId = selectionIdPrefix + Utils.generateChars(4) + '-'; + + if (selection.id) { + selectionId += selection.id; + } else { + selectionId += Utils.generateChars(4); + } + + $selection.find('.select2-selection__choice__display') + .append(formatted) + .attr('id', selectionId); + + var title = selection.title || selection.text; + + if (title) { + $selection.attr('title', title); + } + + var removeItem = this.options.get('translations').get('removeItem'); + + var $remove = $selection.find('.select2-selection__choice__remove'); + + $remove.attr('title', removeItem()); + $remove.attr('aria-label', removeItem()); + $remove.attr('aria-describedby', selectionId); + + Utils.StoreData($selection[0], 'data', selection); + + $selections.push($selection); + } + + var $rendered = this.$selection.find('.select2-selection__rendered'); + + $rendered.append($selections); + }; + + return MultipleSelection; +}); + +S2.define('select2/selection/placeholder',[ + +], function () { + function Placeholder (decorated, $element, options) { + this.placeholder = this.normalizePlaceholder(options.get('placeholder')); + + decorated.call(this, $element, options); + } + + Placeholder.prototype.normalizePlaceholder = function (_, placeholder) { + if (typeof placeholder === 'string') { + placeholder = { + id: '', + text: placeholder + }; + } + + return placeholder; + }; + + Placeholder.prototype.createPlaceholder = function (decorated, placeholder) { + var $placeholder = this.selectionContainer(); + + $placeholder.html(this.display(placeholder)); + $placeholder[0].classList.add('select2-selection__placeholder'); + $placeholder[0].classList.remove('select2-selection__choice'); + + var placeholderTitle = placeholder.title || + placeholder.text || + $placeholder.text(); + + this.$selection.find('.select2-selection__rendered').attr( + 'title', + placeholderTitle + ); + + return $placeholder; + }; + + Placeholder.prototype.update = function (decorated, data) { + var singlePlaceholder = ( + data.length == 1 && data[0].id != this.placeholder.id + ); + var multipleSelections = data.length > 1; + + if (multipleSelections || singlePlaceholder) { + return decorated.call(this, data); + } + + this.clear(); + + var $placeholder = this.createPlaceholder(this.placeholder); + + this.$selection.find('.select2-selection__rendered').append($placeholder); + }; + + return Placeholder; +}); + +S2.define('select2/selection/allowClear',[ + 'jquery', + '../keys', + '../utils' +], function ($, KEYS, Utils) { + function AllowClear () { } + + AllowClear.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + if (this.placeholder == null) { + if (this.options.get('debug') && window.console && console.error) { + console.error( + 'Select2: The `allowClear` option should be used in combination ' + + 'with the `placeholder` option.' + ); + } + } + + this.$selection.on('mousedown', '.select2-selection__clear', + function (evt) { + self._handleClear(evt); + }); + + container.on('keypress', function (evt) { + self._handleKeyboardClear(evt, container); + }); + }; + + AllowClear.prototype._handleClear = function (_, evt) { + // Ignore the event if it is disabled + if (this.isDisabled()) { + return; + } + + var $clear = this.$selection.find('.select2-selection__clear'); + + // Ignore the event if nothing has been selected + if ($clear.length === 0) { + return; + } + + evt.stopPropagation(); + + var data = Utils.GetData($clear[0], 'data'); + + var previousVal = this.$element.val(); + this.$element.val(this.placeholder.id); + + var unselectData = { + data: data + }; + this.trigger('clear', unselectData); + if (unselectData.prevented) { + this.$element.val(previousVal); + return; + } + + for (var d = 0; d < data.length; d++) { + unselectData = { + data: data[d] + }; + + // Trigger the `unselect` event, so people can prevent it from being + // cleared. + this.trigger('unselect', unselectData); + + // If the event was prevented, don't clear it out. + if (unselectData.prevented) { + this.$element.val(previousVal); + return; + } + } + + this.$element.trigger('input').trigger('change'); + + this.trigger('toggle', {}); + }; + + AllowClear.prototype._handleKeyboardClear = function (_, evt, container) { + if (container.isOpen()) { + return; + } + + if (evt.which == KEYS.DELETE || evt.which == KEYS.BACKSPACE) { + this._handleClear(evt); + } + }; + + AllowClear.prototype.update = function (decorated, data) { + decorated.call(this, data); + + this.$selection.find('.select2-selection__clear').remove(); + this.$selection[0].classList.remove('select2-selection--clearable'); + + if (this.$selection.find('.select2-selection__placeholder').length > 0 || + data.length === 0) { + return; + } + + var selectionId = this.$selection.find('.select2-selection__rendered') + .attr('id'); + + var removeAll = this.options.get('translations').get('removeAllItems'); + + var $remove = $( + '' + ); + $remove.attr('title', removeAll()); + $remove.attr('aria-label', removeAll()); + $remove.attr('aria-describedby', selectionId); + Utils.StoreData($remove[0], 'data', data); + + this.$selection.prepend($remove); + this.$selection[0].classList.add('select2-selection--clearable'); + }; + + return AllowClear; +}); + +S2.define('select2/selection/search',[ + 'jquery', + '../utils', + '../keys' +], function ($, Utils, KEYS) { + function Search (decorated, $element, options) { + decorated.call(this, $element, options); + } + + Search.prototype.render = function (decorated) { + var searchLabel = this.options.get('translations').get('search'); + var $search = $( + '' + + '' + + '' + ); + + this.$searchContainer = $search; + this.$search = $search.find('textarea'); + + this.$search.prop('autocomplete', this.options.get('autocomplete')); + this.$search.attr('aria-label', searchLabel()); + + var $rendered = decorated.call(this); + + this._transferTabIndex(); + $rendered.append(this.$searchContainer); + + return $rendered; + }; + + Search.prototype.bind = function (decorated, container, $container) { + var self = this; + + var resultsId = container.id + '-results'; + var selectionId = container.id + '-container'; + + decorated.call(this, container, $container); + + self.$search.attr('aria-describedby', selectionId); + + container.on('open', function () { + self.$search.attr('aria-controls', resultsId); + self.$search.trigger('focus'); + }); + + container.on('close', function () { + self.$search.val(''); + self.resizeSearch(); + self.$search.removeAttr('aria-controls'); + self.$search.removeAttr('aria-activedescendant'); + self.$search.trigger('focus'); + }); + + container.on('enable', function () { + self.$search.prop('disabled', false); + + self._transferTabIndex(); + }); + + container.on('disable', function () { + self.$search.prop('disabled', true); + }); + + container.on('focus', function (evt) { + self.$search.trigger('focus'); + }); + + container.on('results:focus', function (params) { + if (params.data._resultId) { + self.$search.attr('aria-activedescendant', params.data._resultId); + } else { + self.$search.removeAttr('aria-activedescendant'); + } + }); + + this.$selection.on('focusin', '.select2-search--inline', function (evt) { + self.trigger('focus', evt); + }); + + this.$selection.on('focusout', '.select2-search--inline', function (evt) { + self._handleBlur(evt); + }); + + this.$selection.on('keydown', '.select2-search--inline', function (evt) { + evt.stopPropagation(); + + self.trigger('keypress', evt); + + self._keyUpPrevented = evt.isDefaultPrevented(); + + var key = evt.which; + + if (key === KEYS.BACKSPACE && self.$search.val() === '') { + var $previousChoice = self.$selection + .find('.select2-selection__choice').last(); + + if ($previousChoice.length > 0) { + var item = Utils.GetData($previousChoice[0], 'data'); + + self.searchRemoveChoice(item); + + evt.preventDefault(); + } + } + }); + + this.$selection.on('click', '.select2-search--inline', function (evt) { + if (self.$search.val()) { + evt.stopPropagation(); + } + }); + + // Try to detect the IE version should the `documentMode` property that + // is stored on the document. This is only implemented in IE and is + // slightly cleaner than doing a user agent check. + // This property is not available in Edge, but Edge also doesn't have + // this bug. + var msie = document.documentMode; + var disableInputEvents = msie && msie <= 11; + + // Workaround for browsers which do not support the `input` event + // This will prevent double-triggering of events for browsers which support + // both the `keyup` and `input` events. + this.$selection.on( + 'input.searchcheck', + '.select2-search--inline', + function (evt) { + // IE will trigger the `input` event when a placeholder is used on a + // search box. To get around this issue, we are forced to ignore all + // `input` events in IE and keep using `keyup`. + if (disableInputEvents) { + self.$selection.off('input.search input.searchcheck'); + return; + } + + // Unbind the duplicated `keyup` event + self.$selection.off('keyup.search'); + } + ); + + this.$selection.on( + 'keyup.search input.search', + '.select2-search--inline', + function (evt) { + // IE will trigger the `input` event when a placeholder is used on a + // search box. To get around this issue, we are forced to ignore all + // `input` events in IE and keep using `keyup`. + if (disableInputEvents && evt.type === 'input') { + self.$selection.off('input.search input.searchcheck'); + return; + } + + var key = evt.which; + + // We can freely ignore events from modifier keys + if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) { + return; + } + + // Tabbing will be handled during the `keydown` phase + if (key == KEYS.TAB) { + return; + } + + self.handleSearch(evt); + } + ); + }; + + /** + * This method will transfer the tabindex attribute from the rendered + * selection to the search box. This allows for the search box to be used as + * the primary focus instead of the selection container. + * + * @private + */ + Search.prototype._transferTabIndex = function (decorated) { + this.$search.attr('tabindex', this.$selection.attr('tabindex')); + this.$selection.attr('tabindex', '-1'); + }; + + Search.prototype.createPlaceholder = function (decorated, placeholder) { + this.$search.attr('placeholder', placeholder.text); + }; + + Search.prototype.update = function (decorated, data) { + var searchHadFocus = this.$search[0] == document.activeElement; + + this.$search.attr('placeholder', ''); + + decorated.call(this, data); + + this.resizeSearch(); + if (searchHadFocus) { + this.$search.trigger('focus'); + } + }; + + Search.prototype.handleSearch = function () { + this.resizeSearch(); + + if (!this._keyUpPrevented) { + var input = this.$search.val(); + + this.trigger('query', { + term: input + }); + } + + this._keyUpPrevented = false; + }; + + Search.prototype.searchRemoveChoice = function (decorated, item) { + this.trigger('unselect', { + data: item + }); + + this.$search.val(item.text); + this.handleSearch(); + }; + + Search.prototype.resizeSearch = function () { + this.$search.css('width', '25px'); + + var width = '100%'; + + if (this.$search.attr('placeholder') === '') { + var minimumWidth = this.$search.val().length + 1; + + width = (minimumWidth * 0.75) + 'em'; + } + + this.$search.css('width', width); + }; + + return Search; +}); + +S2.define('select2/selection/selectionCss',[ + '../utils' +], function (Utils) { + function SelectionCSS () { } + + SelectionCSS.prototype.render = function (decorated) { + var $selection = decorated.call(this); + + var selectionCssClass = this.options.get('selectionCssClass') || ''; + + if (selectionCssClass.indexOf(':all:') !== -1) { + selectionCssClass = selectionCssClass.replace(':all:', ''); + + Utils.copyNonInternalCssClasses($selection[0], this.$element[0]); + } + + $selection.addClass(selectionCssClass); + + return $selection; + }; + + return SelectionCSS; +}); + +S2.define('select2/selection/eventRelay',[ + 'jquery' +], function ($) { + function EventRelay () { } + + EventRelay.prototype.bind = function (decorated, container, $container) { + var self = this; + var relayEvents = [ + 'open', 'opening', + 'close', 'closing', + 'select', 'selecting', + 'unselect', 'unselecting', + 'clear', 'clearing' + ]; + + var preventableEvents = [ + 'opening', 'closing', 'selecting', 'unselecting', 'clearing' + ]; + + decorated.call(this, container, $container); + + container.on('*', function (name, params) { + // Ignore events that should not be relayed + if (relayEvents.indexOf(name) === -1) { + return; + } + + // The parameters should always be an object + params = params || {}; + + // Generate the jQuery event for the Select2 event + var evt = $.Event('select2:' + name, { + params: params + }); + + self.$element.trigger(evt); + + // Only handle preventable events if it was one + if (preventableEvents.indexOf(name) === -1) { + return; + } + + params.prevented = evt.isDefaultPrevented(); + }); + }; + + return EventRelay; +}); + +S2.define('select2/translation',[ + 'jquery', + 'require' +], function ($, require) { + function Translation (dict) { + this.dict = dict || {}; + } + + Translation.prototype.all = function () { + return this.dict; + }; + + Translation.prototype.get = function (key) { + return this.dict[key]; + }; + + Translation.prototype.extend = function (translation) { + this.dict = $.extend({}, translation.all(), this.dict); + }; + + // Static functions + + Translation._cache = {}; + + Translation.loadPath = function (path) { + if (!(path in Translation._cache)) { + var translations = require(path); + + Translation._cache[path] = translations; + } + + return new Translation(Translation._cache[path]); + }; + + return Translation; +}); + +S2.define('select2/diacritics',[ + +], function () { + var diacritics = { + '\u24B6': 'A', + '\uFF21': 'A', + '\u00C0': 'A', + '\u00C1': 'A', + '\u00C2': 'A', + '\u1EA6': 'A', + '\u1EA4': 'A', + '\u1EAA': 'A', + '\u1EA8': 'A', + '\u00C3': 'A', + '\u0100': 'A', + '\u0102': 'A', + '\u1EB0': 'A', + '\u1EAE': 'A', + '\u1EB4': 'A', + '\u1EB2': 'A', + '\u0226': 'A', + '\u01E0': 'A', + '\u00C4': 'A', + '\u01DE': 'A', + '\u1EA2': 'A', + '\u00C5': 'A', + '\u01FA': 'A', + '\u01CD': 'A', + '\u0200': 'A', + '\u0202': 'A', + '\u1EA0': 'A', + '\u1EAC': 'A', + '\u1EB6': 'A', + '\u1E00': 'A', + '\u0104': 'A', + '\u023A': 'A', + '\u2C6F': 'A', + '\uA732': 'AA', + '\u00C6': 'AE', + '\u01FC': 'AE', + '\u01E2': 'AE', + '\uA734': 'AO', + '\uA736': 'AU', + '\uA738': 'AV', + '\uA73A': 'AV', + '\uA73C': 'AY', + '\u24B7': 'B', + '\uFF22': 'B', + '\u1E02': 'B', + '\u1E04': 'B', + '\u1E06': 'B', + '\u0243': 'B', + '\u0182': 'B', + '\u0181': 'B', + '\u24B8': 'C', + '\uFF23': 'C', + '\u0106': 'C', + '\u0108': 'C', + '\u010A': 'C', + '\u010C': 'C', + '\u00C7': 'C', + '\u1E08': 'C', + '\u0187': 'C', + '\u023B': 'C', + '\uA73E': 'C', + '\u24B9': 'D', + '\uFF24': 'D', + '\u1E0A': 'D', + '\u010E': 'D', + '\u1E0C': 'D', + '\u1E10': 'D', + '\u1E12': 'D', + '\u1E0E': 'D', + '\u0110': 'D', + '\u018B': 'D', + '\u018A': 'D', + '\u0189': 'D', + '\uA779': 'D', + '\u01F1': 'DZ', + '\u01C4': 'DZ', + '\u01F2': 'Dz', + '\u01C5': 'Dz', + '\u24BA': 'E', + '\uFF25': 'E', + '\u00C8': 'E', + '\u00C9': 'E', + '\u00CA': 'E', + '\u1EC0': 'E', + '\u1EBE': 'E', + '\u1EC4': 'E', + '\u1EC2': 'E', + '\u1EBC': 'E', + '\u0112': 'E', + '\u1E14': 'E', + '\u1E16': 'E', + '\u0114': 'E', + '\u0116': 'E', + '\u00CB': 'E', + '\u1EBA': 'E', + '\u011A': 'E', + '\u0204': 'E', + '\u0206': 'E', + '\u1EB8': 'E', + '\u1EC6': 'E', + '\u0228': 'E', + '\u1E1C': 'E', + '\u0118': 'E', + '\u1E18': 'E', + '\u1E1A': 'E', + '\u0190': 'E', + '\u018E': 'E', + '\u24BB': 'F', + '\uFF26': 'F', + '\u1E1E': 'F', + '\u0191': 'F', + '\uA77B': 'F', + '\u24BC': 'G', + '\uFF27': 'G', + '\u01F4': 'G', + '\u011C': 'G', + '\u1E20': 'G', + '\u011E': 'G', + '\u0120': 'G', + '\u01E6': 'G', + '\u0122': 'G', + '\u01E4': 'G', + '\u0193': 'G', + '\uA7A0': 'G', + '\uA77D': 'G', + '\uA77E': 'G', + '\u24BD': 'H', + '\uFF28': 'H', + '\u0124': 'H', + '\u1E22': 'H', + '\u1E26': 'H', + '\u021E': 'H', + '\u1E24': 'H', + '\u1E28': 'H', + '\u1E2A': 'H', + '\u0126': 'H', + '\u2C67': 'H', + '\u2C75': 'H', + '\uA78D': 'H', + '\u24BE': 'I', + '\uFF29': 'I', + '\u00CC': 'I', + '\u00CD': 'I', + '\u00CE': 'I', + '\u0128': 'I', + '\u012A': 'I', + '\u012C': 'I', + '\u0130': 'I', + '\u00CF': 'I', + '\u1E2E': 'I', + '\u1EC8': 'I', + '\u01CF': 'I', + '\u0208': 'I', + '\u020A': 'I', + '\u1ECA': 'I', + '\u012E': 'I', + '\u1E2C': 'I', + '\u0197': 'I', + '\u24BF': 'J', + '\uFF2A': 'J', + '\u0134': 'J', + '\u0248': 'J', + '\u24C0': 'K', + '\uFF2B': 'K', + '\u1E30': 'K', + '\u01E8': 'K', + '\u1E32': 'K', + '\u0136': 'K', + '\u1E34': 'K', + '\u0198': 'K', + '\u2C69': 'K', + '\uA740': 'K', + '\uA742': 'K', + '\uA744': 'K', + '\uA7A2': 'K', + '\u24C1': 'L', + '\uFF2C': 'L', + '\u013F': 'L', + '\u0139': 'L', + '\u013D': 'L', + '\u1E36': 'L', + '\u1E38': 'L', + '\u013B': 'L', + '\u1E3C': 'L', + '\u1E3A': 'L', + '\u0141': 'L', + '\u023D': 'L', + '\u2C62': 'L', + '\u2C60': 'L', + '\uA748': 'L', + '\uA746': 'L', + '\uA780': 'L', + '\u01C7': 'LJ', + '\u01C8': 'Lj', + '\u24C2': 'M', + '\uFF2D': 'M', + '\u1E3E': 'M', + '\u1E40': 'M', + '\u1E42': 'M', + '\u2C6E': 'M', + '\u019C': 'M', + '\u24C3': 'N', + '\uFF2E': 'N', + '\u01F8': 'N', + '\u0143': 'N', + '\u00D1': 'N', + '\u1E44': 'N', + '\u0147': 'N', + '\u1E46': 'N', + '\u0145': 'N', + '\u1E4A': 'N', + '\u1E48': 'N', + '\u0220': 'N', + '\u019D': 'N', + '\uA790': 'N', + '\uA7A4': 'N', + '\u01CA': 'NJ', + '\u01CB': 'Nj', + '\u24C4': 'O', + '\uFF2F': 'O', + '\u00D2': 'O', + '\u00D3': 'O', + '\u00D4': 'O', + '\u1ED2': 'O', + '\u1ED0': 'O', + '\u1ED6': 'O', + '\u1ED4': 'O', + '\u00D5': 'O', + '\u1E4C': 'O', + '\u022C': 'O', + '\u1E4E': 'O', + '\u014C': 'O', + '\u1E50': 'O', + '\u1E52': 'O', + '\u014E': 'O', + '\u022E': 'O', + '\u0230': 'O', + '\u00D6': 'O', + '\u022A': 'O', + '\u1ECE': 'O', + '\u0150': 'O', + '\u01D1': 'O', + '\u020C': 'O', + '\u020E': 'O', + '\u01A0': 'O', + '\u1EDC': 'O', + '\u1EDA': 'O', + '\u1EE0': 'O', + '\u1EDE': 'O', + '\u1EE2': 'O', + '\u1ECC': 'O', + '\u1ED8': 'O', + '\u01EA': 'O', + '\u01EC': 'O', + '\u00D8': 'O', + '\u01FE': 'O', + '\u0186': 'O', + '\u019F': 'O', + '\uA74A': 'O', + '\uA74C': 'O', + '\u0152': 'OE', + '\u01A2': 'OI', + '\uA74E': 'OO', + '\u0222': 'OU', + '\u24C5': 'P', + '\uFF30': 'P', + '\u1E54': 'P', + '\u1E56': 'P', + '\u01A4': 'P', + '\u2C63': 'P', + '\uA750': 'P', + '\uA752': 'P', + '\uA754': 'P', + '\u24C6': 'Q', + '\uFF31': 'Q', + '\uA756': 'Q', + '\uA758': 'Q', + '\u024A': 'Q', + '\u24C7': 'R', + '\uFF32': 'R', + '\u0154': 'R', + '\u1E58': 'R', + '\u0158': 'R', + '\u0210': 'R', + '\u0212': 'R', + '\u1E5A': 'R', + '\u1E5C': 'R', + '\u0156': 'R', + '\u1E5E': 'R', + '\u024C': 'R', + '\u2C64': 'R', + '\uA75A': 'R', + '\uA7A6': 'R', + '\uA782': 'R', + '\u24C8': 'S', + '\uFF33': 'S', + '\u1E9E': 'S', + '\u015A': 'S', + '\u1E64': 'S', + '\u015C': 'S', + '\u1E60': 'S', + '\u0160': 'S', + '\u1E66': 'S', + '\u1E62': 'S', + '\u1E68': 'S', + '\u0218': 'S', + '\u015E': 'S', + '\u2C7E': 'S', + '\uA7A8': 'S', + '\uA784': 'S', + '\u24C9': 'T', + '\uFF34': 'T', + '\u1E6A': 'T', + '\u0164': 'T', + '\u1E6C': 'T', + '\u021A': 'T', + '\u0162': 'T', + '\u1E70': 'T', + '\u1E6E': 'T', + '\u0166': 'T', + '\u01AC': 'T', + '\u01AE': 'T', + '\u023E': 'T', + '\uA786': 'T', + '\uA728': 'TZ', + '\u24CA': 'U', + '\uFF35': 'U', + '\u00D9': 'U', + '\u00DA': 'U', + '\u00DB': 'U', + '\u0168': 'U', + '\u1E78': 'U', + '\u016A': 'U', + '\u1E7A': 'U', + '\u016C': 'U', + '\u00DC': 'U', + '\u01DB': 'U', + '\u01D7': 'U', + '\u01D5': 'U', + '\u01D9': 'U', + '\u1EE6': 'U', + '\u016E': 'U', + '\u0170': 'U', + '\u01D3': 'U', + '\u0214': 'U', + '\u0216': 'U', + '\u01AF': 'U', + '\u1EEA': 'U', + '\u1EE8': 'U', + '\u1EEE': 'U', + '\u1EEC': 'U', + '\u1EF0': 'U', + '\u1EE4': 'U', + '\u1E72': 'U', + '\u0172': 'U', + '\u1E76': 'U', + '\u1E74': 'U', + '\u0244': 'U', + '\u24CB': 'V', + '\uFF36': 'V', + '\u1E7C': 'V', + '\u1E7E': 'V', + '\u01B2': 'V', + '\uA75E': 'V', + '\u0245': 'V', + '\uA760': 'VY', + '\u24CC': 'W', + '\uFF37': 'W', + '\u1E80': 'W', + '\u1E82': 'W', + '\u0174': 'W', + '\u1E86': 'W', + '\u1E84': 'W', + '\u1E88': 'W', + '\u2C72': 'W', + '\u24CD': 'X', + '\uFF38': 'X', + '\u1E8A': 'X', + '\u1E8C': 'X', + '\u24CE': 'Y', + '\uFF39': 'Y', + '\u1EF2': 'Y', + '\u00DD': 'Y', + '\u0176': 'Y', + '\u1EF8': 'Y', + '\u0232': 'Y', + '\u1E8E': 'Y', + '\u0178': 'Y', + '\u1EF6': 'Y', + '\u1EF4': 'Y', + '\u01B3': 'Y', + '\u024E': 'Y', + '\u1EFE': 'Y', + '\u24CF': 'Z', + '\uFF3A': 'Z', + '\u0179': 'Z', + '\u1E90': 'Z', + '\u017B': 'Z', + '\u017D': 'Z', + '\u1E92': 'Z', + '\u1E94': 'Z', + '\u01B5': 'Z', + '\u0224': 'Z', + '\u2C7F': 'Z', + '\u2C6B': 'Z', + '\uA762': 'Z', + '\u24D0': 'a', + '\uFF41': 'a', + '\u1E9A': 'a', + '\u00E0': 'a', + '\u00E1': 'a', + '\u00E2': 'a', + '\u1EA7': 'a', + '\u1EA5': 'a', + '\u1EAB': 'a', + '\u1EA9': 'a', + '\u00E3': 'a', + '\u0101': 'a', + '\u0103': 'a', + '\u1EB1': 'a', + '\u1EAF': 'a', + '\u1EB5': 'a', + '\u1EB3': 'a', + '\u0227': 'a', + '\u01E1': 'a', + '\u00E4': 'a', + '\u01DF': 'a', + '\u1EA3': 'a', + '\u00E5': 'a', + '\u01FB': 'a', + '\u01CE': 'a', + '\u0201': 'a', + '\u0203': 'a', + '\u1EA1': 'a', + '\u1EAD': 'a', + '\u1EB7': 'a', + '\u1E01': 'a', + '\u0105': 'a', + '\u2C65': 'a', + '\u0250': 'a', + '\uA733': 'aa', + '\u00E6': 'ae', + '\u01FD': 'ae', + '\u01E3': 'ae', + '\uA735': 'ao', + '\uA737': 'au', + '\uA739': 'av', + '\uA73B': 'av', + '\uA73D': 'ay', + '\u24D1': 'b', + '\uFF42': 'b', + '\u1E03': 'b', + '\u1E05': 'b', + '\u1E07': 'b', + '\u0180': 'b', + '\u0183': 'b', + '\u0253': 'b', + '\u24D2': 'c', + '\uFF43': 'c', + '\u0107': 'c', + '\u0109': 'c', + '\u010B': 'c', + '\u010D': 'c', + '\u00E7': 'c', + '\u1E09': 'c', + '\u0188': 'c', + '\u023C': 'c', + '\uA73F': 'c', + '\u2184': 'c', + '\u24D3': 'd', + '\uFF44': 'd', + '\u1E0B': 'd', + '\u010F': 'd', + '\u1E0D': 'd', + '\u1E11': 'd', + '\u1E13': 'd', + '\u1E0F': 'd', + '\u0111': 'd', + '\u018C': 'd', + '\u0256': 'd', + '\u0257': 'd', + '\uA77A': 'd', + '\u01F3': 'dz', + '\u01C6': 'dz', + '\u24D4': 'e', + '\uFF45': 'e', + '\u00E8': 'e', + '\u00E9': 'e', + '\u00EA': 'e', + '\u1EC1': 'e', + '\u1EBF': 'e', + '\u1EC5': 'e', + '\u1EC3': 'e', + '\u1EBD': 'e', + '\u0113': 'e', + '\u1E15': 'e', + '\u1E17': 'e', + '\u0115': 'e', + '\u0117': 'e', + '\u00EB': 'e', + '\u1EBB': 'e', + '\u011B': 'e', + '\u0205': 'e', + '\u0207': 'e', + '\u1EB9': 'e', + '\u1EC7': 'e', + '\u0229': 'e', + '\u1E1D': 'e', + '\u0119': 'e', + '\u1E19': 'e', + '\u1E1B': 'e', + '\u0247': 'e', + '\u025B': 'e', + '\u01DD': 'e', + '\u24D5': 'f', + '\uFF46': 'f', + '\u1E1F': 'f', + '\u0192': 'f', + '\uA77C': 'f', + '\u24D6': 'g', + '\uFF47': 'g', + '\u01F5': 'g', + '\u011D': 'g', + '\u1E21': 'g', + '\u011F': 'g', + '\u0121': 'g', + '\u01E7': 'g', + '\u0123': 'g', + '\u01E5': 'g', + '\u0260': 'g', + '\uA7A1': 'g', + '\u1D79': 'g', + '\uA77F': 'g', + '\u24D7': 'h', + '\uFF48': 'h', + '\u0125': 'h', + '\u1E23': 'h', + '\u1E27': 'h', + '\u021F': 'h', + '\u1E25': 'h', + '\u1E29': 'h', + '\u1E2B': 'h', + '\u1E96': 'h', + '\u0127': 'h', + '\u2C68': 'h', + '\u2C76': 'h', + '\u0265': 'h', + '\u0195': 'hv', + '\u24D8': 'i', + '\uFF49': 'i', + '\u00EC': 'i', + '\u00ED': 'i', + '\u00EE': 'i', + '\u0129': 'i', + '\u012B': 'i', + '\u012D': 'i', + '\u00EF': 'i', + '\u1E2F': 'i', + '\u1EC9': 'i', + '\u01D0': 'i', + '\u0209': 'i', + '\u020B': 'i', + '\u1ECB': 'i', + '\u012F': 'i', + '\u1E2D': 'i', + '\u0268': 'i', + '\u0131': 'i', + '\u24D9': 'j', + '\uFF4A': 'j', + '\u0135': 'j', + '\u01F0': 'j', + '\u0249': 'j', + '\u24DA': 'k', + '\uFF4B': 'k', + '\u1E31': 'k', + '\u01E9': 'k', + '\u1E33': 'k', + '\u0137': 'k', + '\u1E35': 'k', + '\u0199': 'k', + '\u2C6A': 'k', + '\uA741': 'k', + '\uA743': 'k', + '\uA745': 'k', + '\uA7A3': 'k', + '\u24DB': 'l', + '\uFF4C': 'l', + '\u0140': 'l', + '\u013A': 'l', + '\u013E': 'l', + '\u1E37': 'l', + '\u1E39': 'l', + '\u013C': 'l', + '\u1E3D': 'l', + '\u1E3B': 'l', + '\u017F': 'l', + '\u0142': 'l', + '\u019A': 'l', + '\u026B': 'l', + '\u2C61': 'l', + '\uA749': 'l', + '\uA781': 'l', + '\uA747': 'l', + '\u01C9': 'lj', + '\u24DC': 'm', + '\uFF4D': 'm', + '\u1E3F': 'm', + '\u1E41': 'm', + '\u1E43': 'm', + '\u0271': 'm', + '\u026F': 'm', + '\u24DD': 'n', + '\uFF4E': 'n', + '\u01F9': 'n', + '\u0144': 'n', + '\u00F1': 'n', + '\u1E45': 'n', + '\u0148': 'n', + '\u1E47': 'n', + '\u0146': 'n', + '\u1E4B': 'n', + '\u1E49': 'n', + '\u019E': 'n', + '\u0272': 'n', + '\u0149': 'n', + '\uA791': 'n', + '\uA7A5': 'n', + '\u01CC': 'nj', + '\u24DE': 'o', + '\uFF4F': 'o', + '\u00F2': 'o', + '\u00F3': 'o', + '\u00F4': 'o', + '\u1ED3': 'o', + '\u1ED1': 'o', + '\u1ED7': 'o', + '\u1ED5': 'o', + '\u00F5': 'o', + '\u1E4D': 'o', + '\u022D': 'o', + '\u1E4F': 'o', + '\u014D': 'o', + '\u1E51': 'o', + '\u1E53': 'o', + '\u014F': 'o', + '\u022F': 'o', + '\u0231': 'o', + '\u00F6': 'o', + '\u022B': 'o', + '\u1ECF': 'o', + '\u0151': 'o', + '\u01D2': 'o', + '\u020D': 'o', + '\u020F': 'o', + '\u01A1': 'o', + '\u1EDD': 'o', + '\u1EDB': 'o', + '\u1EE1': 'o', + '\u1EDF': 'o', + '\u1EE3': 'o', + '\u1ECD': 'o', + '\u1ED9': 'o', + '\u01EB': 'o', + '\u01ED': 'o', + '\u00F8': 'o', + '\u01FF': 'o', + '\u0254': 'o', + '\uA74B': 'o', + '\uA74D': 'o', + '\u0275': 'o', + '\u0153': 'oe', + '\u01A3': 'oi', + '\u0223': 'ou', + '\uA74F': 'oo', + '\u24DF': 'p', + '\uFF50': 'p', + '\u1E55': 'p', + '\u1E57': 'p', + '\u01A5': 'p', + '\u1D7D': 'p', + '\uA751': 'p', + '\uA753': 'p', + '\uA755': 'p', + '\u24E0': 'q', + '\uFF51': 'q', + '\u024B': 'q', + '\uA757': 'q', + '\uA759': 'q', + '\u24E1': 'r', + '\uFF52': 'r', + '\u0155': 'r', + '\u1E59': 'r', + '\u0159': 'r', + '\u0211': 'r', + '\u0213': 'r', + '\u1E5B': 'r', + '\u1E5D': 'r', + '\u0157': 'r', + '\u1E5F': 'r', + '\u024D': 'r', + '\u027D': 'r', + '\uA75B': 'r', + '\uA7A7': 'r', + '\uA783': 'r', + '\u24E2': 's', + '\uFF53': 's', + '\u00DF': 's', + '\u015B': 's', + '\u1E65': 's', + '\u015D': 's', + '\u1E61': 's', + '\u0161': 's', + '\u1E67': 's', + '\u1E63': 's', + '\u1E69': 's', + '\u0219': 's', + '\u015F': 's', + '\u023F': 's', + '\uA7A9': 's', + '\uA785': 's', + '\u1E9B': 's', + '\u24E3': 't', + '\uFF54': 't', + '\u1E6B': 't', + '\u1E97': 't', + '\u0165': 't', + '\u1E6D': 't', + '\u021B': 't', + '\u0163': 't', + '\u1E71': 't', + '\u1E6F': 't', + '\u0167': 't', + '\u01AD': 't', + '\u0288': 't', + '\u2C66': 't', + '\uA787': 't', + '\uA729': 'tz', + '\u24E4': 'u', + '\uFF55': 'u', + '\u00F9': 'u', + '\u00FA': 'u', + '\u00FB': 'u', + '\u0169': 'u', + '\u1E79': 'u', + '\u016B': 'u', + '\u1E7B': 'u', + '\u016D': 'u', + '\u00FC': 'u', + '\u01DC': 'u', + '\u01D8': 'u', + '\u01D6': 'u', + '\u01DA': 'u', + '\u1EE7': 'u', + '\u016F': 'u', + '\u0171': 'u', + '\u01D4': 'u', + '\u0215': 'u', + '\u0217': 'u', + '\u01B0': 'u', + '\u1EEB': 'u', + '\u1EE9': 'u', + '\u1EEF': 'u', + '\u1EED': 'u', + '\u1EF1': 'u', + '\u1EE5': 'u', + '\u1E73': 'u', + '\u0173': 'u', + '\u1E77': 'u', + '\u1E75': 'u', + '\u0289': 'u', + '\u24E5': 'v', + '\uFF56': 'v', + '\u1E7D': 'v', + '\u1E7F': 'v', + '\u028B': 'v', + '\uA75F': 'v', + '\u028C': 'v', + '\uA761': 'vy', + '\u24E6': 'w', + '\uFF57': 'w', + '\u1E81': 'w', + '\u1E83': 'w', + '\u0175': 'w', + '\u1E87': 'w', + '\u1E85': 'w', + '\u1E98': 'w', + '\u1E89': 'w', + '\u2C73': 'w', + '\u24E7': 'x', + '\uFF58': 'x', + '\u1E8B': 'x', + '\u1E8D': 'x', + '\u24E8': 'y', + '\uFF59': 'y', + '\u1EF3': 'y', + '\u00FD': 'y', + '\u0177': 'y', + '\u1EF9': 'y', + '\u0233': 'y', + '\u1E8F': 'y', + '\u00FF': 'y', + '\u1EF7': 'y', + '\u1E99': 'y', + '\u1EF5': 'y', + '\u01B4': 'y', + '\u024F': 'y', + '\u1EFF': 'y', + '\u24E9': 'z', + '\uFF5A': 'z', + '\u017A': 'z', + '\u1E91': 'z', + '\u017C': 'z', + '\u017E': 'z', + '\u1E93': 'z', + '\u1E95': 'z', + '\u01B6': 'z', + '\u0225': 'z', + '\u0240': 'z', + '\u2C6C': 'z', + '\uA763': 'z', + '\u0386': '\u0391', + '\u0388': '\u0395', + '\u0389': '\u0397', + '\u038A': '\u0399', + '\u03AA': '\u0399', + '\u038C': '\u039F', + '\u038E': '\u03A5', + '\u03AB': '\u03A5', + '\u038F': '\u03A9', + '\u03AC': '\u03B1', + '\u03AD': '\u03B5', + '\u03AE': '\u03B7', + '\u03AF': '\u03B9', + '\u03CA': '\u03B9', + '\u0390': '\u03B9', + '\u03CC': '\u03BF', + '\u03CD': '\u03C5', + '\u03CB': '\u03C5', + '\u03B0': '\u03C5', + '\u03CE': '\u03C9', + '\u03C2': '\u03C3', + '\u2019': '\'' + }; + + return diacritics; +}); + +S2.define('select2/data/base',[ + '../utils' +], function (Utils) { + function BaseAdapter ($element, options) { + BaseAdapter.__super__.constructor.call(this); + } + + Utils.Extend(BaseAdapter, Utils.Observable); + + BaseAdapter.prototype.current = function (callback) { + throw new Error('The `current` method must be defined in child classes.'); + }; + + BaseAdapter.prototype.query = function (params, callback) { + throw new Error('The `query` method must be defined in child classes.'); + }; + + BaseAdapter.prototype.bind = function (container, $container) { + // Can be implemented in subclasses + }; + + BaseAdapter.prototype.destroy = function () { + // Can be implemented in subclasses + }; + + BaseAdapter.prototype.generateResultId = function (container, data) { + var id = container.id + '-result-'; + + id += Utils.generateChars(4); + + if (data.id != null) { + id += '-' + data.id.toString(); + } else { + id += '-' + Utils.generateChars(4); + } + return id; + }; + + return BaseAdapter; +}); + +S2.define('select2/data/select',[ + './base', + '../utils', + 'jquery' +], function (BaseAdapter, Utils, $) { + function SelectAdapter ($element, options) { + this.$element = $element; + this.options = options; + + SelectAdapter.__super__.constructor.call(this); + } + + Utils.Extend(SelectAdapter, BaseAdapter); + + SelectAdapter.prototype.current = function (callback) { + var self = this; + + var data = Array.prototype.map.call( + this.$element[0].querySelectorAll(':checked'), + function (selectedElement) { + return self.item($(selectedElement)); + } + ); + + callback(data); + }; + + SelectAdapter.prototype.select = function (data) { + var self = this; + + data.selected = true; + + // If data.element is a DOM node, use it instead + if ( + data.element != null && data.element.tagName.toLowerCase() === 'option' + ) { + data.element.selected = true; + + this.$element.trigger('input').trigger('change'); + + return; + } + + if (this.$element.prop('multiple')) { + this.current(function (currentData) { + var val = []; + + data = [data]; + data.push.apply(data, currentData); + + for (var d = 0; d < data.length; d++) { + var id = data[d].id; + + if (val.indexOf(id) === -1) { + val.push(id); + } + } + + self.$element.val(val); + self.$element.trigger('input').trigger('change'); + }); + } else { + var val = data.id; + + this.$element.val(val); + this.$element.trigger('input').trigger('change'); + } + }; + + SelectAdapter.prototype.unselect = function (data) { + var self = this; + + if (!this.$element.prop('multiple')) { + return; + } + + data.selected = false; + + if ( + data.element != null && + data.element.tagName.toLowerCase() === 'option' + ) { + data.element.selected = false; + + this.$element.trigger('input').trigger('change'); + + return; + } + + this.current(function (currentData) { + var val = []; + + for (var d = 0; d < currentData.length; d++) { + var id = currentData[d].id; + + if (id !== data.id && val.indexOf(id) === -1) { + val.push(id); + } + } + + self.$element.val(val); + + self.$element.trigger('input').trigger('change'); + }); + }; + + SelectAdapter.prototype.bind = function (container, $container) { + var self = this; + + this.container = container; + + container.on('select', function (params) { + self.select(params.data); + }); + + container.on('unselect', function (params) { + self.unselect(params.data); + }); + }; + + SelectAdapter.prototype.destroy = function () { + // Remove anything added to child elements + this.$element.find('*').each(function () { + // Remove any custom data set by Select2 + Utils.RemoveData(this); + }); + }; + + SelectAdapter.prototype.query = function (params, callback) { + var data = []; + var self = this; + + var $options = this.$element.children(); + + $options.each(function () { + if ( + this.tagName.toLowerCase() !== 'option' && + this.tagName.toLowerCase() !== 'optgroup' + ) { + return; + } + + var $option = $(this); + + var option = self.item($option); + + var matches = self.matches(params, option); + + if (matches !== null) { + data.push(matches); + } + }); + + callback({ + results: data + }); + }; + + SelectAdapter.prototype.addOptions = function ($options) { + this.$element.append($options); + }; + + SelectAdapter.prototype.option = function (data) { + var option; + + if (data.children) { + option = document.createElement('optgroup'); + option.label = data.text; + } else { + option = document.createElement('option'); + + if (option.textContent !== undefined) { + option.textContent = data.text; + } else { + option.innerText = data.text; + } + } + + if (data.id !== undefined) { + option.value = data.id; + } + + if (data.disabled) { + option.disabled = true; + } + + if (data.selected) { + option.selected = true; + } + + if (data.title) { + option.title = data.title; + } + + var normalizedData = this._normalizeItem(data); + normalizedData.element = option; + + // Override the option's data with the combined data + Utils.StoreData(option, 'data', normalizedData); + + return $(option); + }; + + SelectAdapter.prototype.item = function ($option) { + var data = {}; + + data = Utils.GetData($option[0], 'data'); + + if (data != null) { + return data; + } + + var option = $option[0]; + + if (option.tagName.toLowerCase() === 'option') { + data = { + id: $option.val(), + text: $option.text(), + disabled: $option.prop('disabled'), + selected: $option.prop('selected'), + title: $option.prop('title') + }; + } else if (option.tagName.toLowerCase() === 'optgroup') { + data = { + text: $option.prop('label'), + children: [], + title: $option.prop('title') + }; + + var $children = $option.children('option'); + var children = []; + + for (var c = 0; c < $children.length; c++) { + var $child = $($children[c]); + + var child = this.item($child); + + children.push(child); + } + + data.children = children; + } + + data = this._normalizeItem(data); + data.element = $option[0]; + + Utils.StoreData($option[0], 'data', data); + + return data; + }; + + SelectAdapter.prototype._normalizeItem = function (item) { + if (item !== Object(item)) { + item = { + id: item, + text: item + }; + } + + item = $.extend({}, { + text: '' + }, item); + + var defaults = { + selected: false, + disabled: false + }; + + if (item.id != null) { + item.id = item.id.toString(); + } + + if (item.text != null) { + item.text = item.text.toString(); + } + + if (item._resultId == null && item.id && this.container != null) { + item._resultId = this.generateResultId(this.container, item); + } + + return $.extend({}, defaults, item); + }; + + SelectAdapter.prototype.matches = function (params, data) { + var matcher = this.options.get('matcher'); + + return matcher(params, data); + }; + + return SelectAdapter; +}); + +S2.define('select2/data/array',[ + './select', + '../utils', + 'jquery' +], function (SelectAdapter, Utils, $) { + function ArrayAdapter ($element, options) { + this._dataToConvert = options.get('data') || []; + + ArrayAdapter.__super__.constructor.call(this, $element, options); + } + + Utils.Extend(ArrayAdapter, SelectAdapter); + + ArrayAdapter.prototype.bind = function (container, $container) { + ArrayAdapter.__super__.bind.call(this, container, $container); + + this.addOptions(this.convertToOptions(this._dataToConvert)); + }; + + ArrayAdapter.prototype.select = function (data) { + var $option = this.$element.find('option').filter(function (i, elm) { + return elm.value == data.id.toString(); + }); + + if ($option.length === 0) { + $option = this.option(data); + + this.addOptions($option); + } + + ArrayAdapter.__super__.select.call(this, data); + }; + + ArrayAdapter.prototype.convertToOptions = function (data) { + var self = this; + + var $existing = this.$element.find('option'); + var existingIds = $existing.map(function () { + return self.item($(this)).id; + }).get(); + + var $options = []; + + // Filter out all items except for the one passed in the argument + function onlyItem (item) { + return function () { + return $(this).val() == item.id; + }; + } + + for (var d = 0; d < data.length; d++) { + var item = this._normalizeItem(data[d]); + + // Skip items which were pre-loaded, only merge the data + if (existingIds.indexOf(item.id) >= 0) { + var $existingOption = $existing.filter(onlyItem(item)); + + var existingData = this.item($existingOption); + var newData = $.extend(true, {}, item, existingData); + + var $newOption = this.option(newData); + + $existingOption.replaceWith($newOption); + + continue; + } + + var $option = this.option(item); + + if (item.children) { + var $children = this.convertToOptions(item.children); + + $option.append($children); + } + + $options.push($option); + } + + return $options; + }; + + return ArrayAdapter; +}); + +S2.define('select2/data/ajax',[ + './array', + '../utils', + 'jquery' +], function (ArrayAdapter, Utils, $) { + function AjaxAdapter ($element, options) { + this.ajaxOptions = this._applyDefaults(options.get('ajax')); + + if (this.ajaxOptions.processResults != null) { + this.processResults = this.ajaxOptions.processResults; + } + + AjaxAdapter.__super__.constructor.call(this, $element, options); + } + + Utils.Extend(AjaxAdapter, ArrayAdapter); + + AjaxAdapter.prototype._applyDefaults = function (options) { + var defaults = { + data: function (params) { + return $.extend({}, params, { + q: params.term + }); + }, + transport: function (params, success, failure) { + var $request = $.ajax(params); + + $request.then(success); + $request.fail(failure); + + return $request; + } + }; + + return $.extend({}, defaults, options, true); + }; + + AjaxAdapter.prototype.processResults = function (results) { + return results; + }; + + AjaxAdapter.prototype.query = function (params, callback) { + var matches = []; + var self = this; + + if (this._request != null) { + // JSONP requests cannot always be aborted + if (typeof this._request.abort === 'function') { + this._request.abort(); + } + + this._request = null; + } + + var options = $.extend({ + type: 'GET' + }, this.ajaxOptions); + + if (typeof options.url === 'function') { + options.url = options.url.call(this.$element, params); + } + + if (typeof options.data === 'function') { + options.data = options.data.call(this.$element, params); + } + + function request () { + var $request = options.transport(options, function (data) { + var results = self.processResults(data, params); + + if (self.options.get('debug') && window.console && console.error) { + // Check to make sure that the response included a `results` key. + if (!results || !results.results || !Array.isArray(results.results)) { + console.error( + 'Select2: The AJAX results did not return an array in the ' + + '`results` key of the response.' + ); + } + } + + callback(results); + }, function () { + // Attempt to detect if a request was aborted + // Only works if the transport exposes a status property + if ('status' in $request && + ($request.status === 0 || $request.status === '0')) { + return; + } + + self.trigger('results:message', { + message: 'errorLoading' + }); + }); + + self._request = $request; + } + + if (this.ajaxOptions.delay && params.term != null) { + if (this._queryTimeout) { + window.clearTimeout(this._queryTimeout); + } + + this._queryTimeout = window.setTimeout(request, this.ajaxOptions.delay); + } else { + request(); + } + }; + + return AjaxAdapter; +}); + +S2.define('select2/data/tags',[ + 'jquery' +], function ($) { + function Tags (decorated, $element, options) { + var tags = options.get('tags'); + + var createTag = options.get('createTag'); + + if (createTag !== undefined) { + this.createTag = createTag; + } + + var insertTag = options.get('insertTag'); + + if (insertTag !== undefined) { + this.insertTag = insertTag; + } + + decorated.call(this, $element, options); + + if (Array.isArray(tags)) { + for (var t = 0; t < tags.length; t++) { + var tag = tags[t]; + var item = this._normalizeItem(tag); + + var $option = this.option(item); + + this.$element.append($option); + } + } + } + + Tags.prototype.query = function (decorated, params, callback) { + var self = this; + + this._removeOldTags(); + + if (params.term == null || params.page != null) { + decorated.call(this, params, callback); + return; + } + + function wrapper (obj, child) { + var data = obj.results; + + for (var i = 0; i < data.length; i++) { + var option = data[i]; + + var checkChildren = ( + option.children != null && + !wrapper({ + results: option.children + }, true) + ); + + var optionText = (option.text || '').toUpperCase(); + var paramsTerm = (params.term || '').toUpperCase(); + + var checkText = optionText === paramsTerm; + + if (checkText || checkChildren) { + if (child) { + return false; + } + + obj.data = data; + callback(obj); + + return; + } + } + + if (child) { + return true; + } + + var tag = self.createTag(params); + + if (tag != null) { + var $option = self.option(tag); + $option.attr('data-select2-tag', 'true'); + + self.addOptions([$option]); + + self.insertTag(data, tag); + } + + obj.results = data; + + callback(obj); + } + + decorated.call(this, params, wrapper); + }; + + Tags.prototype.createTag = function (decorated, params) { + if (params.term == null) { + return null; + } + + var term = params.term.trim(); + + if (term === '') { + return null; + } + + return { + id: term, + text: term + }; + }; + + Tags.prototype.insertTag = function (_, data, tag) { + data.unshift(tag); + }; + + Tags.prototype._removeOldTags = function (_) { + var $options = this.$element.find('option[data-select2-tag]'); + + $options.each(function () { + if (this.selected) { + return; + } + + $(this).remove(); + }); + }; + + return Tags; +}); + +S2.define('select2/data/tokenizer',[ + 'jquery' +], function ($) { + function Tokenizer (decorated, $element, options) { + var tokenizer = options.get('tokenizer'); + + if (tokenizer !== undefined) { + this.tokenizer = tokenizer; + } + + decorated.call(this, $element, options); + } + + Tokenizer.prototype.bind = function (decorated, container, $container) { + decorated.call(this, container, $container); + + this.$search = container.dropdown.$search || container.selection.$search || + $container.find('.select2-search__field'); + }; + + Tokenizer.prototype.query = function (decorated, params, callback) { + var self = this; + + function createAndSelect (data) { + // Normalize the data object so we can use it for checks + var item = self._normalizeItem(data); + + // Check if the data object already exists as a tag + // Select it if it doesn't + var $existingOptions = self.$element.find('option').filter(function () { + return $(this).val() === item.id; + }); + + // If an existing option wasn't found for it, create the option + if (!$existingOptions.length) { + var $option = self.option(item); + $option.attr('data-select2-tag', true); + + self._removeOldTags(); + self.addOptions([$option]); + } + + // Select the item, now that we know there is an option for it + select(item); + } + + function select (data) { + self.trigger('select', { + data: data + }); + } + + params.term = params.term || ''; + + var tokenData = this.tokenizer(params, this.options, createAndSelect); + + if (tokenData.term !== params.term) { + // Replace the search term if we have the search box + if (this.$search.length) { + this.$search.val(tokenData.term); + this.$search.trigger('focus'); + } + + params.term = tokenData.term; + } + + decorated.call(this, params, callback); + }; + + Tokenizer.prototype.tokenizer = function (_, params, options, callback) { + var separators = options.get('tokenSeparators') || []; + var term = params.term; + var i = 0; + + var createTag = this.createTag || function (params) { + return { + id: params.term, + text: params.term + }; + }; + + while (i < term.length) { + var termChar = term[i]; + + if (separators.indexOf(termChar) === -1) { + i++; + + continue; + } + + var part = term.substr(0, i); + var partParams = $.extend({}, params, { + term: part + }); + + var data = createTag(partParams); + + if (data == null) { + i++; + continue; + } + + callback(data); + + // Reset the term to not include the tokenized portion + term = term.substr(i + 1) || ''; + i = 0; + } + + return { + term: term + }; + }; + + return Tokenizer; +}); + +S2.define('select2/data/minimumInputLength',[ + +], function () { + function MinimumInputLength (decorated, $e, options) { + this.minimumInputLength = options.get('minimumInputLength'); + + decorated.call(this, $e, options); + } + + MinimumInputLength.prototype.query = function (decorated, params, callback) { + params.term = params.term || ''; + + if (params.term.length < this.minimumInputLength) { + this.trigger('results:message', { + message: 'inputTooShort', + args: { + minimum: this.minimumInputLength, + input: params.term, + params: params + } + }); + + return; + } + + decorated.call(this, params, callback); + }; + + return MinimumInputLength; +}); + +S2.define('select2/data/maximumInputLength',[ + +], function () { + function MaximumInputLength (decorated, $e, options) { + this.maximumInputLength = options.get('maximumInputLength'); + + decorated.call(this, $e, options); + } + + MaximumInputLength.prototype.query = function (decorated, params, callback) { + params.term = params.term || ''; + + if (this.maximumInputLength > 0 && + params.term.length > this.maximumInputLength) { + this.trigger('results:message', { + message: 'inputTooLong', + args: { + maximum: this.maximumInputLength, + input: params.term, + params: params + } + }); + + return; + } + + decorated.call(this, params, callback); + }; + + return MaximumInputLength; +}); + +S2.define('select2/data/maximumSelectionLength',[ + +], function (){ + function MaximumSelectionLength (decorated, $e, options) { + this.maximumSelectionLength = options.get('maximumSelectionLength'); + + decorated.call(this, $e, options); + } + + MaximumSelectionLength.prototype.bind = + function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('select', function () { + self._checkIfMaximumSelected(); + }); + }; + + MaximumSelectionLength.prototype.query = + function (decorated, params, callback) { + var self = this; + + this._checkIfMaximumSelected(function () { + decorated.call(self, params, callback); + }); + }; + + MaximumSelectionLength.prototype._checkIfMaximumSelected = + function (_, successCallback) { + var self = this; + + this.current(function (currentData) { + var count = currentData != null ? currentData.length : 0; + if (self.maximumSelectionLength > 0 && + count >= self.maximumSelectionLength) { + self.trigger('results:message', { + message: 'maximumSelected', + args: { + maximum: self.maximumSelectionLength + } + }); + return; + } + + if (successCallback) { + successCallback(); + } + }); + }; + + return MaximumSelectionLength; +}); + +S2.define('select2/dropdown',[ + 'jquery', + './utils' +], function ($, Utils) { + function Dropdown ($element, options) { + this.$element = $element; + this.options = options; + + Dropdown.__super__.constructor.call(this); + } + + Utils.Extend(Dropdown, Utils.Observable); + + Dropdown.prototype.render = function () { + var $dropdown = $( + '' + + '' + + '' + ); + + $dropdown.attr('dir', this.options.get('dir')); + + this.$dropdown = $dropdown; + + return $dropdown; + }; + + Dropdown.prototype.bind = function () { + // Should be implemented in subclasses + }; + + Dropdown.prototype.position = function ($dropdown, $container) { + // Should be implemented in subclasses + }; + + Dropdown.prototype.destroy = function () { + // Remove the dropdown from the DOM + this.$dropdown.remove(); + }; + + return Dropdown; +}); + +S2.define('select2/dropdown/search',[ + 'jquery' +], function ($) { + function Search () { } + + Search.prototype.render = function (decorated) { + var $rendered = decorated.call(this); + var searchLabel = this.options.get('translations').get('search'); + + var $search = $( + '' + + '' + + '' + ); + + this.$searchContainer = $search; + this.$search = $search.find('input'); + + this.$search.prop('autocomplete', this.options.get('autocomplete')); + this.$search.attr('aria-label', searchLabel()); + + $rendered.prepend($search); + + return $rendered; + }; + + Search.prototype.bind = function (decorated, container, $container) { + var self = this; + + var resultsId = container.id + '-results'; + + decorated.call(this, container, $container); + + this.$search.on('keydown', function (evt) { + self.trigger('keypress', evt); + + self._keyUpPrevented = evt.isDefaultPrevented(); + }); + + // Workaround for browsers which do not support the `input` event + // This will prevent double-triggering of events for browsers which support + // both the `keyup` and `input` events. + this.$search.on('input', function (evt) { + // Unbind the duplicated `keyup` event + $(this).off('keyup'); + }); + + this.$search.on('keyup input', function (evt) { + self.handleSearch(evt); + }); + + container.on('open', function () { + self.$search.attr('tabindex', 0); + self.$search.attr('aria-controls', resultsId); + + self.$search.trigger('focus'); + + window.setTimeout(function () { + self.$search.trigger('focus'); + }, 0); + }); + + container.on('close', function () { + self.$search.attr('tabindex', -1); + self.$search.removeAttr('aria-controls'); + self.$search.removeAttr('aria-activedescendant'); + + self.$search.val(''); + self.$search.trigger('blur'); + }); + + container.on('focus', function () { + if (!container.isOpen()) { + self.$search.trigger('focus'); + } + }); + + container.on('results:all', function (params) { + if (params.query.term == null || params.query.term === '') { + var showSearch = self.showSearch(params); + + if (showSearch) { + self.$searchContainer[0].classList.remove('select2-search--hide'); + } else { + self.$searchContainer[0].classList.add('select2-search--hide'); + } + } + }); + + container.on('results:focus', function (params) { + if (params.data._resultId) { + self.$search.attr('aria-activedescendant', params.data._resultId); + } else { + self.$search.removeAttr('aria-activedescendant'); + } + }); + }; + + Search.prototype.handleSearch = function (evt) { + if (!this._keyUpPrevented) { + var input = this.$search.val(); + + this.trigger('query', { + term: input + }); + } + + this._keyUpPrevented = false; + }; + + Search.prototype.showSearch = function (_, params) { + return true; + }; + + return Search; +}); + +S2.define('select2/dropdown/hidePlaceholder',[ + +], function () { + function HidePlaceholder (decorated, $element, options, dataAdapter) { + this.placeholder = this.normalizePlaceholder(options.get('placeholder')); + + decorated.call(this, $element, options, dataAdapter); + } + + HidePlaceholder.prototype.append = function (decorated, data) { + data.results = this.removePlaceholder(data.results); + + decorated.call(this, data); + }; + + HidePlaceholder.prototype.normalizePlaceholder = function (_, placeholder) { + if (typeof placeholder === 'string') { + placeholder = { + id: '', + text: placeholder + }; + } + + return placeholder; + }; + + HidePlaceholder.prototype.removePlaceholder = function (_, data) { + var modifiedData = data.slice(0); + + for (var d = data.length - 1; d >= 0; d--) { + var item = data[d]; + + if (this.placeholder.id === item.id) { + modifiedData.splice(d, 1); + } + } + + return modifiedData; + }; + + return HidePlaceholder; +}); + +S2.define('select2/dropdown/infiniteScroll',[ + 'jquery' +], function ($) { + function InfiniteScroll (decorated, $element, options, dataAdapter) { + this.lastParams = {}; + + decorated.call(this, $element, options, dataAdapter); + + this.$loadingMore = this.createLoadingMore(); + this.loading = false; + } + + InfiniteScroll.prototype.append = function (decorated, data) { + this.$loadingMore.remove(); + this.loading = false; + + decorated.call(this, data); + + if (this.showLoadingMore(data)) { + this.$results.append(this.$loadingMore); + this.loadMoreIfNeeded(); + } + }; + + InfiniteScroll.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('query', function (params) { + self.lastParams = params; + self.loading = true; + }); + + container.on('query:append', function (params) { + self.lastParams = params; + self.loading = true; + }); + + this.$results.on('scroll', this.loadMoreIfNeeded.bind(this)); + }; + + InfiniteScroll.prototype.loadMoreIfNeeded = function () { + var isLoadMoreVisible = $.contains( + document.documentElement, + this.$loadingMore[0] + ); + + if (this.loading || !isLoadMoreVisible) { + return; + } + + var currentOffset = this.$results.offset().top + + this.$results.outerHeight(false); + var loadingMoreOffset = this.$loadingMore.offset().top + + this.$loadingMore.outerHeight(false); + + if (currentOffset + 50 >= loadingMoreOffset) { + this.loadMore(); + } + }; + + InfiniteScroll.prototype.loadMore = function () { + this.loading = true; + + var params = $.extend({}, {page: 1}, this.lastParams); + + params.page++; + + this.trigger('query:append', params); + }; + + InfiniteScroll.prototype.showLoadingMore = function (_, data) { + return data.pagination && data.pagination.more; + }; + + InfiniteScroll.prototype.createLoadingMore = function () { + var $option = $( + '
          • ' + ); + + var message = this.options.get('translations').get('loadingMore'); + + $option.html(message(this.lastParams)); + + return $option; + }; + + return InfiniteScroll; +}); + +S2.define('select2/dropdown/attachBody',[ + 'jquery', + '../utils' +], function ($, Utils) { + function AttachBody (decorated, $element, options) { + this.$dropdownParent = $(options.get('dropdownParent') || document.body); + + decorated.call(this, $element, options); + } + + AttachBody.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('open', function () { + self._showDropdown(); + self._attachPositioningHandler(container); + + // Must bind after the results handlers to ensure correct sizing + self._bindContainerResultHandlers(container); + }); + + container.on('close', function () { + self._hideDropdown(); + self._detachPositioningHandler(container); + }); + + this.$dropdownContainer.on('mousedown', function (evt) { + evt.stopPropagation(); + }); + }; + + AttachBody.prototype.destroy = function (decorated) { + decorated.call(this); + + this.$dropdownContainer.remove(); + }; + + AttachBody.prototype.position = function (decorated, $dropdown, $container) { + // Clone all of the container classes + $dropdown.attr('class', $container.attr('class')); + + $dropdown[0].classList.remove('select2'); + $dropdown[0].classList.add('select2-container--open'); + + $dropdown.css({ + position: 'absolute', + top: -999999 + }); + + this.$container = $container; + }; + + AttachBody.prototype.render = function (decorated) { + var $container = $(''); + + var $dropdown = decorated.call(this); + $container.append($dropdown); + + this.$dropdownContainer = $container; + + return $container; + }; + + AttachBody.prototype._hideDropdown = function (decorated) { + this.$dropdownContainer.detach(); + }; + + AttachBody.prototype._bindContainerResultHandlers = + function (decorated, container) { + + // These should only be bound once + if (this._containerResultsHandlersBound) { + return; + } + + var self = this; + + container.on('results:all', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + + container.on('results:append', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + + container.on('results:message', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + + container.on('select', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + + container.on('unselect', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + + this._containerResultsHandlersBound = true; + }; + + AttachBody.prototype._attachPositioningHandler = + function (decorated, container) { + var self = this; + + var scrollEvent = 'scroll.select2.' + container.id; + var resizeEvent = 'resize.select2.' + container.id; + var orientationEvent = 'orientationchange.select2.' + container.id; + + var $watchers = this.$container.parents().filter(Utils.hasScroll); + $watchers.each(function () { + Utils.StoreData(this, 'select2-scroll-position', { + x: $(this).scrollLeft(), + y: $(this).scrollTop() + }); + }); + + $watchers.on(scrollEvent, function (ev) { + var position = Utils.GetData(this, 'select2-scroll-position'); + $(this).scrollTop(position.y); + }); + + $(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent, + function (e) { + self._positionDropdown(); + self._resizeDropdown(); + }); + }; + + AttachBody.prototype._detachPositioningHandler = + function (decorated, container) { + var scrollEvent = 'scroll.select2.' + container.id; + var resizeEvent = 'resize.select2.' + container.id; + var orientationEvent = 'orientationchange.select2.' + container.id; + + var $watchers = this.$container.parents().filter(Utils.hasScroll); + $watchers.off(scrollEvent); + + $(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent); + }; + + AttachBody.prototype._positionDropdown = function () { + var $window = $(window); + + var isCurrentlyAbove = this.$dropdown[0].classList + .contains('select2-dropdown--above'); + var isCurrentlyBelow = this.$dropdown[0].classList + .contains('select2-dropdown--below'); + + var newDirection = null; + + var offset = this.$container.offset(); + + offset.bottom = offset.top + this.$container.outerHeight(false); + + var container = { + height: this.$container.outerHeight(false) + }; + + container.top = offset.top; + container.bottom = offset.top + container.height; + + var dropdown = { + height: this.$dropdown.outerHeight(false) + }; + + var viewport = { + top: $window.scrollTop(), + bottom: $window.scrollTop() + $window.height() + }; + + var enoughRoomAbove = viewport.top < (offset.top - dropdown.height); + var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height); + + var css = { + left: offset.left, + top: container.bottom + }; + + // Determine what the parent element is to use for calculating the offset + var $offsetParent = this.$dropdownParent; + + // For statically positioned elements, we need to get the element + // that is determining the offset + if ($offsetParent.css('position') === 'static') { + $offsetParent = $offsetParent.offsetParent(); + } + + var parentOffset = { + top: 0, + left: 0 + }; + + if ( + $.contains(document.body, $offsetParent[0]) || + $offsetParent[0].isConnected + ) { + parentOffset = $offsetParent.offset(); + } + + css.top -= parentOffset.top; + css.left -= parentOffset.left; + + if (!isCurrentlyAbove && !isCurrentlyBelow) { + newDirection = 'below'; + } + + if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) { + newDirection = 'above'; + } else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) { + newDirection = 'below'; + } + + if (newDirection == 'above' || + (isCurrentlyAbove && newDirection !== 'below')) { + css.top = container.top - parentOffset.top - dropdown.height; + } + + if (newDirection != null) { + this.$dropdown[0].classList.remove('select2-dropdown--below'); + this.$dropdown[0].classList.remove('select2-dropdown--above'); + this.$dropdown[0].classList.add('select2-dropdown--' + newDirection); + + this.$container[0].classList.remove('select2-container--below'); + this.$container[0].classList.remove('select2-container--above'); + this.$container[0].classList.add('select2-container--' + newDirection); + } + + this.$dropdownContainer.css(css); + }; + + AttachBody.prototype._resizeDropdown = function () { + var css = { + width: this.$container.outerWidth(false) + 'px' + }; + + if (this.options.get('dropdownAutoWidth')) { + css.minWidth = css.width; + css.position = 'relative'; + css.width = 'auto'; + } + + this.$dropdown.css(css); + }; + + AttachBody.prototype._showDropdown = function (decorated) { + this.$dropdownContainer.appendTo(this.$dropdownParent); + + this._positionDropdown(); + this._resizeDropdown(); + }; + + return AttachBody; +}); + +S2.define('select2/dropdown/minimumResultsForSearch',[ + +], function () { + function countResults (data) { + var count = 0; + + for (var d = 0; d < data.length; d++) { + var item = data[d]; + + if (item.children) { + count += countResults(item.children); + } else { + count++; + } + } + + return count; + } + + function MinimumResultsForSearch (decorated, $element, options, dataAdapter) { + this.minimumResultsForSearch = options.get('minimumResultsForSearch'); + + if (this.minimumResultsForSearch < 0) { + this.minimumResultsForSearch = Infinity; + } + + decorated.call(this, $element, options, dataAdapter); + } + + MinimumResultsForSearch.prototype.showSearch = function (decorated, params) { + if (countResults(params.data.results) < this.minimumResultsForSearch) { + return false; + } + + return decorated.call(this, params); + }; + + return MinimumResultsForSearch; +}); + +S2.define('select2/dropdown/selectOnClose',[ + '../utils' +], function (Utils) { + function SelectOnClose () { } + + SelectOnClose.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('close', function (params) { + self._handleSelectOnClose(params); + }); + }; + + SelectOnClose.prototype._handleSelectOnClose = function (_, params) { + if (params && params.originalSelect2Event != null) { + var event = params.originalSelect2Event; + + // Don't select an item if the close event was triggered from a select or + // unselect event + if (event._type === 'select' || event._type === 'unselect') { + return; + } + } + + var $highlightedResults = this.getHighlightedResults(); + + // Only select highlighted results + if ($highlightedResults.length < 1) { + return; + } + + var data = Utils.GetData($highlightedResults[0], 'data'); + + // Don't re-select already selected resulte + if ( + (data.element != null && data.element.selected) || + (data.element == null && data.selected) + ) { + return; + } + + this.trigger('select', { + data: data + }); + }; + + return SelectOnClose; +}); + +S2.define('select2/dropdown/closeOnSelect',[ + +], function () { + function CloseOnSelect () { } + + CloseOnSelect.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('select', function (evt) { + self._selectTriggered(evt); + }); + + container.on('unselect', function (evt) { + self._selectTriggered(evt); + }); + }; + + CloseOnSelect.prototype._selectTriggered = function (_, evt) { + var originalEvent = evt.originalEvent; + + // Don't close if the control key is being held + if (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey)) { + return; + } + + this.trigger('close', { + originalEvent: originalEvent, + originalSelect2Event: evt + }); + }; + + return CloseOnSelect; +}); + +S2.define('select2/dropdown/dropdownCss',[ + '../utils' +], function (Utils) { + function DropdownCSS () { } + + DropdownCSS.prototype.render = function (decorated) { + var $dropdown = decorated.call(this); + + var dropdownCssClass = this.options.get('dropdownCssClass') || ''; + + if (dropdownCssClass.indexOf(':all:') !== -1) { + dropdownCssClass = dropdownCssClass.replace(':all:', ''); + + Utils.copyNonInternalCssClasses($dropdown[0], this.$element[0]); + } + + $dropdown.addClass(dropdownCssClass); + + return $dropdown; + }; + + return DropdownCSS; +}); + +S2.define('select2/dropdown/tagsSearchHighlight',[ + '../utils' +], function (Utils) { + function TagsSearchHighlight () { } + + TagsSearchHighlight.prototype.highlightFirstItem = function (decorated) { + var $options = this.$results + .find( + '.select2-results__option--selectable' + + ':not(.select2-results__option--selected)' + ); + + if ($options.length > 0) { + var $firstOption = $options.first(); + var data = Utils.GetData($firstOption[0], 'data'); + var firstElement = data.element; + + if (firstElement && firstElement.getAttribute) { + if (firstElement.getAttribute('data-select2-tag') === 'true') { + $firstOption.trigger('mouseenter'); + + return; + } + } + } + + decorated.call(this); + }; + + return TagsSearchHighlight; +}); + +S2.define('select2/i18n/en',[],function () { + // English + return { + errorLoading: function () { + return 'The results could not be loaded.'; + }, + inputTooLong: function (args) { + var overChars = args.input.length - args.maximum; + + var message = 'Please delete ' + overChars + ' character'; + + if (overChars != 1) { + message += 's'; + } + + return message; + }, + inputTooShort: function (args) { + var remainingChars = args.minimum - args.input.length; + + var message = 'Please enter ' + remainingChars + ' or more characters'; + + return message; + }, + loadingMore: function () { + return 'Loading more results…'; + }, + maximumSelected: function (args) { + var message = 'You can only select ' + args.maximum + ' item'; + + if (args.maximum != 1) { + message += 's'; + } + + return message; + }, + noResults: function () { + return 'No results found'; + }, + searching: function () { + return 'Searching…'; + }, + removeAllItems: function () { + return 'Remove all items'; + }, + removeItem: function () { + return 'Remove item'; + }, + search: function() { + return 'Search'; + } + }; +}); + +S2.define('select2/defaults',[ + 'jquery', + + './results', + + './selection/single', + './selection/multiple', + './selection/placeholder', + './selection/allowClear', + './selection/search', + './selection/selectionCss', + './selection/eventRelay', + + './utils', + './translation', + './diacritics', + + './data/select', + './data/array', + './data/ajax', + './data/tags', + './data/tokenizer', + './data/minimumInputLength', + './data/maximumInputLength', + './data/maximumSelectionLength', + + './dropdown', + './dropdown/search', + './dropdown/hidePlaceholder', + './dropdown/infiniteScroll', + './dropdown/attachBody', + './dropdown/minimumResultsForSearch', + './dropdown/selectOnClose', + './dropdown/closeOnSelect', + './dropdown/dropdownCss', + './dropdown/tagsSearchHighlight', + + './i18n/en' +], function ($, + + ResultsList, + + SingleSelection, MultipleSelection, Placeholder, AllowClear, + SelectionSearch, SelectionCSS, EventRelay, + + Utils, Translation, DIACRITICS, + + SelectData, ArrayData, AjaxData, Tags, Tokenizer, + MinimumInputLength, MaximumInputLength, MaximumSelectionLength, + + Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll, + AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect, + DropdownCSS, TagsSearchHighlight, + + EnglishTranslation) { + function Defaults () { + this.reset(); + } + + Defaults.prototype.apply = function (options) { + options = $.extend(true, {}, this.defaults, options); + + if (options.dataAdapter == null) { + if (options.ajax != null) { + options.dataAdapter = AjaxData; + } else if (options.data != null) { + options.dataAdapter = ArrayData; + } else { + options.dataAdapter = SelectData; + } + + if (options.minimumInputLength > 0) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + MinimumInputLength + ); + } + + if (options.maximumInputLength > 0) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + MaximumInputLength + ); + } + + if (options.maximumSelectionLength > 0) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + MaximumSelectionLength + ); + } + + if (options.tags) { + options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags); + } + + if (options.tokenSeparators != null || options.tokenizer != null) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + Tokenizer + ); + } + } + + if (options.resultsAdapter == null) { + options.resultsAdapter = ResultsList; + + if (options.ajax != null) { + options.resultsAdapter = Utils.Decorate( + options.resultsAdapter, + InfiniteScroll + ); + } + + if (options.placeholder != null) { + options.resultsAdapter = Utils.Decorate( + options.resultsAdapter, + HidePlaceholder + ); + } + + if (options.selectOnClose) { + options.resultsAdapter = Utils.Decorate( + options.resultsAdapter, + SelectOnClose + ); + } + + if (options.tags) { + options.resultsAdapter = Utils.Decorate( + options.resultsAdapter, + TagsSearchHighlight + ); + } + } + + if (options.dropdownAdapter == null) { + if (options.multiple) { + options.dropdownAdapter = Dropdown; + } else { + var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch); + + options.dropdownAdapter = SearchableDropdown; + } + + if (options.minimumResultsForSearch !== 0) { + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + MinimumResultsForSearch + ); + } + + if (options.closeOnSelect) { + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + CloseOnSelect + ); + } + + if (options.dropdownCssClass != null) { + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + DropdownCSS + ); + } + + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + AttachBody + ); + } + + if (options.selectionAdapter == null) { + if (options.multiple) { + options.selectionAdapter = MultipleSelection; + } else { + options.selectionAdapter = SingleSelection; + } + + // Add the placeholder mixin if a placeholder was specified + if (options.placeholder != null) { + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + Placeholder + ); + } + + if (options.allowClear) { + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + AllowClear + ); + } + + if (options.multiple) { + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + SelectionSearch + ); + } + + if (options.selectionCssClass != null) { + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + SelectionCSS + ); + } + + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + EventRelay + ); + } + + // If the defaults were not previously applied from an element, it is + // possible for the language option to have not been resolved + options.language = this._resolveLanguage(options.language); + + // Always fall back to English since it will always be complete + options.language.push('en'); + + var uniqueLanguages = []; + + for (var l = 0; l < options.language.length; l++) { + var language = options.language[l]; + + if (uniqueLanguages.indexOf(language) === -1) { + uniqueLanguages.push(language); + } + } + + options.language = uniqueLanguages; + + options.translations = this._processTranslations( + options.language, + options.debug + ); + + return options; + }; + + Defaults.prototype.reset = function () { + function stripDiacritics (text) { + // Used 'uni range + named function' from http://jsperf.com/diacritics/18 + function match(a) { + return DIACRITICS[a] || a; + } + + return text.replace(/[^\u0000-\u007E]/g, match); + } + + function matcher (params, data) { + // Always return the object if there is nothing to compare + if (params.term == null || params.term.trim() === '') { + return data; + } + + // Do a recursive check for options with children + if (data.children && data.children.length > 0) { + // Clone the data object if there are children + // This is required as we modify the object to remove any non-matches + var match = $.extend(true, {}, data); + + // Check each child of the option + for (var c = data.children.length - 1; c >= 0; c--) { + var child = data.children[c]; + + var matches = matcher(params, child); + + // If there wasn't a match, remove the object in the array + if (matches == null) { + match.children.splice(c, 1); + } + } + + // If any children matched, return the new object + if (match.children.length > 0) { + return match; + } + + // If there were no matching children, check just the plain object + return matcher(params, match); + } + + var original = stripDiacritics(data.text).toUpperCase(); + var term = stripDiacritics(params.term).toUpperCase(); + + // Check if the text contains the term + if (original.indexOf(term) > -1) { + return data; + } + + // If it doesn't contain the term, don't return anything + return null; + } + + this.defaults = { + amdLanguageBase: './i18n/', + autocomplete: 'off', + closeOnSelect: true, + debug: false, + dropdownAutoWidth: false, + escapeMarkup: Utils.escapeMarkup, + language: {}, + matcher: matcher, + minimumInputLength: 0, + maximumInputLength: 0, + maximumSelectionLength: 0, + minimumResultsForSearch: 0, + selectOnClose: false, + scrollAfterSelect: false, + sorter: function (data) { + return data; + }, + templateResult: function (result) { + return result.text; + }, + templateSelection: function (selection) { + return selection.text; + }, + theme: 'default', + width: 'resolve' + }; + }; + + Defaults.prototype.applyFromElement = function (options, $element) { + var optionLanguage = options.language; + var defaultLanguage = this.defaults.language; + var elementLanguage = $element.prop('lang'); + var parentLanguage = $element.closest('[lang]').prop('lang'); + + var languages = Array.prototype.concat.call( + this._resolveLanguage(elementLanguage), + this._resolveLanguage(optionLanguage), + this._resolveLanguage(defaultLanguage), + this._resolveLanguage(parentLanguage) + ); + + options.language = languages; + + return options; + }; + + Defaults.prototype._resolveLanguage = function (language) { + if (!language) { + return []; + } + + if ($.isEmptyObject(language)) { + return []; + } + + if ($.isPlainObject(language)) { + return [language]; + } + + var languages; + + if (!Array.isArray(language)) { + languages = [language]; + } else { + languages = language; + } + + var resolvedLanguages = []; + + for (var l = 0; l < languages.length; l++) { + resolvedLanguages.push(languages[l]); + + if (typeof languages[l] === 'string' && languages[l].indexOf('-') > 0) { + // Extract the region information if it is included + var languageParts = languages[l].split('-'); + var baseLanguage = languageParts[0]; + + resolvedLanguages.push(baseLanguage); + } + } + + return resolvedLanguages; + }; + + Defaults.prototype._processTranslations = function (languages, debug) { + var translations = new Translation(); + + for (var l = 0; l < languages.length; l++) { + var languageData = new Translation(); + + var language = languages[l]; + + if (typeof language === 'string') { + try { + // Try to load it with the original name + languageData = Translation.loadPath(language); + } catch (e) { + try { + // If we couldn't load it, check if it wasn't the full path + language = this.defaults.amdLanguageBase + language; + languageData = Translation.loadPath(language); + } catch (ex) { + // The translation could not be loaded at all. Sometimes this is + // because of a configuration problem, other times this can be + // because of how Select2 helps load all possible translation files + if (debug && window.console && console.warn) { + console.warn( + 'Select2: The language file for "' + language + '" could ' + + 'not be automatically loaded. A fallback will be used instead.' + ); + } + } + } + } else if ($.isPlainObject(language)) { + languageData = new Translation(language); + } else { + languageData = language; + } + + translations.extend(languageData); + } + + return translations; + }; + + Defaults.prototype.set = function (key, value) { + var camelKey = $.camelCase(key); + + var data = {}; + data[camelKey] = value; + + var convertedData = Utils._convertData(data); + + $.extend(true, this.defaults, convertedData); + }; + + var defaults = new Defaults(); + + return defaults; +}); + +S2.define('select2/options',[ + 'jquery', + './defaults', + './utils' +], function ($, Defaults, Utils) { + function Options (options, $element) { + this.options = options; + + if ($element != null) { + this.fromElement($element); + } + + if ($element != null) { + this.options = Defaults.applyFromElement(this.options, $element); + } + + this.options = Defaults.apply(this.options); + } + + Options.prototype.fromElement = function ($e) { + var excludedData = ['select2']; + + if (this.options.multiple == null) { + this.options.multiple = $e.prop('multiple'); + } + + if (this.options.disabled == null) { + this.options.disabled = $e.prop('disabled'); + } + + if (this.options.autocomplete == null && $e.prop('autocomplete')) { + this.options.autocomplete = $e.prop('autocomplete'); + } + + if (this.options.dir == null) { + if ($e.prop('dir')) { + this.options.dir = $e.prop('dir'); + } else if ($e.closest('[dir]').prop('dir')) { + this.options.dir = $e.closest('[dir]').prop('dir'); + } else { + this.options.dir = 'ltr'; + } + } + + $e.prop('disabled', this.options.disabled); + $e.prop('multiple', this.options.multiple); + + if (Utils.GetData($e[0], 'select2Tags')) { + if (this.options.debug && window.console && console.warn) { + console.warn( + 'Select2: The `data-select2-tags` attribute has been changed to ' + + 'use the `data-data` and `data-tags="true"` attributes and will be ' + + 'removed in future versions of Select2.' + ); + } + + Utils.StoreData($e[0], 'data', Utils.GetData($e[0], 'select2Tags')); + Utils.StoreData($e[0], 'tags', true); + } + + if (Utils.GetData($e[0], 'ajaxUrl')) { + if (this.options.debug && window.console && console.warn) { + console.warn( + 'Select2: The `data-ajax-url` attribute has been changed to ' + + '`data-ajax--url` and support for the old attribute will be removed' + + ' in future versions of Select2.' + ); + } + + $e.attr('ajax--url', Utils.GetData($e[0], 'ajaxUrl')); + Utils.StoreData($e[0], 'ajax-Url', Utils.GetData($e[0], 'ajaxUrl')); + } + + var dataset = {}; + + function upperCaseLetter(_, letter) { + return letter.toUpperCase(); + } + + // Pre-load all of the attributes which are prefixed with `data-` + for (var attr = 0; attr < $e[0].attributes.length; attr++) { + var attributeName = $e[0].attributes[attr].name; + var prefix = 'data-'; + + if (attributeName.substr(0, prefix.length) == prefix) { + // Get the contents of the attribute after `data-` + var dataName = attributeName.substring(prefix.length); + + // Get the data contents from the consistent source + // This is more than likely the jQuery data helper + var dataValue = Utils.GetData($e[0], dataName); + + // camelCase the attribute name to match the spec + var camelDataName = dataName.replace(/-([a-z])/g, upperCaseLetter); + + // Store the data attribute contents into the dataset since + dataset[camelDataName] = dataValue; + } + } + + // Prefer the element's `dataset` attribute if it exists + // jQuery 1.x does not correctly handle data attributes with multiple dashes + if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && $e[0].dataset) { + dataset = $.extend(true, {}, $e[0].dataset, dataset); + } + + // Prefer our internal data cache if it exists + var data = $.extend(true, {}, Utils.GetData($e[0]), dataset); + + data = Utils._convertData(data); + + for (var key in data) { + if (excludedData.indexOf(key) > -1) { + continue; + } + + if ($.isPlainObject(this.options[key])) { + $.extend(this.options[key], data[key]); + } else { + this.options[key] = data[key]; + } + } + + return this; + }; + + Options.prototype.get = function (key) { + return this.options[key]; + }; + + Options.prototype.set = function (key, val) { + this.options[key] = val; + }; + + return Options; +}); + +S2.define('select2/core',[ + 'jquery', + './options', + './utils', + './keys' +], function ($, Options, Utils, KEYS) { + var Select2 = function ($element, options) { + if (Utils.GetData($element[0], 'select2') != null) { + Utils.GetData($element[0], 'select2').destroy(); + } + + this.$element = $element; + + this.id = this._generateId($element); + + options = options || {}; + + this.options = new Options(options, $element); + + Select2.__super__.constructor.call(this); + + // Set up the tabindex + + var tabindex = $element.attr('tabindex') || 0; + Utils.StoreData($element[0], 'old-tabindex', tabindex); + $element.attr('tabindex', '-1'); + + // Set up containers and adapters + + var DataAdapter = this.options.get('dataAdapter'); + this.dataAdapter = new DataAdapter($element, this.options); + + var $container = this.render(); + + this._placeContainer($container); + + var SelectionAdapter = this.options.get('selectionAdapter'); + this.selection = new SelectionAdapter($element, this.options); + this.$selection = this.selection.render(); + + this.selection.position(this.$selection, $container); + + var DropdownAdapter = this.options.get('dropdownAdapter'); + this.dropdown = new DropdownAdapter($element, this.options); + this.$dropdown = this.dropdown.render(); + + this.dropdown.position(this.$dropdown, $container); + + var ResultsAdapter = this.options.get('resultsAdapter'); + this.results = new ResultsAdapter($element, this.options, this.dataAdapter); + this.$results = this.results.render(); + + this.results.position(this.$results, this.$dropdown); + + // Bind events + + var self = this; + + // Bind the container to all of the adapters + this._bindAdapters(); + + // Register any DOM event handlers + this._registerDomEvents(); + + // Register any internal event handlers + this._registerDataEvents(); + this._registerSelectionEvents(); + this._registerDropdownEvents(); + this._registerResultsEvents(); + this._registerEvents(); + + // Set the initial state + this.dataAdapter.current(function (initialData) { + self.trigger('selection:update', { + data: initialData + }); + }); + + // Hide the original select + $element[0].classList.add('select2-hidden-accessible'); + $element.attr('aria-hidden', 'true'); + + // Synchronize any monitored attributes + this._syncAttributes(); + + Utils.StoreData($element[0], 'select2', this); + + // Ensure backwards compatibility with $element.data('select2'). + $element.data('select2', this); + }; + + Utils.Extend(Select2, Utils.Observable); + + Select2.prototype._generateId = function ($element) { + var id = ''; + + if ($element.attr('id') != null) { + id = $element.attr('id'); + } else if ($element.attr('name') != null) { + id = $element.attr('name') + '-' + Utils.generateChars(2); + } else { + id = Utils.generateChars(4); + } + + id = id.replace(/(:|\.|\[|\]|,)/g, ''); + id = 'select2-' + id; + + return id; + }; + + Select2.prototype._placeContainer = function ($container) { + $container.insertAfter(this.$element); + + var width = this._resolveWidth(this.$element, this.options.get('width')); + + if (width != null) { + $container.css('width', width); + } + }; + + Select2.prototype._resolveWidth = function ($element, method) { + var WIDTH = /^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i; + + if (method == 'resolve') { + var styleWidth = this._resolveWidth($element, 'style'); + + if (styleWidth != null) { + return styleWidth; + } + + return this._resolveWidth($element, 'element'); + } + + if (method == 'element') { + var elementWidth = $element.outerWidth(false); + + if (elementWidth <= 0) { + return 'auto'; + } + + return elementWidth + 'px'; + } + + if (method == 'style') { + var style = $element.attr('style'); + + if (typeof(style) !== 'string') { + return null; + } + + var attrs = style.split(';'); + + for (var i = 0, l = attrs.length; i < l; i = i + 1) { + var attr = attrs[i].replace(/\s/g, ''); + var matches = attr.match(WIDTH); + + if (matches !== null && matches.length >= 1) { + return matches[1]; + } + } + + return null; + } + + if (method == 'computedstyle') { + var computedStyle = window.getComputedStyle($element[0]); + + return computedStyle.width; + } + + return method; + }; + + Select2.prototype._bindAdapters = function () { + this.dataAdapter.bind(this, this.$container); + this.selection.bind(this, this.$container); + + this.dropdown.bind(this, this.$container); + this.results.bind(this, this.$container); + }; + + Select2.prototype._registerDomEvents = function () { + var self = this; + + this.$element.on('change.select2', function () { + self.dataAdapter.current(function (data) { + self.trigger('selection:update', { + data: data + }); + }); + }); + + this.$element.on('focus.select2', function (evt) { + self.trigger('focus', evt); + }); + + this._syncA = Utils.bind(this._syncAttributes, this); + this._syncS = Utils.bind(this._syncSubtree, this); + + this._observer = new window.MutationObserver(function (mutations) { + self._syncA(); + self._syncS(mutations); + }); + this._observer.observe(this.$element[0], { + attributes: true, + childList: true, + subtree: false + }); + }; + + Select2.prototype._registerDataEvents = function () { + var self = this; + + this.dataAdapter.on('*', function (name, params) { + self.trigger(name, params); + }); + }; + + Select2.prototype._registerSelectionEvents = function () { + var self = this; + var nonRelayEvents = ['toggle', 'focus']; + + this.selection.on('toggle', function () { + self.toggleDropdown(); + }); + + this.selection.on('focus', function (params) { + self.focus(params); + }); + + this.selection.on('*', function (name, params) { + if (nonRelayEvents.indexOf(name) !== -1) { + return; + } + + self.trigger(name, params); + }); + }; + + Select2.prototype._registerDropdownEvents = function () { + var self = this; + + this.dropdown.on('*', function (name, params) { + self.trigger(name, params); + }); + }; + + Select2.prototype._registerResultsEvents = function () { + var self = this; + + this.results.on('*', function (name, params) { + self.trigger(name, params); + }); + }; + + Select2.prototype._registerEvents = function () { + var self = this; + + this.on('open', function () { + self.$container[0].classList.add('select2-container--open'); + }); + + this.on('close', function () { + self.$container[0].classList.remove('select2-container--open'); + }); + + this.on('enable', function () { + self.$container[0].classList.remove('select2-container--disabled'); + }); + + this.on('disable', function () { + self.$container[0].classList.add('select2-container--disabled'); + }); + + this.on('blur', function () { + self.$container[0].classList.remove('select2-container--focus'); + }); + + this.on('query', function (params) { + if (!self.isOpen()) { + self.trigger('open', {}); + } + + this.dataAdapter.query(params, function (data) { + self.trigger('results:all', { + data: data, + query: params + }); + }); + }); + + this.on('query:append', function (params) { + this.dataAdapter.query(params, function (data) { + self.trigger('results:append', { + data: data, + query: params + }); + }); + }); + + this.on('keypress', function (evt) { + var key = evt.which; + + if (self.isOpen()) { + if (key === KEYS.ESC || (key === KEYS.UP && evt.altKey)) { + self.close(evt); + + evt.preventDefault(); + } else if (key === KEYS.ENTER || key === KEYS.TAB) { + self.trigger('results:select', {}); + + evt.preventDefault(); + } else if ((key === KEYS.SPACE && evt.ctrlKey)) { + self.trigger('results:toggle', {}); + + evt.preventDefault(); + } else if (key === KEYS.UP) { + self.trigger('results:previous', {}); + + evt.preventDefault(); + } else if (key === KEYS.DOWN) { + self.trigger('results:next', {}); + + evt.preventDefault(); + } + } else { + if (key === KEYS.ENTER || key === KEYS.SPACE || + (key === KEYS.DOWN && evt.altKey)) { + self.open(); + + evt.preventDefault(); + } + } + }); + }; + + Select2.prototype._syncAttributes = function () { + this.options.set('disabled', this.$element.prop('disabled')); + + if (this.isDisabled()) { + if (this.isOpen()) { + this.close(); + } + + this.trigger('disable', {}); + } else { + this.trigger('enable', {}); + } + }; + + Select2.prototype._isChangeMutation = function (mutations) { + var self = this; + + if (mutations.addedNodes && mutations.addedNodes.length > 0) { + for (var n = 0; n < mutations.addedNodes.length; n++) { + var node = mutations.addedNodes[n]; + + if (node.selected) { + return true; + } + } + } else if (mutations.removedNodes && mutations.removedNodes.length > 0) { + return true; + } else if (Array.isArray(mutations)) { + return mutations.some(function (mutation) { + return self._isChangeMutation(mutation); + }); + } + + return false; + }; + + Select2.prototype._syncSubtree = function (mutations) { + var changed = this._isChangeMutation(mutations); + var self = this; + + // Only re-pull the data if we think there is a change + if (changed) { + this.dataAdapter.current(function (currentData) { + self.trigger('selection:update', { + data: currentData + }); + }); + } + }; + + /** + * Override the trigger method to automatically trigger pre-events when + * there are events that can be prevented. + */ + Select2.prototype.trigger = function (name, args) { + var actualTrigger = Select2.__super__.trigger; + var preTriggerMap = { + 'open': 'opening', + 'close': 'closing', + 'select': 'selecting', + 'unselect': 'unselecting', + 'clear': 'clearing' + }; + + if (args === undefined) { + args = {}; + } + + if (name in preTriggerMap) { + var preTriggerName = preTriggerMap[name]; + var preTriggerArgs = { + prevented: false, + name: name, + args: args + }; + + actualTrigger.call(this, preTriggerName, preTriggerArgs); + + if (preTriggerArgs.prevented) { + args.prevented = true; + + return; + } + } + + actualTrigger.call(this, name, args); + }; + + Select2.prototype.toggleDropdown = function () { + if (this.isDisabled()) { + return; + } + + if (this.isOpen()) { + this.close(); + } else { + this.open(); + } + }; + + Select2.prototype.open = function () { + if (this.isOpen()) { + return; + } + + if (this.isDisabled()) { + return; + } + + this.trigger('query', {}); + }; + + Select2.prototype.close = function (evt) { + if (!this.isOpen()) { + return; + } + + this.trigger('close', { originalEvent : evt }); + }; + + /** + * Helper method to abstract the "enabled" (not "disabled") state of this + * object. + * + * @return {true} if the instance is not disabled. + * @return {false} if the instance is disabled. + */ + Select2.prototype.isEnabled = function () { + return !this.isDisabled(); + }; + + /** + * Helper method to abstract the "disabled" state of this object. + * + * @return {true} if the disabled option is true. + * @return {false} if the disabled option is false. + */ + Select2.prototype.isDisabled = function () { + return this.options.get('disabled'); + }; + + Select2.prototype.isOpen = function () { + return this.$container[0].classList.contains('select2-container--open'); + }; + + Select2.prototype.hasFocus = function () { + return this.$container[0].classList.contains('select2-container--focus'); + }; + + Select2.prototype.focus = function (data) { + // No need to re-trigger focus events if we are already focused + if (this.hasFocus()) { + return; + } + + this.$container[0].classList.add('select2-container--focus'); + this.trigger('focus', {}); + }; + + Select2.prototype.enable = function (args) { + if (this.options.get('debug') && window.console && console.warn) { + console.warn( + 'Select2: The `select2("enable")` method has been deprecated and will' + + ' be removed in later Select2 versions. Use $element.prop("disabled")' + + ' instead.' + ); + } + + if (args == null || args.length === 0) { + args = [true]; + } + + var disabled = !args[0]; + + this.$element.prop('disabled', disabled); + }; + + Select2.prototype.data = function () { + if (this.options.get('debug') && + arguments.length > 0 && window.console && console.warn) { + console.warn( + 'Select2: Data can no longer be set using `select2("data")`. You ' + + 'should consider setting the value instead using `$element.val()`.' + ); + } + + var data = []; + + this.dataAdapter.current(function (currentData) { + data = currentData; + }); + + return data; + }; + + Select2.prototype.val = function (args) { + if (this.options.get('debug') && window.console && console.warn) { + console.warn( + 'Select2: The `select2("val")` method has been deprecated and will be' + + ' removed in later Select2 versions. Use $element.val() instead.' + ); + } + + if (args == null || args.length === 0) { + return this.$element.val(); + } + + var newVal = args[0]; + + if (Array.isArray(newVal)) { + newVal = newVal.map(function (obj) { + return obj.toString(); + }); + } + + this.$element.val(newVal).trigger('input').trigger('change'); + }; + + Select2.prototype.destroy = function () { + Utils.RemoveData(this.$container[0]); + this.$container.remove(); + + this._observer.disconnect(); + this._observer = null; + + this._syncA = null; + this._syncS = null; + + this.$element.off('.select2'); + this.$element.attr('tabindex', + Utils.GetData(this.$element[0], 'old-tabindex')); + + this.$element[0].classList.remove('select2-hidden-accessible'); + this.$element.attr('aria-hidden', 'false'); + Utils.RemoveData(this.$element[0]); + this.$element.removeData('select2'); + + this.dataAdapter.destroy(); + this.selection.destroy(); + this.dropdown.destroy(); + this.results.destroy(); + + this.dataAdapter = null; + this.selection = null; + this.dropdown = null; + this.results = null; + }; + + Select2.prototype.render = function () { + var $container = $( + '' + + '' + + '' + + '' + ); + + $container.attr('dir', this.options.get('dir')); + + this.$container = $container; + + this.$container[0].classList + .add('select2-container--' + this.options.get('theme')); + + Utils.StoreData($container[0], 'element', this.$element); + + return $container; + }; + + return Select2; +}); + +S2.define('jquery-mousewheel',[ + 'jquery' +], function ($) { + // Used to shim jQuery.mousewheel for non-full builds. + return $; +}); + +S2.define('jquery.select2',[ + 'jquery', + 'jquery-mousewheel', + + './select2/core', + './select2/defaults', + './select2/utils' +], function ($, _, Select2, Defaults, Utils) { + if ($.fn.select2 == null) { + // All methods that should return the element + var thisMethods = ['open', 'close', 'destroy']; + + $.fn.select2 = function (options) { + options = options || {}; + + if (typeof options === 'object') { + this.each(function () { + var instanceOptions = $.extend(true, {}, options); + + var instance = new Select2($(this), instanceOptions); + }); + + return this; + } else if (typeof options === 'string') { + var ret; + var args = Array.prototype.slice.call(arguments, 1); + + this.each(function () { + var instance = Utils.GetData(this, 'select2'); + + if (instance == null && window.console && console.error) { + console.error( + 'The select2(\'' + options + '\') method was called on an ' + + 'element that is not using Select2.' + ); + } + + ret = instance[options].apply(instance, args); + }); + + // Check if we should be returning `this` + if (thisMethods.indexOf(options) > -1) { + return this; + } + + return ret; + } else { + throw new Error('Invalid arguments for Select2: ' + options); + } + }; + } + + if ($.fn.select2.defaults == null) { + $.fn.select2.defaults = Defaults; + } + + return Select2; +}); + + // Return the AMD loader configuration so it can be used outside of this file + return { + define: S2.define, + require: S2.require + }; +}()); + + // Autoload the jQuery bindings + // We know that all of the modules exist above this, so we're safe + var select2 = S2.require('jquery.select2'); + + // Hold the AMD module references on the jQuery function that was just loaded + // This allows Select2 to use the internal loader outside of this file, such + // as in the language files. + jQuery.fn.select2.amd = S2; + + // Return the Select2 instance for anyone who is importing it. + return select2; +})); diff --git a/src/scripts/cores/plugin.js b/src/scripts/cores/plugin.js new file mode 100644 index 0000000..00cb4c8 --- /dev/null +++ b/src/scripts/cores/plugin.js @@ -0,0 +1,97 @@ +const states = { + loading: 0, + interactive: 1, + complete: 2 +}; + +const READY_STATE = 'DOMContentLoaded'; + +function getElementData (el) { + const elDataset = el.dataset; + + return Object.keys(elDataset).reduce((obj, key) => { + let data = {}; + + if (elDataset[key]) { + data[key] = ''; + + try { + data[key] = JSON.parse(elDataset[key]); + } catch (err) { + data[key] = elDataset[key]; + } + } + + return { ...obj, ...data }; + }, {}); +} + +function setupClass (Class, element, options, pluginName) { + const _this = new Class(); + + _this.__pluginName = pluginName; + _this.$element = $(element); + _this.options = $.extend( + {}, + $.fn[pluginName].defaults, + getElementData(element), + options + ); + _this.props = {}; + + typeof _this.init === 'function' && _this.init(); + + return _this; +} + +export default function Plugin (param) { + function createPlugin (Class) { + const baseName = Class.name; + const name = baseName.toKebabCase(); + const options = (param && param.options) || {}; + const loadEvent = (param && param.when) || READY_STATE; + const selector = param && param.selector; + + function init () { + $(`[data-${name}]`)[name](); + + if (typeof selector === 'string') { + $(selector)[name](); + } + } + + $.fn[name] = function (opts, params) { + const instanceName = `${name}-instance`; + + return this.each(function () { + const instance = $.data(this, instanceName); + + if (!(instance instanceof Class)) { + $.data(this, instanceName, setupClass(Class, this, opts, name)); + + return; + } + + if (typeof instance[opts] !== 'function') { + console.error(`This element has been initialized with plugin ${baseName}, please provide a correct method`); + + return; + } + + instance[opts](params); + }); + }; + + $.fn[name].defaults = options; + + if (loadEvent === READY_STATE && states[document.readyState] > 0) { + init(); + } else { + window.addEventListener(loadEvent, init); + } + + return Class; + } + + return typeof param === 'function' ? createPlugin(param) : createPlugin; +} diff --git a/src/scripts/cores/prototype.js b/src/scripts/cores/prototype.js new file mode 100644 index 0000000..8d36500 --- /dev/null +++ b/src/scripts/cores/prototype.js @@ -0,0 +1,17 @@ +const PI = Math.PI; + +String.prototype.toCamelCase = function () { + return this.replace(/-([a-z])/g, (g) => g[1].toUpperCase()); +}; + +String.prototype.toKebabCase = function () { + return this.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); +}; + +Number.prototype.toRad = function () { + return this * PI / 180; +}; + +Number.prototype.toDeg = function () { + return this * 180 / PI; +}; diff --git a/src/scripts/initializations/browser-detect.js b/src/scripts/initializations/browser-detect.js new file mode 100644 index 0000000..6b5b080 --- /dev/null +++ b/src/scripts/initializations/browser-detect.js @@ -0,0 +1,59 @@ +const { documentElement: html } = document; + +const isTouch = 'ontouchstart' in window + || navigator.maxTouchPoints > 0 + || navigator.msMaxTouchPoints > 0; + +html.classList.add(isTouch ? 'touch' : 'no-touch'); + +if (typeof InstallTrigger !== 'undefined') { + html.classList.add('firefox'); +} + +const isIOSPlatform = [ + 'iPad Simulator', + 'iPhone Simulator', + 'iPod Simulator', + 'iPad', + 'iPhone', + 'iPod', +].includes(navigator.platform); + +if (isIOSPlatform || (navigator.userAgent.includes('Mac') && isTouch)) { + html.classList.add('ios'); +} + +const isDeviceMobile = [ + 'Android', + 'webOS', + 'iPhone', + 'iPod', + 'BlackBerry', + 'IEMobile', + 'Opera Mini', +].includes(navigator.userAgent); +if (isDeviceMobile) { + html.classList.add('is-device'); +} + +const isIE = document.documentMode; + +if (isIE) { + html.classList.add('ie'); +} + +if (!isIE && window.StyleMedia) { + html.classList.add('edge'); +} + +const isChrome = !!window.chrome; + +if (isChrome) { + html.classList.add('chrome'); +} + +if (isChrome && navigator.userAgent.indexOf('Edg') > -1) { + html.classList.add('edge-chromium'); +} + +export default null; diff --git a/src/scripts/initializations/import-jquery-plugins.js b/src/scripts/initializations/import-jquery-plugins.js new file mode 100644 index 0000000..7befcf3 --- /dev/null +++ b/src/scripts/initializations/import-jquery-plugins.js @@ -0,0 +1,9 @@ +// Initializations +import '../cores/prototype'; +import 'moment'; +import 'parsleyjs'; +import 'jquery'; + +// import '../_libs/jquery.fullPage.min.js'; + +window.devj = $; diff --git a/src/scripts/initializations/improve-window-events.js b/src/scripts/initializations/improve-window-events.js new file mode 100644 index 0000000..fc699c9 --- /dev/null +++ b/src/scripts/initializations/improve-window-events.js @@ -0,0 +1,82 @@ +import { throttle } from '../utils'; +import { $win } from '../utils/doms'; +import layout from '../utils/layout'; + +let passiveIfSupported = false; +let lastWinScroll = layout.scroll; +let resizeTimeout; +let lastWinWidth = layout.width; +let lastWinHeight = layout.height; +let lastBreakpointIsDesktop = layout.isDesktop; + +const RESIZE_TIME = 180; + +try { + const passive = Object.defineProperty({}, 'passive', { + get() { + passiveIfSupported = { passive: true }; + + return true; + }, + }); + + window.addEventListener('test', null, passive); +} catch (err) { /**/ } + +window.addEventListener('scroll', throttle(() => { + const currentWinScroll = layout.scroll; + + if (currentWinScroll === lastWinScroll) { + return; + } + + const name = currentWinScroll < lastWinScroll ? 'up' : 'down'; + + $win.trigger('scrolling', currentWinScroll); + $win.trigger(`scroll:${name}`, currentWinScroll); + + lastWinScroll = currentWinScroll; +}), passiveIfSupported); + +window.addEventListener('resize', () => { + clearTimeout(resizeTimeout); + setTimeout(() => { + const currentWinWidth = layout.width; + const currentWinHeight = layout.height; + const isWidthChanged = lastWinWidth !== currentWinWidth; + const isHeightChanged = lastWinHeight !== currentWinHeight; + + $win.trigger('resized', [currentWinWidth, currentWinHeight]); + + if (isWidthChanged) { + $win.trigger('width-change', currentWinWidth); + + const currentBreakpointIsDesktop = layout.isDesktop; + + if (lastBreakpointIsDesktop !== currentBreakpointIsDesktop) { + // Prevent conflict event name with slick + $win.trigger('breakpoint:change', currentWinWidth); + + const breakpointEvtName = currentBreakpointIsDesktop + ? 'desktop' + : 'mobile'; + + $win.trigger(`breakpoint:${breakpointEvtName}`, currentWinWidth); + + lastBreakpointIsDesktop = currentBreakpointIsDesktop; + } + + lastWinWidth = currentWinWidth; + } + + if (isHeightChanged) { + $win.trigger('height-change', currentWinHeight); + + lastWinHeight = currentWinHeight; + } + + if (isWidthChanged && isHeightChanged) { + $win.trigger('size-change', currentWinWidth, currentWinHeight); + } + }, RESIZE_TIME); +}, passiveIfSupported); diff --git a/src/scripts/initializations/update-js-assets-path.js b/src/scripts/initializations/update-js-assets-path.js new file mode 100644 index 0000000..2c6b38f --- /dev/null +++ b/src/scripts/initializations/update-js-assets-path.js @@ -0,0 +1,7 @@ +const { staticJsAssetsPath } = window; + +if (staticJsAssetsPath) { + __webpack_require__.p = staticJsAssetsPath; +} + +export default null; diff --git a/src/scripts/main.js b/src/scripts/main.js new file mode 100644 index 0000000..4617aca --- /dev/null +++ b/src/scripts/main.js @@ -0,0 +1,8 @@ +import '@/initializations/update-js-assets-path'; +import '@/initializations/browser-detect'; +import '@/initializations/import-jquery-plugins'; +import '@/initializations/improve-window-events'; +import 'slick-carousel'; + +// Plugins +import '@/plugins/back-to-top'; diff --git a/src/scripts/plugins/back-to-top.js b/src/scripts/plugins/back-to-top.js new file mode 100644 index 0000000..0251b65 --- /dev/null +++ b/src/scripts/plugins/back-to-top.js @@ -0,0 +1,22 @@ +import { $win } from '@/utils/doms'; + +@Plugin +export default class BackToTop { + init() { + const $ele = this.$element; + + $ele.off(`click`).on(`click`, () => { + $('html, body').animate({ + scrollTop: 0, + }); + }); + + $win.on('scroll', () => { + if ($win.scrollTop() > 20) { + $ele.addClass('active'); + } else { + $ele.removeClass('active'); + } + }); + } +} diff --git a/src/scripts/repositories/api.js b/src/scripts/repositories/api.js new file mode 100644 index 0000000..38106c7 --- /dev/null +++ b/src/scripts/repositories/api.js @@ -0,0 +1,98 @@ +import { transformObjectToParams } from '@/utils'; + +function getScript(src) { + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + + script.async = true; + script.src = src; + script.onload = resolve; + script.onerror = reject; + + document.head.appendChild(script); + }); +} + +export class ApiRepository { + baseUrl = ''; + + scriptCacher = {}; + + getScript = getScript; + + constructor(baseUrl = '') { + this.baseUrl = baseUrl; + } + + getUrl(path, params) { + let url = this.baseUrl + path; + + if (typeof params === 'object') { + url += `?${transformObjectToParams(params)}`; + } + + return url; + } + + loadScript(url) { + if (!this.scriptCacher[url]) { + this.scriptCacher[url] = getScript(url); + } + + return this.scriptCacher[url]; + } + + get(path, data) { + return new Promise((resolve, reject) => { + const url = this.getUrl(path); + + $.ajax({ + url, + data, + method: 'get', + }) + .done(resolve) + .catch(reject); + }); + } + + post(path, data = {}) { + return new Promise((resolve, reject) => { + const url = this.getUrl(path); + + $.ajax({ + url, + data, + method: 'post', + }) + .done(resolve) + .catch(reject); + }); + } + + put(path, data = {}) { + return new Promise((resolve, reject) => { + const url = this.getUrl(path); + + $.ajax({ + url, + data, + method: 'put', + }) + .done(resolve) + .catch(reject); + }); + } + + async resolveJSON(data) { + if (typeof data === 'object') { + return data; + } + + const response = await this.get(data); + + return response; + } +} + +export default new ApiRepository(); diff --git a/src/scripts/utils/cookies.js b/src/scripts/utils/cookies.js new file mode 100644 index 0000000..47cbf52 --- /dev/null +++ b/src/scripts/utils/cookies.js @@ -0,0 +1,40 @@ +export default { + set(key, value = '', days = 7) { + if (!key) { + console.error('no key to set cookie'); + return; + } + + let expires = ''; + + if (days) { + const date = new Date(); + const expiresDate = days * 24 * 60 * 60 * 1000; + + date.setTime(date.getTime() + expiresDate); + expires = `; expires=${date.toUTCString()}`; + } + + document.cookie = `${key}=${value.toString() + expires}`; + }, + + get(key) { + const cookieArr = document.cookie.split(';'); + + for (let i = 0, l = cookieArr.length; i < l; i += 1) { + const perCookie = cookieArr[i].trim(); + const indexOfSplitter = perCookie.trim().indexOf('='); + const perCookieKey = perCookie.slice(0, indexOfSplitter); + + if (perCookieKey === key) { + return perCookie.slice(indexOfSplitter + 1); + } + } + + return null; + }, + + remove(key) { + document.cookie = `${key}=; Max-Age=-99999999;`; + }, +}; diff --git a/src/scripts/utils/date.js b/src/scripts/utils/date.js new file mode 100644 index 0000000..6828b67 --- /dev/null +++ b/src/scripts/utils/date.js @@ -0,0 +1,11 @@ +import dayjs from 'dayjs'; + +export const defaultDateFormat = 'YYYY-MM-DD'; + +export function transformNumberToDate(number, format = defaultDateFormat) { + return dayjs(number).format(format); +} + +export function transformDateToNumber(date) { + return +dayjs(date, defaultDateFormat); +} diff --git a/src/scripts/utils/doms.js b/src/scripts/utils/doms.js new file mode 100644 index 0000000..43e6688 --- /dev/null +++ b/src/scripts/utils/doms.js @@ -0,0 +1,9 @@ +export const $win = $(window); +export const $doc = $(document); +export const $html = $('html'); +export const $body = $('body'); +export const $mapKey = $('meta[name="map-key"]'); +export const $htmlAndbody = $('html, body'); +export const $canFixed = $([ + '.header', +].join(',')); diff --git a/src/scripts/utils/http.js b/src/scripts/utils/http.js new file mode 100644 index 0000000..6bf70b8 --- /dev/null +++ b/src/scripts/utils/http.js @@ -0,0 +1,75 @@ +import { GMAP_URL, DEFAULT_AJAX_OPTS } from './variables'; +import { wait } from './index'; +import { lang } from './layout'; + +const SCRIPT_CACHED = {}; + +function getScript (src) { + return new Promise((resolve, reject) => { + let script = document.createElement('script'); + + script.async = true; + script.src = src; + script.onload = resolve; + script.onerror = reject; + + document.head.appendChild(script); + }); +} + +export function loadScript (url) { + if (!SCRIPT_CACHED[url]) { + SCRIPT_CACHED[url] = getScript(url); + } + + return SCRIPT_CACHED[url]; +} + +export function callApi (opts) { + return new Promise((resolve, reject) => { + const isString = typeof opts === 'string'; + + let options = { + ...DEFAULT_AJAX_OPTS, + data: {}, + url: isString ? opts : '' + }; + + if (!isString) { + options = { ...options, ...opts }; + } + + if (!options.data.lang) { + options.data.lang = lang; + } + + $.ajax(options).done(resolve).fail(reject); + }); +} + +export async function loadMapApi () { + await loadScript(GMAP_URL); + + return window.google.maps; +} + +export async function download (url, fileName = '') { + if (!url) { + return; + } + + const $link = $('', { + href: url, + download: fileName, + style: 'display:none' + }); + + $link + .on('click', e => e.stopImmediatePropagation()) + .appendTo('body')[0] + .click(); + + await wait(); + + $link.remove(); +} diff --git a/src/scripts/utils/index.js b/src/scripts/utils/index.js new file mode 100644 index 0000000..c0abd06 --- /dev/null +++ b/src/scripts/utils/index.js @@ -0,0 +1,53 @@ +export function throttle(fn, thisArg = window) { + let scheduledAnimationFrame; + + return () => { + if (scheduledAnimationFrame) { + return; + } + + scheduledAnimationFrame = true; + + requestAnimationFrame(() => { + fn.call(thisArg); + scheduledAnimationFrame = false; + }); + }; +} + +export function transformToCamelCase(str) { + return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase()); +} + +export function transformToKebabCase(str) { + return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); +} + +export function cloneJSON(json) { + return JSON.parse(JSON.stringify(json)); +} + +export function transformObjectToParams(obj) { + return Object + .keys(obj) + .reduce((arr, key) => ( + arr.concat(`${key}=${encodeURIComponent(obj[key])}`) + ), []) + .join('&'); +} + +export function wait (ms = 10) { + let timeout; + + const _promise = new Promise((resolve) => { + timeout = setTimeout(() => { + resolve(timeout); + }, ms); + }); + + _promise.cancel = () => clearTimeout(timeout); + + return _promise; +} + +export const waitTmp = { cancel () { /* empty fn */ } }; diff --git a/src/scripts/utils/layout.js b/src/scripts/utils/layout.js new file mode 100644 index 0000000..57f94d4 --- /dev/null +++ b/src/scripts/utils/layout.js @@ -0,0 +1,225 @@ +import { $canFixed } from '@/utils/doms'; +import { RESPONSIVE_BREAKPOINTS } from './variables'; + +const { TABLET, DESKTOP } = RESPONSIVE_BREAKPOINTS; + +const { body, documentElement: html } = document; + +const freezeClass = '--freeze'; +const addBorderClass = '--add-fixed-border'; + +let lockTimeout; +let lastScroll; +let $header = $('.header'); + +function calculateScrollWidth() { + const div = document.createElement('div'); + + div.style.position = 'absolute'; + div.style.top = '0px'; + div.style.left = '0px'; + div.style.width = '100%'; + div.style.height = '50px'; + + body.appendChild(div); + + const fullWidth = div.offsetWidth; + + div.style.overflowY = 'scroll'; + + const limitWidth = div.clientWidth; + + body.removeChild(div); + + const scrollWidth = fullWidth - limitWidth; + + html.classList.add(`--scroll-${scrollWidth}`); + + return scrollWidth; +} + +export const removeAccents = (alias) => { + let str = alias; + str = str.replace(/à|á|ạ|ả|ã|â|ầ|ấ|ậ|ẩ|ẫ|ă|ằ|ắ|ặ|ẳ|ẵ/g, 'a') + .replace(/è|é|ẹ|ẻ|ẽ|ê|ề|ế|ệ|ể|ễ/g, 'e') + .replace(/ì|í|ị|ỉ|ĩ/g, 'i') + .replace(/ò|ó|ọ|ỏ|õ|ô|ồ|ố|ộ|ổ|ỗ|ơ|ờ|ớ|ợ|ở|ỡ/g, 'o') + .replace(/ù|ú|ụ|ủ|ũ|ư|ừ|ứ|ự|ử|ữ/g, 'u') + .replace(/ỳ|ý|ỵ|ỷ|ỹ/g, 'y') + .replace(/đ/g, 'd') + .replace(/À|Á|Ạ|Ả|Ã|Â|Ầ|Ấ|Ậ|Ẩ|Ẫ|Ă|Ằ|Ắ|Ặ|Ẳ|Ẵ/g, 'A') + .replace(/È|É|Ẹ|Ẻ|Ẽ|Ê|Ề|Ế|Ệ|Ể|Ễ/g, 'E') + .replace(/Ì|Í|Ị|Ỉ|Ĩ/g, 'I') + .replace(/Ò|Ó|Ọ|Ỏ|Õ|Ô|Ồ|Ố|Ộ|Ổ|Ỗ|Ơ|Ờ|Ớ|Ợ|Ở|Ỡ/g, 'O') + .replace(/Ù|Ú|Ụ|Ủ|Ũ|Ư|Ừ|Ứ|Ự|Ử|Ữ/g, 'U') + .replace(/Ỳ|Ý|Ỵ|Ỷ|Ỹ/g, 'Y') + .replace(/Đ/g, 'D') + .replace(/\u0300|\u0301|\u0303|\u0309|\u0323/g, '') + .replace(/\u02C6|\u0306|\u031B/g, '') + .replace(/[\u0300-\u036f]/g, ''); + return str; +}; + +export const removeEscape = (value) => { + const str = value.trim().replace(/(<([^>]+)>)|&|<|>|"|'/ig, ''); + + return removeAccents(str); +}; + +export const regexEmail = /^([A-Za-z0-9][\._\+\-]{0,1})+[A-Za-z0-9]+@[A-Za-z0-9]+(\.{0,1}[A-Za-z0-9]){2,3}\.[a-z]{2,3}$/; //eslint-disable-line + +export const scrollWidth = calculateScrollWidth(); +export const lang = html.getAttribute('lang') || ''; +export const isRTL = html.getAttribute('dir') === 'rtl'; +export const isIOS = html.classList.contains('ios'); +export const isIE = html.classList.contains('ie') + || html.classList.contains('edge'); + +export default { + scrollWidth, + lang, + isRTL, + isIOS, + isIE, + + get screenWidth() { + return window.innerWidth; + }, + + get width() { + return body.clientWidth; + }, + + get height() { + return window.innerHeight; + }, + + get bodyHeight() { + return body.offsetHeight; + }, + + get isFrozen() { + return body.classList.contains(freezeClass); + }, + + get isDesktop() { + return this.screenWidth >= DESKTOP; + }, + + get isSmallScreen() { + return this.screenWidth >= TABLET; + }, + + get isMobile() { + return !this.isDesktop; + }, + + get scroll() { + return document.documentElement.scrollTop + || document.body.scrollTop + || window.pageYOffset; + }, + + set scroll(top) { + window.scrollTo({ + top, + left: 0, + behavior: 'smooth', + }); + }, + + scrollImmediate(top) { + window.scrollTo(0, top); + }, + + scrollToElement($element, immediately) { + if (!$element[0]) { + return; + } + + if (!$header[0]) { + $header = $('.header'); + } + + const scrollTo = $element.offset().top - ($header.outerHeight() || 0); + + if (immediately) { + this.scrollImmediate(scrollTo); + } else { + this.scroll = $element.offset().top - ($header.outerHeight() || 0); + } + }, + + freeze(callback) { + clearTimeout(lockTimeout); + setTimeout(() => { + window.isFreezing = true; + + const willBeFrozen = !this.isFrozen; + + if (!willBeFrozen) { + if (typeof callback === 'function') { + callback(); + } + + return; + } + + if (this.isIOS) { + lastScroll = this.scroll; + body.style.top = `${-lastScroll}px`; + } + + body.classList.add(freezeClass); + + if (this.bodyHeight > this.height) { + body.classList.add(addBorderClass); + $canFixed.each((_, element) => { + if (window.getComputedStyle(element).position === 'fixed') { + element.classList.add(addBorderClass); + } + }); + } + + if (this.isIOS) { + this.scrollImmediate(0); + } + + if (typeof callback === 'function') { + callback(); + } + }); + }, + + unfreeze(callback) { + clearTimeout(lockTimeout); + lockTimeout = setTimeout(() => { + window.isFreezing = false; + + if (!this.isFrozen) { + if (typeof callback === 'function') { + callback(); + } + + return; + } + + body.classList.remove(freezeClass); + body.classList.remove(addBorderClass); + $canFixed.removeClass(addBorderClass); + + if (this.isIOS) { + body.style.top = ''; + this.scrollImmediate(lastScroll); + + if (typeof callback === 'function') { + setTimeout(() => { + callback(); + }, 50); + } + } else if (typeof callback === 'function') { + callback(); + } + }); + }, +}; diff --git a/src/scripts/utils/variables.js b/src/scripts/utils/variables.js new file mode 100644 index 0000000..b846adb --- /dev/null +++ b/src/scripts/utils/variables.js @@ -0,0 +1,17 @@ +import { $mapKey } from '../utils/doms'; + +const GMAP_KEY = $mapKey.attr('content') || ''; + + +export const GMAP_URL = GMAP_KEY && `https://maps.googleapis.com/maps/api/js?v=3&key=${GMAP_KEY}`; + +export const RESIZE_TIME = 180; + +export const RESPONSIVE_BREAKPOINTS = { + TABLET: 768, + DESKTOP: 992 +}; + +export const DEFAULT_AJAX_OPTS = { + cache: false +}; diff --git a/src/styles/$components/footer.scss b/src/styles/$components/footer.scss new file mode 100644 index 0000000..1c7476f --- /dev/null +++ b/src/styles/$components/footer.scss @@ -0,0 +1,15 @@ +.footer { + width: 100%; + height: rem(80); + align-items: center; + display: flex; + justify-content: space-between; + color: $color-white; + background-color: $color-black; + padding: 0 rem(20); + font-size: 16px; + + img { + max-width: rem(60); + } +} diff --git a/src/styles/$components/header.scss b/src/styles/$components/header.scss new file mode 100644 index 0000000..5cef4f0 --- /dev/null +++ b/src/styles/$components/header.scss @@ -0,0 +1,31 @@ +.header { + width: 100%; + padding: rem(10) rem(20); + color: $color-black; + background-color: $color-black; + display: flex; + align-items: center; + justify-content: space-between; + + img { + max-width: 60px; + } + + ul { + display: flex; + color: $color-white; + list-style: none; + padding-left: 0; + margin: 0; + + li { + padding: 0 rem(10); + + a { + color: $color-white; + font-size: rem(16); + text-decoration: none; + } + } + } +} diff --git a/src/styles/$libs.scss b/src/styles/$libs.scss new file mode 100644 index 0000000..8a43d3c --- /dev/null +++ b/src/styles/$libs.scss @@ -0,0 +1,8 @@ +@import "_utils/index"; +@import "_cores/layout"; + +@import "../../node_modules/bootstrap/scss/bootstrap"; +@import "../../node_modules/bootstrap-select/dist/css/bootstrap-select"; +@import "../../node_modules/slick-carousel/slick/slick"; +@import "../../node_modules/slick-carousel/slick/slick-theme"; +// @import "_libs/jquery.fullPage"; diff --git a/src/styles/_cores/layout.scss b/src/styles/_cores/layout.scss new file mode 100644 index 0000000..0fdd8ba --- /dev/null +++ b/src/styles/_cores/layout.scss @@ -0,0 +1,55 @@ +html { + font-family: $font-family-default; + font-size: $font-size-rem; + scroll-behavior: smooth; + // height: 100%; + height: -webkit-fill-available; + + // Fix modernizr bug + &.safari { + background: none; + } +} + +body { + position: relative; + width: 100%; + margin: 0; + font-size: $font-size-default; + font-family: $font-family-default; + --bs-body-font-family: $font-family-default; + + .main { + min-height: calc(100vh - 145px); + } + + &.freeze { + height: 100%; + overflow: hidden !important; /* stylelint-disable-line */ + + html.ios & { + position: fixed; + width: 100%; + } + + &[class*="--scroll-"] { + border-right-style: solid; + border-right-color: $color-white; + } + + @for $i from 13 through 18 { + &.\--scroll-#{$i} { + border-right-width: $i * 1px; + } + } + } +} + +img { + max-width: 100%; + + &:not([src]), + &[src=""] { + display: none; + } +} diff --git a/src/styles/_libs/.gitkeep b/src/styles/_libs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/styles/_libs/jquery.fullPage.css b/src/styles/_libs/jquery.fullPage.css new file mode 100644 index 0000000..054b7ba --- /dev/null +++ b/src/styles/_libs/jquery.fullPage.css @@ -0,0 +1,203 @@ +/** + * fullPage 2.6.6 + * https://github.com/alvarotrigo/fullPage.js + * MIT licensed + * + * Copyright (C) 2013 alvarotrigo.com - A project by Alvaro Trigo + */ +html.fp-enabled, +.fp-enabled body { + margin: 0; + padding: 0; + overflow:hidden; + + /*Avoid flicker on slides transitions for mobile phones #336 */ + -webkit-tap-highlight-color: rgba(0,0,0,0); +} +#superContainer { + height: 100%; + position: relative; + + /* Touch detection for Windows 8 */ + -ms-touch-action: none; + + /* IE 11 on Windows Phone 8.1*/ + touch-action: none; +} +.fp-section { + position: relative; + -webkit-box-sizing: border-box; /* Safari<=5 Android<=3 */ + -moz-box-sizing: border-box; /* <=28 */ + box-sizing: border-box; +} +.fp-slide { + float: left; +} +.fp-slide, .fp-slidesContainer { + height: 100%; + display: block; +} +.fp-slides { + z-index:1; + height: 100%; + overflow: hidden; + position: relative; + -webkit-transition: all 0.3s ease-out; /* Safari<=6 Android<=4.3 */ + transition: all 0.3s ease-out; +} +.fp-section.fp-table, .fp-slide.fp-table { + display: table; + table-layout:fixed; + width: 100%; +} +.fp-tableCell { + display: table-cell; + vertical-align: middle; + width: 100%; + height: 100%; +} +.fp-slidesContainer { + float: left; + position: relative; +} +.fp-controlArrow { + position: absolute; + z-index: 4; + top: 50%; + cursor: pointer; + width: 0; + height: 0; + border-style: solid; + margin-top: -38px; + -webkit-transform: translate3d(0,0,0); + -ms-transform: translate3d(0,0,0); + transform: translate3d(0,0,0); +} +.fp-controlArrow.fp-prev { + left: 15px; + width: 0; + border-width: 38.5px 34px 38.5px 0; + border-color: transparent #fff transparent transparent; +} +.fp-controlArrow.fp-next { + right: 15px; + border-width: 38.5px 0 38.5px 34px; + border-color: transparent transparent transparent #fff; +} +.fp-scrollable { + overflow: scroll; +} +.fp-notransition { + -webkit-transition: none !important; + transition: none !important; +} +#fp-nav { + position: fixed; + z-index: 100; + margin-top: -32px; + top: 50%; + opacity: 1; + -webkit-transform: translate3d(0,0,0); +} +#fp-nav.right { + right: 17px; +} +#fp-nav.left { + left: 17px; +} +.fp-slidesNav{ + position: absolute; + z-index: 4; + left: 50%; + opacity: 1; +} +.fp-slidesNav.bottom { + bottom: 17px; +} +.fp-slidesNav.top { + top: 17px; +} +#fp-nav ul, +.fp-slidesNav ul { + margin: 0; + padding: 0; +} +#fp-nav ul li, +.fp-slidesNav ul li { + display: block; + width: 14px; + height: 13px; + margin: 7px; + position:relative; +} +.fp-slidesNav ul li { + display: inline-block; +} +#fp-nav ul li a, +.fp-slidesNav ul li a { + display: block; + position: relative; + z-index: 1; + width: 100%; + height: 100%; + cursor: pointer; + text-decoration: none; +} +#fp-nav ul li a.active span, +.fp-slidesNav ul li a.active span, +#fp-nav ul li:hover a.active span, +.fp-slidesNav ul li:hover a.active span{ + height: 12px; + width: 12px; + margin: -6px 0 0 -6px; + border-radius: 100%; + } +#fp-nav ul li a span, +.fp-slidesNav ul li a span { + border-radius: 50%; + position: absolute; + z-index: 1; + height: 4px; + width: 4px; + border: 0; + background: #333; + left: 50%; + top: 50%; + margin: -2px 0 0 -2px; + -webkit-transition: all 0.1s ease-in-out; + -moz-transition: all 0.1s ease-in-out; + -o-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; +} +#fp-nav ul li:hover a span, +.fp-slidesNav ul li:hover a span{ + width: 10px; + height: 10px; + margin: -5px 0px 0px -5px; +} +#fp-nav ul li .fp-tooltip { + position: absolute; + top: -2px; + color: #fff; + font-size: 14px; + font-family: arial, helvetica, sans-serif; + white-space: nowrap; + max-width: 220px; + overflow: hidden; + display: block; + opacity: 0; + width: 0; +} +#fp-nav ul li:hover .fp-tooltip, +#fp-nav.fp-show-active a.active + .fp-tooltip { + -webkit-transition: opacity 0.2s ease-in; + transition: opacity 0.2s ease-in; + width: auto; + opacity: 1; +} +#fp-nav ul li .fp-tooltip.right { + right: 20px; +} +#fp-nav ul li .fp-tooltip.left { + left: 20px; +} diff --git a/src/styles/_utils/common.scss b/src/styles/_utils/common.scss new file mode 100644 index 0000000..932649c --- /dev/null +++ b/src/styles/_utils/common.scss @@ -0,0 +1,21 @@ +.back-to-top { + position: fixed; + bottom: 45px; + right: 45px; + z-index: 10; + cursor: pointer; + opacity: 0; + transition: opacity 0.3s ease-in-out; + + img { + max-width: 60px; + } + + @include mq('md', max) { + right: 25px; + } + + &.active { + opacity: 1; + } +} diff --git a/src/styles/_utils/functions.scss b/src/styles/_utils/functions.scss new file mode 100644 index 0000000..2c69b79 --- /dev/null +++ b/src/styles/_utils/functions.scss @@ -0,0 +1,43 @@ +@use 'sass:math'; + +$unit-range: ( + 'px': 1px, + 'em': 1em, + 'rem': 1rem, +); + +@function addUnit($value, $unit) { + @if type-of($unit) != 'string' { + @error 'Value for unit should be a string.'; + } + + $matched-unit: map-get($unit-range, $unit); + + @if $matched-unit { + @return $value * $matched-unit; + } @else { + @error 'Value for unit not a valid unit.'; + } +} + +@function px2unit($unit, $pixels, $context) { + @if $unit { + @if (unitless($pixels)) { + $pixels: $pixels * 1px; + } + + @if (unitless($context)) { + $context: $context * 1px; + } + + @return addUnit(math.div($pixels, $context), $unit); + } +} + +@function em($pixels, $context) { + @return px2unit('em', $pixels, $context); +} + +@function rem($pixels, $context: $font-size-rem) { + @return px2unit('rem', $pixels, $context); +} diff --git a/src/styles/_utils/index.scss b/src/styles/_utils/index.scss new file mode 100644 index 0000000..eead94d --- /dev/null +++ b/src/styles/_utils/index.scss @@ -0,0 +1,5 @@ +@import "variables"; +@import "override-grid"; +@import "functions"; +@import "mixins"; +@import "common"; diff --git a/src/styles/_utils/mixins.scss b/src/styles/_utils/mixins.scss new file mode 100644 index 0000000..4050086 --- /dev/null +++ b/src/styles/_utils/mixins.scss @@ -0,0 +1,182 @@ +@mixin font-face($name, $pathNotIncludeExt, $weight: normal, $style: normal) { + @font-face { + font-family: $name; + src: + url("#{$pathNotIncludeExt}.woff") format("woff"), + url("#{$pathNotIncludeExt}.woff2") format("woff2"); + font-weight: $weight; + font-style: $style; + font-display: swap; + } +} + +@mixin font-smooth { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +@function screen($name) { + @return map-get($grid-breakpoints, $name); +} + +@mixin mq($width, $type: min) { + @if map_has_key($grid-breakpoints, $width) { + $width: screen($width); + } + // @if unitless($width) { + // $width: $width * 1px + // } + + @if $type == max { + $width: $width - 1px; + } + + @media only screen and (#{$type}-width: $width) { + @content; + } +} + +@mixin ip-small() { + @media only screen and (max-width: 375px) and (max-height: 600px) { + .iphone & { + @content; + } + .chrome & { + @content; + } + } +} + +@mixin ip-medium() { + @media only screen and (max-width: 375px) and (max-height: 667px) { + .iphone & { + @content; + } + .chrome & { + @content; + } + } +} + +@mixin ip-chrome-large() { + @media only screen and (max-width: 414px) and (max-height: 720px) { + .iphone.chrome & { + @content; + } + } +} + +@mixin ip-large() { + @media only screen and (max-width: 450px) and (max-height: 844px) { + .iphone & { + @content; + } + .chrome & { + @content; + } + } +} + +@mixin android-large() { + @media only screen and (max-width: 414px) and (max-height: 896px) { + .chrome & { + @content; + } + } +} + +@mixin mm($minwidth, $maxwidth) { + @if map_has_key($grid-breakpoints, $minwidth) { + $minwidth: screen($minwidth); + } + @if map_has_key($grid-breakpoints, $maxwidth) { + $maxwidth: screen($maxwidth) - 1px; + } + @media only screen and (min-width: $minwidth) and (max-width: $maxwidth) { + @content; + } +} + +@mixin no-touch { + html.no-touch & { + @content; + } +} +@mixin hover($parentSelector: false) { + @if $parentSelector { + #{$parentSelector}:hover & { + @include no-touch { + @content; + } + } + } @else { + @include no-touch { + &:hover { + @content; + } + } + } +} + + +@mixin size($width, $height: $width) { + @if unitless($width) { + $width: rem($width * 1px) + } + + @if unitless($height) { + $height: rem($height * 1px) + } + + width: $width; + height: $height; +} + + +@mixin font-carnegie-sans-regular { + font-family: $font-family-default; + font-weight: normal; +} +@mixin font-carnegie-sans-bold { + font-family: $font-family-default; + font-weight: 700; +} +@mixin font-carnegie-sans-light { + font-family: $font-family-default; + font-weight: 300; +} + + +@mixin text-overflow { + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; +} + + +@mixin overflow-y { + overflow-x: hidden; + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} + +@mixin overflow-x { + overflow-x: auto; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: none; + scrollbar-width: none; + + &::-webkit-scrollbar { + display: none; + } +} + +@mixin hide-text-with-line($line: 4) { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: $line; + -webkit-box-orient: vertical; + white-space: initial; +} diff --git a/src/styles/_utils/override-grid.scss b/src/styles/_utils/override-grid.scss new file mode 100644 index 0000000..83724e5 --- /dev/null +++ b/src/styles/_utils/override-grid.scss @@ -0,0 +1,26 @@ +$gutters: 20px; +$half-gutters: 10px; + +$screen-xxl: 1600; +$screen-xl: 1440; +$screen-lg: 1200; +$screen-md: 992; +$screen-sm: 768; +$screen-xs: 0; +$screen-xs-max: 767px; +$screen-md-max: 991px; + +$grid-breakpoints: ( + xs: 0, + sm: 768px, + md: 992px, + lg: 1200px, + xl: 1440px, +); + +$container-max-widths: ( + sm: 720px, + md: 960px, + lg: 1134px, + xl: 1400px, +); diff --git a/src/styles/_utils/variables.scss b/src/styles/_utils/variables.scss new file mode 100644 index 0000000..d4261f5 --- /dev/null +++ b/src/styles/_utils/variables.scss @@ -0,0 +1,5 @@ +@import "variables/others"; +@import "variables/colors"; +@import "variables/fonts"; +@import "variables/z-indexes"; +@import "variables/icomoon"; diff --git a/src/styles/_utils/variables/colors.scss b/src/styles/_utils/variables/colors.scss new file mode 100644 index 0000000..a78053f --- /dev/null +++ b/src/styles/_utils/variables/colors.scss @@ -0,0 +1,2 @@ +$color-white: #FFFFFF; +$color-black: #000000; diff --git a/src/styles/_utils/variables/fonts.scss b/src/styles/_utils/variables/fonts.scss new file mode 100644 index 0000000..626cfc5 --- /dev/null +++ b/src/styles/_utils/variables/fonts.scss @@ -0,0 +1,7 @@ +$font-family-default: 'Arial', sans-serif; + +$font-size-rem: 10px; +$font-size-mobile-default: 18px; +$font-size-desktop-default: 18px; +$line-height-default: 1.5; +$font-size-default: 14px; diff --git a/src/styles/_utils/variables/icomoon.scss b/src/styles/_utils/variables/icomoon.scss new file mode 100644 index 0000000..67c679e --- /dev/null +++ b/src/styles/_utils/variables/icomoon.scss @@ -0,0 +1,33 @@ +@font-face { + font-family: 'icomoon'; + src: url('../fonts/icomoon/icomoon.eot?dr0xri'); + src: url('../fonts/icomoon/icomoon.eot?dr0xri#iefix') format('embedded-opentype'), + url('../fonts/icomoon/icomoon.ttf?dr0xri') format('truetype'), + url('../fonts/icomoon/icomoon.woff?dr0xri') format('woff'), + url('../fonts/icomoon/icomoon.svg?dr0xri#icomoon') format('svg'); + font-weight: normal; + font-style: normal; + font-display: block; +} + +[class^="icon-"], [class*=" icon-"] { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: 'icomoon' !important; + speak: never; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-arrow-down:before { + content: "\e901"; +} +.icon-infomation:before { + content: "\e900"; +} \ No newline at end of file diff --git a/src/styles/_utils/variables/others.scss b/src/styles/_utils/variables/others.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/styles/_utils/variables/z-indexes.scss b/src/styles/_utils/variables/z-indexes.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/styles/apps.scss b/src/styles/apps.scss new file mode 100644 index 0000000..e49d5ef --- /dev/null +++ b/src/styles/apps.scss @@ -0,0 +1,4 @@ +@import "_utils/index"; + +@import "$components/header"; +@import "$components/footer"; diff --git a/src/views/$modules/back-to-top.pug b/src/views/$modules/back-to-top.pug new file mode 100644 index 0000000..3c401e4 --- /dev/null +++ b/src/views/$modules/back-to-top.pug @@ -0,0 +1,2 @@ +.back-to-top(data-back-to-top) + +img('https://www.seekpng.com/png/detail/216-2162589_back-to-top-img-back-to-top.png', 'Back to top') diff --git a/src/views/$modules/footer.pug b/src/views/$modules/footer.pug new file mode 100644 index 0000000..da103fc --- /dev/null +++ b/src/views/$modules/footer.pug @@ -0,0 +1,5 @@ +footer.footer + .logo + img(src='../images/logo.png') + .copyright + span.text-copyright © 2022 Demo. All Rights Reserved. diff --git a/src/views/$modules/header.pug b/src/views/$modules/header.pug new file mode 100644 index 0000000..89df2f0 --- /dev/null +++ b/src/views/$modules/header.pug @@ -0,0 +1,12 @@ +header.header + .logo-pineapple + +link('/index.html', 'Home') + img(src='../images/logo.png') + .main-menu + ul.active-mb + li + +link('/index.html', 'Home')(class=[menuIndex === 0 && '--active' ]) + li + +link('#', 'About us')(class=[menuIndex === 2 && '--active' ]) + li + +link('#', 'Contact')(class=[menuIndex === 3 && '--active' ]) diff --git a/src/views/_blocks/favicon.pug b/src/views/_blocks/favicon.pug new file mode 100644 index 0000000..54edd85 --- /dev/null +++ b/src/views/_blocks/favicon.pug @@ -0,0 +1,12 @@ +| +| +// Start: Favicon +link(rel="apple-touch-icon" sizes="180x180" href=`${assetsPath}apple-touch-icon.png`) +link(rel="icon" type="image/png" sizes="32x32" href=`${assetsPath}favicon-32x32.png`) +link(rel="icon" type="image/png" sizes="16x16" href=`${assetsPath}favicon-16x16.png`) +link(rel="mask-icon" href="/safari-pinned-tab.svg" color="#e9662c") +meta(name="msapplication-TileColor" content="#ffc40d") +meta(name="theme-color" content="#ffffff") +// End: Favicon +| +| diff --git a/src/views/_blocks/meta.pug b/src/views/_blocks/meta.pug new file mode 100644 index 0000000..f46b050 --- /dev/null +++ b/src/views/_blocks/meta.pug @@ -0,0 +1,8 @@ +| +| +// Start: Meta +meta(name="map-key" content="") +//- meta(http-equiv="Content-Security-Policy", content="upgrade-insecure-requests") +// End: Keys +| +| diff --git a/src/views/_blocks/scripts.pug b/src/views/_blocks/scripts.pug new file mode 100644 index 0000000..03bd9c6 --- /dev/null +++ b/src/views/_blocks/scripts.pug @@ -0,0 +1,4 @@ +// build:js +script(src=`${assetsPath}js/externals.js` defer) +script(src=`${assetsPath}js/main.js` defer) +// endbuild diff --git a/src/views/_blocks/styles.pug b/src/views/_blocks/styles.pug new file mode 100644 index 0000000..1735b56 --- /dev/null +++ b/src/views/_blocks/styles.pug @@ -0,0 +1,9 @@ +| +| ++style('$libs') ++style('apps') +// endbuild +| +| + + diff --git a/src/views/_layouts/layout-component.pug b/src/views/_layouts/layout-component.pug new file mode 100644 index 0000000..7f2fddf --- /dev/null +++ b/src/views/_layouts/layout-component.pug @@ -0,0 +1,154 @@ +extends layout + +block append vars + - useHeader = false + - useFooter = false + - pageLevel = 1 + +block styles + link( + rel="stylesheet" + href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.1/styles/vs2015.min.css" + ) + + style. + .component-container { + max-width: 1600px; + padding: 0 4rem; + margin: 0 auto; + } + + @media screen and (min-width: 992px) { + .component-container { + padding: 0 4rem; + } + } + + .component-dev { + counter-reset: count; + padding: 1rem; + margin-bottom: 5rem; + } + + .component-dev > .title, + .component-dev > .block > .sub-title { + line-height: 1.1; + letter-spacing: 0; + font-weight: bold; + } + + .component-dev > .title { + font-size: 30px; + margin-bottom: 20px; + border-left: 10px solid currentColor; + padding-left: 10px; + } + + .component-dev > .block { + margin-top: 20px; + } + + .component-dev > .block > .sub-title { + font-size: 20px; + margin-bottom: 10px; + } + + .component-dev .section.\--custom-height { + height: 35vh; + } + + .component-dev > .block > .sub-title:before { + counter-increment: count; + content: counter(count) '. '; + } + + .component-dev > .block > .code-frame { + position: relative; + font-family: Consolas, monospace; + font-size: 14px; + z-index: 1; + overflow: auto; + } + + .component-dev > .block > .code-frame > textarea { + width: 100%; + height: 300px; + } + + .component-dev > .block > .code-frame > code { + padding: 20px; + } + + .component-dev > .block > .block-demo { + border: 1px dashed rgba(255, 0, 0, .3); + } + + .component.complete > .block > .block-demo { + border-color: rgba(5, 70, 22, .3); + } + + .component-dev .color-block { + width: 100%; + height: 150px; + border: 3px solid #C3C3C3; + } + + .component-dev .demo-ico em[class^="ico"], + .component-dev .demo-ico em.fa { + color: #C9BA9E; + font-size: 20px; + } + +block styles-custom + +block append scripts + script(src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.5/beautify-html.min.js") + script(src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.1/highlight.min.js") + script(src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.1/languages/htmlbars.min.js") + script. + document.querySelectorAll('.code-frame .textarea').forEach(function (element) { + var pre = document.createElement('pre'); + var code = document.createElement('code'); + var preCode = element.innerHTML + .trim() + .replace(/"\{/g, "'{") + .replace(/"\[/g, "'[") + .replace(/\}"/g, "}'") + .replace(/\]"/g, "]'") + .replace(/\<"/g, "<'") + .replace(/\'/g, "'") + .replace(/\"/g, "\"") + .replace(/"/g, "\"") + + var html = html_beautify(preCode, { + indent_size: 4, + indent_char: ' ', + break_chained_methods: true + }) + + code.innerText = html; + + code.classList.add('html'); + pre.appendChild(code); + element.insertAdjacentElement('afterend', pre); + element.parentElement.removeChild(element); + }); + + hljs.highlightAll(); + +block mixin + mixin component(title) + .component-container + .component-dev&attributes(attributes) + h1.h1.title!=title + + .block + h2.sub-title Demo + .block-demo + block + + .block + h2.sub-title Code Frame + .code-frame + .textarea + block diff --git a/src/views/_layouts/layout.pug b/src/views/_layouts/layout.pug new file mode 100644 index 0000000..f64cb10 --- /dev/null +++ b/src/views/_layouts/layout.pug @@ -0,0 +1,81 @@ +include ../_mixins/all + +block vars + - let useHeader = true + - let useFooter = true + - let useBackToTop = true + - let footerSpace = false + - let title = 'Layout' + - let namespace = '' + - let menuIndex = -1 + - let pageLevel = 0 + - let classMain = '' + - let titleOrg = '' + - let descOrg = '' + - let imgOrg = '' + - let urlOrg = '' + +- const rootAssetsPath = $localeName ? '../' : '' +- const _levelPath = [...Array(pageLevel)].map(() => '..').join('/') +- const _trailDash = pageLevel ? '/' : '' +- const levelPath = _levelPath + _trailDash +- const assetsPath = rootAssetsPath + levelPath +- const isRTL = $translator.isRTL +- const dir = isRTL ? 'rtl' : 'ltr' +- const rtlSuffix = isRTL ? '-rtl' : '' +- const pagePath = $path; +- + const viewports = [ + 'width=device-width', + 'initial-scale=1', + 'shrink-to-fit=no', + 'user-scalable=0' + ].join(',') + +block const + +- + const translateAssetPath = (path, useLevel) => { + const isExternalPath = /^(https?:)?\/\//.test(path) + + return isExternalPath + ? path + : (useLevel ? levelPath : assetsPath) + (path.replace ? path.replace(/^\//, '') : '') + } + +block mixin + +doctype html +html(lang=$localeName dir=dir) + head + title=title + meta(http-equiv="X-UA-Compatible" content="IE=edge") + meta(charset="utf-8") + meta(name="viewport" content!=viewports) + + include ../_blocks/meta + include ../_blocks/favicon + include ../_blocks/styles + + block styles + block styles-custom + + body(class=namespace) + if useHeader + include ../$modules/header + + - var mainClass = useHeader ? classList : '' + .main(class!=classMain) + block container + + if useFooter + include ../$modules/footer + + if useBackToTop + include ../$modules/back-to-top + + block scripts + script. + var staticJsAssetsPath = '#{assetsPath}js/'; + + include ../_blocks/scripts diff --git a/src/views/_mixins/all.pug b/src/views/_mixins/all.pug new file mode 100644 index 0000000..d6451e2 --- /dev/null +++ b/src/views/_mixins/all.pug @@ -0,0 +1,7 @@ +- imageTransparent = 'images/transparent.png' + +include common/style +include common/link +include common/img +include common/nav +include common/cta diff --git a/src/views/_mixins/common/cta.pug b/src/views/_mixins/common/cta.pug new file mode 100644 index 0000000..51f4d15 --- /dev/null +++ b/src/views/_mixins/common/cta.pug @@ -0,0 +1,24 @@ +mixin cta(href = '#', title) + +link(href, title).cta-primary&attributes(attributes) + span!=title + + +mixin cta-button(title) + button(type="button").button.cta-primary&attributes(attributes) + span!=title + +mixin cta-link(href='#', title) + +link(href, title).cta-link&attributes(attributes) + span!=title + span.icon-arrow-right-thin + +mixin cta-radio-button(name, title) + label.cta-primary.cta-radio-button + input(type="radio" name=name)&attributes(attributes) + span!=title + +mixin cta-block-link(href='#', title) + .cta-block-link + +link(href, title)&attributes(attributes) + span!=title + span.icon-arrow-down diff --git a/src/views/_mixins/common/img.pug b/src/views/_mixins/common/img.pug new file mode 100644 index 0000000..8286f5c --- /dev/null +++ b/src/views/_mixins/common/img.pug @@ -0,0 +1,13 @@ +mixin img(src = imageTransparent, alt) + - const _src = translateAssetPath(src) + - const _alt = alt || _src.slice(_src.lastIndexOf('/') + 1) + img(src=_src alt=_alt)&attributes(attributes) + +mixin picture(sources, alt) + picture&attributes(picAttrs) + each source, breakpoint in sources + - const src = typeof source === 'string' ? source : (source.src || imageTransparent) + if breakpoint === 'default' + +img(src, alt)&attributes(attributes) + else + source(media=`(max-width: ${breakpoint}px)` srcset=translateAssetPath(src) type=source.type) diff --git a/src/views/_mixins/common/link.pug b/src/views/_mixins/common/link.pug new file mode 100644 index 0000000..badd74a --- /dev/null +++ b/src/views/_mixins/common/link.pug @@ -0,0 +1,8 @@ +mixin link(href = '#', title, text = title) + - const _href = href !== '#' ? translateAssetPath(href, true) : href + - const _title = title || _href.slice(_href.lastIndexOf('/') + 1) + a(href=_href title=_title)&attributes(attributes) + if block + block + else + !=text diff --git a/src/views/_mixins/common/nav.pug b/src/views/_mixins/common/nav.pug new file mode 100644 index 0000000..4dad953 --- /dev/null +++ b/src/views/_mixins/common/nav.pug @@ -0,0 +1,23 @@ +mixin nav(items = []) + ul.navigation&attributes(attributes) + each item, index in items + li.navigation-item( + class= item.isActive ? '--active' : '' + ) + +link(item.href, item.title, item.text) + + if item.itemsMenu2 && item.itemsMenu2[0] + span.icon.icon-plus + span.icon-close + ul.navigation-level2 + each item2, index in item.itemsMenu2 + li.navigation-item + +link(item2.href, item2.title, item2.text) + + if item2.itemsMenu3 && item2.itemsMenu3[0] + span.icon.icon-plus + span.icon.icon-close + ul.navigation-level3 + each item3, index in item2.itemsMenu3 + li.navigation-item + +link(item3.href, item3.title, item3.text) diff --git a/src/views/_mixins/common/style.pug b/src/views/_mixins/common/style.pug new file mode 100644 index 0000000..a12cf54 --- /dev/null +++ b/src/views/_mixins/common/style.pug @@ -0,0 +1,2 @@ +mixin style(name) + link(rel="stylesheet" href=`${assetsPath}css/${name + rtlSuffix}.css`) diff --git a/src/views/components/footer.pug b/src/views/components/footer.pug new file mode 100644 index 0000000..b77ce25 --- /dev/null +++ b/src/views/components/footer.pug @@ -0,0 +1,8 @@ +extends ../_layouts/layout-component + +block append vars + - title = 'Footer' + +block container + +component('Footer') + include ../$modules/footer diff --git a/src/views/components/header.pug b/src/views/components/header.pug new file mode 100644 index 0000000..7ee0e12 --- /dev/null +++ b/src/views/components/header.pug @@ -0,0 +1,8 @@ +extends ../_layouts/layout-component + +block append vars + - title = 'Header' + +block container + +component('Header') + include ../$modules/header diff --git a/src/views/index.pug b/src/views/index.pug new file mode 100644 index 0000000..2535cdf --- /dev/null +++ b/src/views/index.pug @@ -0,0 +1,10 @@ +extends _layouts/layout + +block append vars + - title = 'Homepage' + - classMain = 'homepage' + +block container + section + .container + h1 Test diff --git a/src/views/sitemap.pug b/src/views/sitemap.pug new file mode 100644 index 0000000..d4b9869 --- /dev/null +++ b/src/views/sitemap.pug @@ -0,0 +1,138 @@ +extends _layouts/layout + +block append vars + - title = 'Sitemap' + - useHeader = false + - useFooter = false + +block append container + mixin blank(title, href = 'javascript:void(0)', target = "_self") + .blank + +link(href, title)(target=target).link + span.text=title + + .container-fluid + h1.text-center SITEMAP + + .block + h2 Pages + .clearfix + +blank('Homepage', '/index.html') + + + + + style. + body { + background: #eee; + overflow-x: hidden; + padding: 0 20px; + font-size: 16px; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + line-height: 1.1; + letter-spacing: 0; + font-weight: bold; + } + + h1 { + font-size: 2.5em; + margin: 1rem 0; + } + + h2 { + font-size: 1.5em; + text-transform: uppercase; + margin-bottom: 1rem; + border-left: .5rem solid black; + padding-left: 1rem; + } + + h3 { + font-size: 1.2em; + text-transform: uppercase; + margin-bottom: 1rem; + border-left: 3px solid black; + padding-left: 1rem; + } + + h4 { + font-size: 1em; + } + + h5 { + font-size: 0.83em; + } + + h6 { + font-size: 0.67em; + } + .clearfix { + overflow: hidden; + } + + .text-center { + text-align: center; + } + + .block { + margin-bottom: 2rem; + } + + @media screen and (min-width: 992px) { + .block { + margin-bottom: 5rem; + } + } + + .block .clearfix { + counter-reset: link; + } + + .blank { + float: left; + width: 100%; + padding: 0 .5rem; + } + + @media screen and (min-width: 992px) { + .blank { + width: calc(20% - 3px); + } + } + + .link { + display: block; + position: relative; + margin-bottom: 1rem; + padding: .7rem 1rem; + text-decoration: none; + border: 1px solid rgba(0,0,0,.2); + font-size: .85em; + background: #fff; + color: inherit; + box-shadow: 0 0 2px rgba(0,0,0,0); + transform: translateY(0); + transition: all .1s ease; + } + + .link:hover, + .link:focus { + text-decoration: none; + border-color: rgba(0,0,0,.5); + color: inherit; + box-shadow: 0 10px 10px -4px rgba(0,0,0,.1); + } + + .link::before { + counter-increment: link; + content: counter(link) "."; + font-weight: bold; + margin-right: 5px; + }