├── examples └── demo │ ├── dom-scheduler.js │ ├── node_modules │ ├── fxos-font │ │ ├── .npmignore │ │ ├── fonts │ │ │ ├── FiraSans-Bold.woff │ │ │ ├── FiraSans-Light.woff │ │ │ ├── FiraSans-Medium.woff │ │ │ ├── FiraSans-Regular.woff │ │ │ ├── FiraSans-BoldItalic.woff │ │ │ ├── FiraSans-LightItalic.woff │ │ │ ├── FiraSans-MediumItalic.woff │ │ │ └── FiraSans-RegularItalic.woff │ │ ├── bower.json │ │ ├── fxos-font.css │ │ └── package.json │ ├── fxos-icons │ │ ├── fonts │ │ │ └── fxos-icons.ttf │ │ ├── fxos-icons.js │ │ ├── fxos-icons.css │ │ ├── bidi-helper.css │ │ ├── package.json │ │ └── README.md │ ├── fxos-component │ │ ├── webpack.config.js │ │ ├── package.json │ │ └── README.md │ ├── fxos-text-input │ │ ├── README.md │ │ ├── package.json │ │ ├── src │ │ │ ├── fxos-text-input-multiline.js │ │ │ ├── fxos-text-input-pin.js │ │ │ └── fxos-text-input.js │ │ ├── fxos-text-input-multiline.js │ │ ├── fxos-text-input-pin.js │ │ └── fxos-text-input.js │ ├── fxos-theme │ │ ├── README.md │ │ ├── package.json │ │ ├── lib │ │ │ └── fxos-theme-selector.js │ │ └── fxos-theme.css │ ├── fxos-dialog │ │ ├── src │ │ │ ├── fxos-dialog-alert.js │ │ │ ├── fxos-dialog-confirm.js │ │ │ ├── fxos-dialog-prompt.js │ │ │ ├── fxos-dialog-menu.js │ │ │ ├── fxos-dialog-action.js │ │ │ ├── fxos-dialog-select.js │ │ │ └── fxos-dialog.js │ │ ├── package.json │ │ ├── fxos-dialog-alert.js │ │ ├── fxos-dialog-confirm.js │ │ ├── README.md │ │ ├── fxos-dialog-menu.js │ │ ├── fxos-dialog-action.js │ │ └── fxos-dialog-select.js │ └── fastlist │ │ ├── package.json │ │ └── README.md │ ├── sections │ ├── dom-scheduler.js │ ├── icons │ │ ├── icon128x128.png │ │ ├── icon16x16.png │ │ ├── icon48x48.png │ │ └── icon60x60.png │ ├── package.json │ ├── manifest.webapp │ ├── css │ │ ├── h1.css │ │ ├── app.css │ │ └── ul.css │ ├── index.html │ └── js │ │ ├── page.js │ │ └── bacon-source.js │ ├── icons │ ├── icon16x16.png │ ├── icon48x48.png │ ├── icon60x60.png │ └── icon128x128.png │ ├── package.json │ ├── manifest.webapp │ ├── css │ ├── h1.css │ ├── app.css │ └── ul.css │ ├── index.html │ └── js │ ├── app.js │ └── bacon-source.js ├── .gitignore ├── .npmignore ├── bower.json ├── .jshintrc ├── .travis.yml ├── package.json ├── test └── karma.conf.js ├── README.md └── dom-scheduler.js /examples/demo/dom-scheduler.js: -------------------------------------------------------------------------------- 1 | ../../dom-scheduler.js -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-font/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /examples/demo/sections/dom-scheduler.js: -------------------------------------------------------------------------------- 1 | ../../dom-scheduler.js -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | !examples/demo/node_modules 3 | bower_components 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | **/.* 2 | webpack.config.js 3 | bower_components 4 | examples 5 | bower.json 6 | index.html 7 | test 8 | -------------------------------------------------------------------------------- /examples/demo/icons/icon16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/icons/icon16x16.png -------------------------------------------------------------------------------- /examples/demo/icons/icon48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/icons/icon48x48.png -------------------------------------------------------------------------------- /examples/demo/icons/icon60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/icons/icon60x60.png -------------------------------------------------------------------------------- /examples/demo/icons/icon128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/icons/icon128x128.png -------------------------------------------------------------------------------- /examples/demo/sections/icons/icon128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/sections/icons/icon128x128.png -------------------------------------------------------------------------------- /examples/demo/sections/icons/icon16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/sections/icons/icon16x16.png -------------------------------------------------------------------------------- /examples/demo/sections/icons/icon48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/sections/icons/icon48x48.png -------------------------------------------------------------------------------- /examples/demo/sections/icons/icon60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/sections/icons/icon60x60.png -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-icons/fonts/fxos-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/node_modules/fxos-icons/fonts/fxos-icons.ttf -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-font/fonts/FiraSans-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/node_modules/fxos-font/fonts/FiraSans-Bold.woff -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-font/fonts/FiraSans-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/node_modules/fxos-font/fonts/FiraSans-Light.woff -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-font/fonts/FiraSans-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/node_modules/fxos-font/fonts/FiraSans-Medium.woff -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-font/fonts/FiraSans-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/node_modules/fxos-font/fonts/FiraSans-Regular.woff -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-font/fonts/FiraSans-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/node_modules/fxos-font/fonts/FiraSans-BoldItalic.woff -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-font/fonts/FiraSans-LightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/node_modules/fxos-font/fonts/FiraSans-LightItalic.woff -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-font/fonts/FiraSans-MediumItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/node_modules/fxos-font/fonts/FiraSans-MediumItalic.woff -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-font/fonts/FiraSans-RegularItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxos-components/dom-scheduler/HEAD/examples/demo/node_modules/fxos-font/fonts/FiraSans-RegularItalic.woff -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-component/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './src/fxos-component.js', 3 | output: { 4 | filename: 'fxos-component.js', 5 | library: 'fxos-component' 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-font/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fxos-font", 3 | "authors": [ 4 | "Wilson Page " 5 | ], 6 | "main": "fxos-font.css", 7 | "license": "MIT", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "test", 13 | "tests" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /examples/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "1.0.0", 4 | "description": "dom-scheduler demo", 5 | "main": "index.html", 6 | "license": "ISC", 7 | "dependencies": { 8 | "fastlist": "^1.0.2", 9 | "fxos-dialog": "^2.0.1", 10 | "fxos-font": "^1.0.0", 11 | "fxos-icons": "^2.0.0", 12 | "fxos-theme": "^2.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-text-input/README.md: -------------------------------------------------------------------------------- 1 | # <fxos-text-input> [![](https://travis-ci.org/fxos-components/fxos-text-input.svg)](https://travis-ci.org/fxos-components/fxos-text-input) 2 | 3 | ## Installation 4 | 5 | ```bash 6 | $ npm install fxos-text-input 7 | ``` 8 | 9 | ## Examples 10 | 11 | - [Example](http://fxos-components.github.io/fxos-text-input/) 12 | -------------------------------------------------------------------------------- /examples/demo/sections/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "1.0.0", 4 | "description": "dom-scheduler demo", 5 | "main": "index.html", 6 | "license": "ISC", 7 | "dependencies": { 8 | "fastlist": "^1.0.2", 9 | "fxos-dialog": "^2.0.1", 10 | "fxos-font": "^1.0.0", 11 | "fxos-icons": "^2.0.0", 12 | "fxos-theme": "^2.0.0", 13 | "popel": "^1.0.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/demo/manifest.webapp: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dom-scheduler-demo", 3 | "description": "A Hello World app", 4 | "launch_path": "/index.html", 5 | "icons": { 6 | "16": "/icons/icon16x16.png", 7 | "48": "/icons/icon48x48.png", 8 | "60": "/icons/icon60x60.png", 9 | "128": "/icons/icon128x128.png" 10 | }, 11 | "type": "certified", 12 | "orientation": "default", 13 | "permissions": {} 14 | } 15 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-theme/README.md: -------------------------------------------------------------------------------- 1 | # <fxos-switch> ![](https://travis-ci.org/fxos-components/fxos-switch.svg) 2 | 3 | ## Installation 4 | 5 | ```bash 6 | $ npm install fxos-switch 7 | ``` 8 | 9 | ```html 10 | 11 | 12 | 13 | ``` 14 | -------------------------------------------------------------------------------- /examples/demo/sections/manifest.webapp: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastlist/sections", 3 | "description": "Faaaaasssst", 4 | "launch_path": "/index.html", 5 | "icons": { 6 | "16": "/icons/icon16x16.png", 7 | "48": "/icons/icon48x48.png", 8 | "60": "/icons/icon60x60.png", 9 | "128": "/icons/icon128x128.png" 10 | }, 11 | "type": "certified", 12 | "orientation": "default", 13 | "permissions": {} 14 | } 15 | -------------------------------------------------------------------------------- /examples/demo/css/h1.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | position: absolute; 3 | top: 0; 4 | left: 0; 5 | 6 | width: 100%; 7 | margin: 0; 8 | padding: 0; 9 | background: #27c8c2; 10 | 11 | text-align: center; 12 | line-height: 50px; 13 | font-weight: 300; 14 | font-style: italic; 15 | font-size: 24px; 16 | color: #ffffff; 17 | 18 | overflow: hidden; 19 | white-space: nowrap; 20 | text-overflow: ellipsis; 21 | } 22 | -------------------------------------------------------------------------------- /examples/demo/sections/css/h1.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | position: absolute; 3 | top: 0; 4 | left: 0; 5 | 6 | width: 100%; 7 | margin: 0; 8 | padding: 0; 9 | background: #27c8c2; 10 | 11 | text-align: center; 12 | line-height: 50px; 13 | font-weight: 300; 14 | font-style: italic; 15 | font-size: 24px; 16 | color: #ffffff; 17 | 18 | overflow: hidden; 19 | white-space: nowrap; 20 | text-overflow: ellipsis; 21 | } 22 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dom-scheduler", 3 | "homepage": "https://github.com/fxos-components/dom-scheduler", 4 | "authors": [ 5 | "Etienne Segonzac " 6 | ], 7 | "description": "Making the performant thing the easy thing.", 8 | "version": "2.1.0", 9 | "main": "dom-scheduler.js", 10 | "keywords": [ 11 | "web", 12 | "performance", 13 | "transition", 14 | "animation", 15 | "promise" 16 | ], 17 | "license": "MPL", 18 | "ignore": [ 19 | "**/.*", 20 | "node_modules", 21 | "examples", 22 | "test" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "camelcase": false, 3 | "curly": true, 4 | "forin": false, 5 | "latedef": "nofunc", 6 | "newcap": false, 7 | "noarg": true, 8 | "node": true, 9 | "nonew": true, 10 | "quotmark": "single", 11 | "undef": true, 12 | "unused": "vars", 13 | "strict": true, 14 | "trailing": true, 15 | "maxlen": 80, 16 | 17 | "eqnull": true, 18 | "esnext": true, 19 | "expr": true, 20 | "globalstrict": true, 21 | 22 | "maxerr": 1000, 23 | "regexdash": true, 24 | "laxcomma": true, 25 | "proto": true, 26 | 27 | "browser": true, 28 | "devel": true, 29 | "nonstandard": true, 30 | "worker": true, 31 | 32 | "-W078": true, 33 | "predef": ["performance"] 34 | } 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - '5.1.0' 5 | 6 | addons: 7 | firefox: '40.0.2' 8 | 9 | before_install: 10 | - wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb 11 | - sudo apt-get update -y 12 | - sudo apt-get install libappindicator1 fonts-liberation 13 | - sudo dpkg -i google-chrome-*.deb 14 | - export CHROME_BIN=`which google-chrome` 15 | 16 | install: 17 | - npm install 18 | 19 | before_script: 20 | - export DISPLAY=:99.0 21 | - sh -e /etc/init.d/xvfb start 22 | 23 | script: 24 | - $CI_ACTION 25 | 26 | env: 27 | global: 28 | - TEST_SUITE=unit 29 | matrix: 30 | - CI_ACTION="npm run lint" 31 | - CI_ACTION="npm test" 32 | 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dom-scheduler", 3 | "main": "dom-scheduler.js", 4 | "description": "Making the performant thing the easy thing.", 5 | "devDependencies": { 6 | "jshint": "^2.9.1-rc1", 7 | "karma": "^0.13.15", 8 | "karma-chrome-launcher": "^0.2.2", 9 | "karma-firefox-launcher": "^0.1.4", 10 | "karma-mocha": "^0.2.1", 11 | "karma-sinon-chai": "^0.3.0", 12 | "mocha": "^2.3.4" 13 | }, 14 | "license": "MPL", 15 | "scripts": { 16 | "lint": "jshint dom-scheduler.js", 17 | "test": "karma start test/karma.conf.js --single-run", 18 | "test-dev": "karma start test/karma.conf.js" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git://github.com/fxos-components/dom-scheduler.git" 23 | }, 24 | "bugs": { 25 | "url": "https://github.com/fxos-components/dom-scheduler/issues" 26 | }, 27 | "version": "2.1.0" 28 | } 29 | -------------------------------------------------------------------------------- /examples/demo/sections/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | fastlist 20 | 21 | 22 |

Main List

23 |
24 | 25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-icons/fxos-icons.js: -------------------------------------------------------------------------------- 1 | (function(define){'use strict';define(function(require,exports,module){ 2 | 3 | /** 4 | * Exports 5 | */ 6 | 7 | var base = window.FXOS_ICONS_BASE_URL 8 | || window.COMPONENTS_BASE_URL 9 | || 'node_modules/'; 10 | 11 | // Load it! 12 | if (!document.documentElement) addEventListener('load', load); 13 | else load(); 14 | 15 | function load() { 16 | if (isLoaded()) return; 17 | var link = document.createElement('link'); 18 | link.rel = 'stylesheet'; 19 | link.type = 'text/css'; 20 | link.href = base + 'fxos-icons/fxos-icons.css'; 21 | document.head.appendChild(link); 22 | exports.loaded = true; 23 | } 24 | 25 | function isLoaded() { 26 | return exports.loaded 27 | || document.querySelector('link[href*=fxos-icons]') 28 | || document.documentElement.classList.contains('fxos-icons-loaded'); 29 | } 30 | 31 | });})(typeof define=='function'&&define.amd?define:(function(n,w){'use strict';return typeof module=='object'?function(c){c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){return w[n];},m.exports,m);w[n]=m.exports;};})('fxos-icons',this));/*jshint ignore:line*/ 32 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-icons/fxos-icons.css: -------------------------------------------------------------------------------- 1 | /* Generated by grunt-webfont */ 2 | /* Based on https://github.com/endtwist/fontcustom/blob/master/lib/fontcustom/templates/fontcustom.css */ 3 | 4 | @font-face { 5 | font-family: "fxos-icons"; 6 | src: url("fonts/fxos-icons.ttf") format("truetype"); 7 | font-weight: 500; 8 | font-style: normal; 9 | } 10 | 11 | [data-icon]:before, 12 | .ligature-icons { 13 | font-family: "fxos-icons"; 14 | content: attr(data-icon); 15 | display: inline-block; 16 | font-weight: 500; 17 | font-style: normal; 18 | text-decoration: inherit; 19 | text-transform: none; 20 | text-rendering: optimizeLegibility; 21 | font-size: 30px; 22 | -webkit-font-smoothing: antialiased; 23 | } 24 | 25 | [data-icon]:not([data-l10n-id]):not([aria-label]):not([aria-hidden="true"]):before { 26 | /* Highlight cases where icons are not localized and still visible to screen 27 | reader */ 28 | background: rgba(255, 0, 0, 0.4); 29 | } 30 | 31 | [data-icon]:not([data-l10n-id]):not([aria-label]):not([aria-hidden="true"]):after { 32 | background: #333; 33 | color: #fff; 34 | position: absolute; 35 | width: 20rem; 36 | border-radius: 0.5rem; 37 | padding: 0.5rem; 38 | content: "Icon is not localized and is still visible to screen reader. Please add data-l10n-id or aria-label for localization or use aria-hidden to hide the icon from screen reader."; 39 | z-index: 9999; 40 | } 41 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-dialog/src/fxos-dialog-alert.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Dependencies 4 | */ 5 | 6 | var GaiaDialogProto = require('./fxos-dialog').prototype; 7 | var component = require('fxos-component'); 8 | 9 | /** 10 | * Exports 11 | */ 12 | module.exports = component.register('fxos-dialog-alert', { 13 | created: function() { 14 | this.setupShadowRoot(); 15 | this.els = { 16 | dialog: this.shadowRoot.querySelector('fxos-dialog') 17 | }; 18 | this.els.dialog.addEventListener('closed', 19 | GaiaDialogProto.hide.bind(this)); 20 | }, 21 | 22 | open: function(e) { 23 | return GaiaDialogProto.show.call(this) 24 | .then(() => this.els.dialog.open(e)); 25 | }, 26 | 27 | close: function() { 28 | return this.els.dialog.close() 29 | .then(GaiaDialogProto.hide.bind(this)); 30 | }, 31 | 32 | template: ` 33 | 34 |
35 |

36 |
37 |
38 | 39 |
40 |
41 | ` 57 | }); 58 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-font/fxos-font.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face { 3 | font-family: "FiraSans"; 4 | src: url("fonts/FiraSans-Light.woff") format("woff"); 5 | font-weight: 300; /* lighter */ 6 | } 7 | 8 | @font-face { 9 | font-family: "FiraSans"; 10 | src: url("fonts/FiraSans-LightItalic.woff") format("woff"); 11 | font-style: italic; 12 | font-weight: 300; /* lighter */ 13 | } 14 | 15 | @font-face { 16 | font-family: "FiraSans"; 17 | src: url("fonts/FiraSans-Regular.woff") format("woff"); 18 | font-style: normal; 19 | font-weight: normal; /* 400 */ 20 | } 21 | 22 | @font-face { 23 | font-family: "FiraSans"; 24 | src: url("fonts/FiraSans-RegularItalic.woff") format("woff"); 25 | font-style: italic; 26 | font-weight: normal; /* 400 */ 27 | } 28 | 29 | @font-face { 30 | font-family: "FiraSans"; 31 | src: url("fonts/FiraSans-Medium.woff") format("woff"); 32 | font-style: italic; 33 | font-weight: bold; /* 700 */ 34 | } 35 | 36 | @font-face { 37 | font-family: "FiraSans"; 38 | src: url("fonts/FiraSans-MediumItalic.woff") format("woff"); 39 | font-style: italic; 40 | font-weight: bold; /* 700 */ 41 | } 42 | 43 | @font-face { 44 | font-family: "FiraSans"; 45 | src: url("fonts/FiraSans-Bold.woff") format("woff"); 46 | font-weight: 800; /* bolder */ 47 | } 48 | 49 | @font-face { 50 | font-family: "FiraSans"; 51 | src: url("fonts/FiraSans-BoldItalic.woff") format("woff"); 52 | font-style: italic; 53 | font-weight: 800; /* bolder */ 54 | } 55 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-dialog/src/fxos-dialog-confirm.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Dependencies 4 | */ 5 | 6 | var GaiaDialogProto = require('./fxos-dialog').prototype; 7 | var component = require('fxos-component'); 8 | 9 | /** 10 | * Exports 11 | */ 12 | module.exports = component.register('fxos-dialog-confirm', { 13 | created: function() { 14 | this.setupShadowRoot(); 15 | 16 | this.els = { 17 | dialog: this.shadowRoot.querySelector('fxos-dialog'), 18 | submit: this.shadowRoot.querySelector('.submit'), 19 | cancel: this.shadowRoot.querySelector('.cancel') 20 | }; 21 | 22 | this.els.cancel.addEventListener('click', this.close.bind(this)); 23 | this.els.submit.addEventListener('click', this.close.bind(this)); 24 | }, 25 | 26 | open: function(e) { 27 | return GaiaDialogProto.show.call(this) 28 | .then(() => this.els.dialog.open(e)); 29 | }, 30 | 31 | close: function() { 32 | // First close (hide) inner dialog and then the container. 33 | return this.els.dialog.close() 34 | .then(GaiaDialogProto.hide.bind(this)); 35 | }, 36 | 37 | template: ` 38 | 39 |
40 |

41 |
42 |
43 | 44 | 45 |
46 |
47 | 48 | ` 65 | }); 66 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-font/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "fxos-font", 5 | "/Users/wilsonpage/Documents/f/fxos-components/dom-scheduler/examples/demo" 6 | ] 7 | ], 8 | "_from": "fxos-font@*", 9 | "_id": "fxos-font@1.0.0", 10 | "_inCache": true, 11 | "_installable": true, 12 | "_location": "/fxos-font", 13 | "_nodeVersion": "5.1.0", 14 | "_npmUser": { 15 | "email": "wilsonpage@me.com", 16 | "name": "wilsonpage" 17 | }, 18 | "_npmVersion": "3.3.12", 19 | "_phantomChildren": {}, 20 | "_requested": { 21 | "name": "fxos-font", 22 | "raw": "fxos-font", 23 | "rawSpec": "", 24 | "scope": null, 25 | "spec": "*", 26 | "type": "range" 27 | }, 28 | "_requiredBy": [ 29 | "/" 30 | ], 31 | "_resolved": "https://registry.npmjs.org/fxos-font/-/fxos-font-1.0.0.tgz", 32 | "_shasum": "6b92ad926cc11d66980c5ada90baaffe70c70f63", 33 | "_shrinkwrap": null, 34 | "_spec": "fxos-font", 35 | "_where": "/Users/wilsonpage/Documents/f/fxos-components/dom-scheduler/examples/demo", 36 | "author": "", 37 | "dependencies": {}, 38 | "description": "", 39 | "devDependencies": {}, 40 | "directories": {}, 41 | "dist": { 42 | "shasum": "6b92ad926cc11d66980c5ada90baaffe70c70f63", 43 | "tarball": "http://registry.npmjs.org/fxos-font/-/fxos-font-1.0.0.tgz" 44 | }, 45 | "gitHead": "24dc885c34d2ee4baa77296a5a5c8e45554e8ade", 46 | "license": "ISC", 47 | "main": "fxos-font.css", 48 | "maintainers": [ 49 | { 50 | "name": "wilsonpage", 51 | "email": "wilsonpage@me.com" 52 | } 53 | ], 54 | "name": "fxos-font", 55 | "optionalDependencies": {}, 56 | "readme": "ERROR: No README data found!", 57 | "scripts": { 58 | "test": "echo \"Error: no test specified\" && exit 1" 59 | }, 60 | "version": "1.0.0" 61 | } 62 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-icons/bidi-helper.css: -------------------------------------------------------------------------------- 1 | /* Some BiDi-proof icons 2 | * In the future, we might handle this at the font level, see 3 | * https://bugzilla.mozilla.org/show_bug.cgi?id=1214127 4 | */ 5 | 6 | [data-icon="back"]:-moz-dir(ltr):before { 7 | content: 'left'; 8 | } 9 | [data-icon="back"]:-moz-dir(rtl):before { 10 | content: 'right'; 11 | } 12 | 13 | [data-icon="forward"]:-moz-dir(ltr):before { 14 | content: 'right'; 15 | } 16 | [data-icon="forward"]:-moz-dir(rtl):before { 17 | content: 'left'; 18 | } 19 | 20 | [data-icon="back-light"]:-moz-dir(ltr):before { 21 | content: 'left-light'; 22 | } 23 | [data-icon="back-light"]:-moz-dir(rtl):before { 24 | content: 'right-light'; 25 | } 26 | 27 | [data-icon="forward-light"]:-moz-dir(ltr):before { 28 | content: 'right-light'; 29 | } 30 | [data-icon="forward-light"]:-moz-dir(rtl):before { 31 | content: 'left-light'; 32 | } 33 | 34 | [data-icon="arrow-back"]:-moz-dir(ltr):before { 35 | content: 'arrow-left'; 36 | } 37 | [data-icon="arrow-back"]:-moz-dir(rtl):before { 38 | content: 'arrow-right'; 39 | } 40 | 41 | [data-icon="arrow-forward"]:-moz-dir(ltr):before { 42 | content: 'arrow-right'; 43 | } 44 | [data-icon="arrow-forward"]:-moz-dir(rtl):before { 45 | content: 'arrow-left'; 46 | } 47 | 48 | [data-icon="clear-input"]:-moz-dir(ltr):before { 49 | content: 'clear-input-left'; 50 | } 51 | [data-icon="clear-input"]:-moz-dir(rtl):before { 52 | content: 'clear-input-right'; 53 | } 54 | 55 | [data-icon="expand"]:-moz-dir(ltr):before { 56 | content: 'expand-left'; 57 | } 58 | [data-icon="expand"]:-moz-dir(rtl):before { 59 | content: 'expand-right'; 60 | } 61 | 62 | [data-icon="send"]:-moz-dir(ltr):before { 63 | content: 'send-right'; 64 | } 65 | [data-icon="send"]:-moz-dir(rtl):before { 66 | content: 'send-left'; 67 | } 68 | 69 | [data-icon="edit"]:-moz-dir(ltr):before { 70 | content: 'edit-left'; 71 | } 72 | [data-icon="edit"]:-moz-dir(rtl):before { 73 | content: 'edit-right'; 74 | } 75 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-dialog/src/fxos-dialog-prompt.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Dependencies 4 | */ 5 | 6 | var GaiaDialogProto = require('./fxos-dialog').prototype; 7 | var component = require('fxos-component'); 8 | 9 | require('fxos-text-input'); 10 | 11 | /** 12 | * Exports 13 | */ 14 | module.exports = component.register('fxos-dialog-prompt', { 15 | created: function() { 16 | this.setupShadowRoot(); 17 | 18 | this.els = { 19 | dialog: this.shadowRoot.querySelector('fxos-dialog'), 20 | input: this.shadowRoot.querySelector('fxos-text-input'), 21 | submit: this.shadowRoot.querySelector('.submit'), 22 | cancel: this.shadowRoot.querySelector('.cancel') 23 | }; 24 | 25 | this.els.dialog.addEventListener('opened', () => { 26 | this.setAttribute('opened', ''); 27 | }); 28 | 29 | this.els.dialog.addEventListener('closed', () => { 30 | this.removeAttribute('opened'); 31 | }); 32 | 33 | this.els.input.placeholder = this.firstChild.textContent; 34 | this.els.cancel.addEventListener('click', this.close.bind(this)); 35 | this.els.submit.addEventListener('click', this.close.bind(this)); 36 | }, 37 | 38 | open: function(e) { 39 | return GaiaDialogProto.show.call(this) 40 | .then(() => this.els.dialog.open(e)); 41 | }, 42 | 43 | close: function() { 44 | return this.els.dialog.close() 45 | .then(GaiaDialogProto.hide.bind(this)); 46 | }, 47 | 48 | template: ` 49 | 50 |
51 |
52 | 53 | 54 |
55 |
56 | ` 70 | }); 71 | -------------------------------------------------------------------------------- /examples/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | dom-scheduler demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 38 | 39 | 40 |

Main List

41 |
42 | 45 |
46 |
    47 |

    52 |
  • ↕︎
  • 58 |
59 |
60 | List item clicked! 61 | 62 | 63 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-theme/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "fxos-theme", 5 | "/Users/wilsonpage/Documents/f/fxos-components/dom-scheduler/examples/demo" 6 | ] 7 | ], 8 | "_from": "fxos-theme@*", 9 | "_id": "fxos-theme@2.0.0", 10 | "_inCache": true, 11 | "_installable": true, 12 | "_location": "/fxos-theme", 13 | "_nodeVersion": "5.1.0", 14 | "_npmUser": { 15 | "email": "wilsonpage@me.com", 16 | "name": "wilsonpage" 17 | }, 18 | "_npmVersion": "3.3.12", 19 | "_phantomChildren": {}, 20 | "_requested": { 21 | "name": "fxos-theme", 22 | "raw": "fxos-theme", 23 | "rawSpec": "", 24 | "scope": null, 25 | "spec": "*", 26 | "type": "range" 27 | }, 28 | "_requiredBy": [ 29 | "/" 30 | ], 31 | "_resolved": "https://registry.npmjs.org/fxos-theme/-/fxos-theme-2.0.0.tgz", 32 | "_shasum": "82cc23fd961cf9367a37822a13ceefe8ad7db806", 33 | "_shrinkwrap": null, 34 | "_spec": "fxos-theme", 35 | "_where": "/Users/wilsonpage/Documents/f/fxos-components/dom-scheduler/examples/demo", 36 | "author": { 37 | "email": "wilsonpage@me.com", 38 | "name": "Wilson Page" 39 | }, 40 | "bugs": { 41 | "url": "https://github.com/fxos-components/fxos-theme/issues" 42 | }, 43 | "dependencies": {}, 44 | "description": "A collection of css-variables to style fxos-components and apps", 45 | "devDependencies": {}, 46 | "directories": {}, 47 | "dist": { 48 | "shasum": "82cc23fd961cf9367a37822a13ceefe8ad7db806", 49 | "tarball": "http://registry.npmjs.org/fxos-theme/-/fxos-theme-2.0.0.tgz" 50 | }, 51 | "gitHead": "cc582da8c1d5046bb070a56c6a0dbca4e6627a7c", 52 | "homepage": "https://github.com/fxos-components/fxos-theme#readme", 53 | "main": "fxos-theme.css", 54 | "maintainers": [ 55 | { 56 | "name": "wilsonpage", 57 | "email": "wilsonpage@me.com" 58 | } 59 | ], 60 | "name": "fxos-theme", 61 | "optionalDependencies": {}, 62 | "readme": "ERROR: No README data found!", 63 | "repository": { 64 | "type": "git", 65 | "url": "git://github.com/fxos-components/fxos-theme.git" 66 | }, 67 | "scripts": {}, 68 | "version": "2.0.0" 69 | } 70 | -------------------------------------------------------------------------------- /test/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Wed May 06 2015 11:43:05 GMT+0200 (CEST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '..', 9 | 10 | // frameworks to use 11 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 12 | frameworks: [ 13 | 'mocha', 14 | 'sinon-chai' 15 | ], 16 | 17 | // list of files / patterns to load in the browser 18 | files: [ 19 | 'dom-scheduler.js', 20 | 'test/*.js' 21 | ], 22 | 23 | 24 | // list of files to exclude 25 | exclude: [], 26 | 27 | // preprocess matching files before serving them to the browser 28 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 29 | preprocessors: {}, 30 | 31 | // test results reporter to use 32 | // possible values: 'dots', 'progress' 33 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 34 | reporters: ['progress'], 35 | 36 | client: { 37 | captureConsole: true, 38 | mocha: {'ui': 'tdd'} 39 | }, 40 | 41 | // web server port 42 | port: 9876, 43 | 44 | // enable / disable colors in the output (reporters and logs) 45 | colors: true, 46 | 47 | // level of logging 48 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 49 | logLevel: config.LOG_INFO, 50 | 51 | // enable / disable watching file and executing tests whenever any file changes 52 | autoWatch: true, 53 | 54 | 55 | // start these browsers 56 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 57 | browsers: [ 58 | 'Chrome', 59 | 'Firefox' 60 | ], 61 | 62 | customLaunchers: { 63 | Chrome_travis_ci: { 64 | base: 'Chrome', 65 | flags: ['--no-sandbox'] 66 | } 67 | }, 68 | 69 | // Continuous Integration mode 70 | // if true, Karma captures browsers, runs the tests and exits 71 | singleRun: false 72 | }); 73 | 74 | if (process.env.TRAVIS){ 75 | config.browsers = [ 76 | 'Chrome_travis_ci', 77 | 'Firefox' 78 | ]; 79 | } 80 | 81 | config.set(config); 82 | }; 83 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fastlist/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "fastlist@^1.0.1", 5 | "/Users/wilsonpage/Documents/f/fxos-components/dom-scheduler/examples/demo" 6 | ] 7 | ], 8 | "_from": "fastlist@>=1.0.1 <2.0.0", 9 | "_id": "fastlist@1.0.2", 10 | "_inCache": true, 11 | "_installable": true, 12 | "_location": "/fastlist", 13 | "_nodeVersion": "5.1.0", 14 | "_npmUser": { 15 | "email": "wilsonpage@me.com", 16 | "name": "wilsonpage" 17 | }, 18 | "_npmVersion": "3.3.12", 19 | "_phantomChildren": {}, 20 | "_requested": { 21 | "name": "fastlist", 22 | "raw": "fastlist@^1.0.1", 23 | "rawSpec": "^1.0.1", 24 | "scope": null, 25 | "spec": ">=1.0.1 <2.0.0", 26 | "type": "range" 27 | }, 28 | "_requiredBy": [ 29 | "/" 30 | ], 31 | "_shasum": "f8d734109f88ccf932bfb9bb32143e895ff90013", 32 | "_shrinkwrap": null, 33 | "_spec": "fastlist@^1.0.1", 34 | "_where": "/Users/wilsonpage/Documents/f/fxos-components/dom-scheduler/examples/demo", 35 | "bugs": { 36 | "url": "https://github.com/fxos-components/fastlist/issues" 37 | }, 38 | "dependencies": {}, 39 | "description": "## Installation", 40 | "devDependencies": { 41 | "jshint": "^2.9.1-rc1", 42 | "karma": "^0.13.15", 43 | "karma-chrome-launcher": "^0.2.2", 44 | "karma-firefox-launcher": "^0.1.4", 45 | "karma-mocha": "^0.2.1", 46 | "karma-sinon-chai": "^0.3.0", 47 | "mocha": "^2.3.4", 48 | "popel": "^1.0.2" 49 | }, 50 | "directories": {}, 51 | "dist": { 52 | "shasum": "f8d734109f88ccf932bfb9bb32143e895ff90013", 53 | "tarball": "http://registry.npmjs.org/fastlist/-/fastlist-1.0.2.tgz" 54 | }, 55 | "gitHead": "8a7b48bfc443f9fd4fb6498968b63cb2b05abab8", 56 | "homepage": "https://github.com/fxos-components/fastlist#readme", 57 | "main": "fastlist.js", 58 | "maintainers": [ 59 | { 60 | "name": "wilsonpage", 61 | "email": "wilsonpage@me.com" 62 | } 63 | ], 64 | "name": "fastlist", 65 | "optionalDependencies": {}, 66 | "readme": "ERROR: No README data found!", 67 | "repository": { 68 | "type": "git", 69 | "url": "git://github.com/fxos-components/fastlist.git" 70 | }, 71 | "scripts": { 72 | "lint": "jshint fastlist.js test/*-test.js", 73 | "test": "karma start test/karma.conf.js --single-run", 74 | "test-dev": "karma start test/karma.conf.js" 75 | }, 76 | "version": "1.0.2" 77 | } 78 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-icons/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "fxos-icons", 5 | "/Users/wilsonpage/Documents/f/fxos-components/dom-scheduler/examples/demo" 6 | ] 7 | ], 8 | "_from": "fxos-icons@*", 9 | "_id": "fxos-icons@2.0.0", 10 | "_inCache": true, 11 | "_installable": true, 12 | "_location": "/fxos-icons", 13 | "_nodeVersion": "5.1.0", 14 | "_npmUser": { 15 | "email": "wilsonpage@me.com", 16 | "name": "wilsonpage" 17 | }, 18 | "_npmVersion": "3.3.12", 19 | "_phantomChildren": {}, 20 | "_requested": { 21 | "name": "fxos-icons", 22 | "raw": "fxos-icons", 23 | "rawSpec": "", 24 | "scope": null, 25 | "spec": "*", 26 | "type": "range" 27 | }, 28 | "_requiredBy": [ 29 | "/" 30 | ], 31 | "_resolved": "https://registry.npmjs.org/fxos-icons/-/fxos-icons-2.0.0.tgz", 32 | "_shasum": "8c02633cf418b3c76732a7b67ce46c27b8d22680", 33 | "_shrinkwrap": null, 34 | "_spec": "fxos-icons", 35 | "_where": "/Users/wilsonpage/Documents/f/fxos-components/dom-scheduler/examples/demo", 36 | "bugs": { 37 | "url": "https://github.com/fxos-components/fxos-icons/issues" 38 | }, 39 | "dependencies": {}, 40 | "description": "## Installation", 41 | "devDependencies": { 42 | "grunt": "^0.4.5", 43 | "grunt-cli": "^0.1.13", 44 | "grunt-contrib-clean": "^0.5.0", 45 | "grunt-rename": "^0.1.3", 46 | "grunt-replace": "^0.7.8", 47 | "grunt-webfont": "^0.5.1", 48 | "jshint": "^2.7.0", 49 | "optimist": "^0.6.1" 50 | }, 51 | "directories": {}, 52 | "dist": { 53 | "shasum": "8c02633cf418b3c76732a7b67ce46c27b8d22680", 54 | "tarball": "http://registry.npmjs.org/fxos-icons/-/fxos-icons-2.0.0.tgz" 55 | }, 56 | "gitHead": "91d91e11292a125f01fa03431763b933d1579b17", 57 | "homepage": "https://github.com/fxos-components/fxos-icons", 58 | "license": "MIT", 59 | "main": "fxos-icons.js", 60 | "maintainers": [ 61 | { 62 | "name": "wilsonpage", 63 | "email": "wilsonpage@me.com" 64 | } 65 | ], 66 | "name": "fxos-icons", 67 | "optionalDependencies": {}, 68 | "readme": "ERROR: No README data found!", 69 | "repository": { 70 | "type": "git", 71 | "url": "git://github.com/fxos-components/fxos-icons.git" 72 | }, 73 | "scripts": { 74 | "build": "grunt", 75 | "docker": "npm run --silent docker-run", 76 | "docker-build": "docker build --tag fxos-icons .", 77 | "docker-run": "docker run -v $(echo $PWD):/fxos-icons-host fxos-icons", 78 | "gh-pages": "git push upstream master:gh-pages", 79 | "lint": "jshint fxos-icons.js" 80 | }, 81 | "version": "2.0.0" 82 | } 83 | -------------------------------------------------------------------------------- /examples/demo/css/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | padding: 0; 3 | margin: 0; 4 | 5 | font-family: sans-serif; 6 | 7 | width: 100%; 8 | height: 100%; 9 | overflow: hidden; 10 | } 11 | 12 | h1 { 13 | pointer-events: none; 14 | } 15 | 16 | #h1-after { 17 | position: absolute; 18 | width: 100%; 19 | height: 4px; 20 | left: 0; 21 | top: 46px; 22 | 23 | pointer-events: none; 24 | background-color: #ff9500; 25 | 26 | opacity: 0; 27 | 28 | animation-duration: 0.25s; 29 | animation-timing-function: ease-in; 30 | animation-fill-mode: both; 31 | 32 | -webkit-animation-duration: 0.25s; 33 | -webkit-animation-timing-function: ease-in; 34 | -webkit-animation-fill-mode: both; 35 | } 36 | 37 | [data-anim='reveal'] { 38 | animation-name: reveal; 39 | -webkit-animation-name: reveal; 40 | } 41 | 42 | @keyframes reveal { 43 | from { 44 | opacity: 0; 45 | } 46 | to { 47 | opacity: 1; 48 | } 49 | } 50 | 51 | @-webkit-keyframes reveal { 52 | from { 53 | opacity: 0; 54 | } 55 | to { 56 | opacity: 1; 57 | } 58 | } 59 | 60 | [data-anim='hide'] { 61 | animation-name: hide; 62 | -webkit-animation-name: hide; 63 | } 64 | 65 | @keyframes hide { 66 | from { 67 | opacity: 1; 68 | } 69 | to { 70 | opacity: 0; 71 | } 72 | } 73 | 74 | @-webkit-keyframes hide { 75 | from { 76 | opacity: 1; 77 | } 78 | to { 79 | opacity: 0; 80 | } 81 | } 82 | 83 | button { 84 | position: absolute; 85 | display: block; 86 | top: 0; 87 | right: 0; 88 | height: 50px; 89 | 90 | border: 0; 91 | border-radius: 0; 92 | outline: 0; 93 | background: none; 94 | padding: 0 15px; 95 | 96 | text-align: center; 97 | text-decoration: underline; 98 | font-size: 14px; 99 | color: #ffffff; 100 | -moz-appearance: none; 101 | 102 | opacity: 1; 103 | transition: transform 0.1s linear, 104 | opacity 0.1s linear; 105 | -webkit-transition: -webkit-transform 0.1s linear, 106 | opacity 0.1s linear; 107 | 108 | -webkit-tap-highlight-color: rgba(0,0,0,0); 109 | } 110 | 111 | button.transitioning { 112 | opacity: 0.5; 113 | transform: scale(0.6, 0.6); 114 | -webkit-transform: scale(0.6, 0.6); 115 | pointer-events: none; 116 | } 117 | 118 | ul { 119 | position: absolute; 120 | top: 0; 121 | left: 0; 122 | width: 100%; 123 | } 124 | 125 | body > section { 126 | position: absolute; 127 | top: 50px; 128 | width: 100%; 129 | height: calc(100% - 50px); 130 | 131 | -webkit-overflow-scrolling: touch; 132 | -webkit-tap-highlight-color: rgba(0,0,0,0); 133 | } 134 | 135 | -------------------------------------------------------------------------------- /examples/demo/sections/css/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | padding: 0; 3 | margin: 0; 4 | 5 | font-family: sans-serif; 6 | 7 | width: 100%; 8 | height: 100%; 9 | overflow: hidden; 10 | } 11 | 12 | h1 { 13 | pointer-events: none; 14 | } 15 | 16 | #h1-after { 17 | position: absolute; 18 | width: 100%; 19 | height: 4px; 20 | left: 0; 21 | top: 46px; 22 | 23 | pointer-events: none; 24 | background-color: #ff9500; 25 | 26 | opacity: 0; 27 | 28 | animation-duration: 0.25s; 29 | animation-timing-function: ease-in; 30 | animation-fill-mode: both; 31 | 32 | -webkit-animation-duration: 0.25s; 33 | -webkit-animation-timing-function: ease-in; 34 | -webkit-animation-fill-mode: both; 35 | } 36 | 37 | [data-anim='reveal'] { 38 | animation-name: reveal; 39 | -webkit-animation-name: reveal; 40 | } 41 | 42 | @keyframes reveal { 43 | from { 44 | opacity: 0; 45 | } 46 | to { 47 | opacity: 1; 48 | } 49 | } 50 | 51 | @-webkit-keyframes reveal { 52 | from { 53 | opacity: 0; 54 | } 55 | to { 56 | opacity: 1; 57 | } 58 | } 59 | 60 | [data-anim='hide'] { 61 | animation-name: hide; 62 | -webkit-animation-name: hide; 63 | } 64 | 65 | @keyframes hide { 66 | from { 67 | opacity: 1; 68 | } 69 | to { 70 | opacity: 0; 71 | } 72 | } 73 | 74 | @-webkit-keyframes hide { 75 | from { 76 | opacity: 1; 77 | } 78 | to { 79 | opacity: 0; 80 | } 81 | } 82 | 83 | button { 84 | position: absolute; 85 | display: block; 86 | top: 0; 87 | right: 0; 88 | height: 50px; 89 | 90 | border: 0; 91 | border-radius: 0; 92 | outline: 0; 93 | background: none; 94 | padding: 0 15px; 95 | 96 | text-align: center; 97 | text-decoration: underline; 98 | font-size: 14px; 99 | color: #ffffff; 100 | -moz-appearance: none; 101 | 102 | opacity: 1; 103 | transition: transform 0.1s linear, 104 | opacity 0.1s linear; 105 | -webkit-transition: -webkit-transform 0.1s linear, 106 | opacity 0.1s linear; 107 | 108 | -webkit-tap-highlight-color: rgba(0,0,0,0); 109 | } 110 | 111 | button.transitioning { 112 | opacity: 0.5; 113 | transform: scale(0.6, 0.6); 114 | -webkit-transform: scale(0.6, 0.6); 115 | pointer-events: none; 116 | } 117 | 118 | ul { 119 | position: absolute; 120 | top: 0; 121 | left: 0; 122 | width: 100%; 123 | } 124 | 125 | body > section { 126 | position: absolute; 127 | top: 50px; 128 | width: 100%; 129 | height: calc(100% - 50px); 130 | 131 | -webkit-overflow-scrolling: touch; 132 | -webkit-tap-highlight-color: rgba(0,0,0,0); 133 | } 134 | 135 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-dialog/src/fxos-dialog-menu.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Dependencies 4 | */ 5 | 6 | var GaiaDialogProto = require('./fxos-dialog').prototype; 7 | var component = require('fxos-component'); 8 | 9 | /** 10 | * Exports 11 | */ 12 | module.exports = component.register('fxos-dialog-menu', { 13 | created: function() { 14 | this.setupShadowRoot(); 15 | this.els = { 16 | dialog: this.shadowRoot.querySelector('fxos-dialog'), 17 | items: this.shadowRoot.querySelector('.items') 18 | }; 19 | 20 | this.els.dialog.addEventListener('closed', 21 | GaiaDialogProto.hide.bind(this)); 22 | 23 | setTimeout(() => this.makeAccessible()); 24 | }, 25 | 26 | makeAccessible() { 27 | this.els.items.setAttribute('role', 'menu'); 28 | [].forEach.call(this.querySelectorAll('button'), 29 | menuitem => menuitem.setAttribute('role', 'menuitem')); 30 | }, 31 | 32 | open: function(e) { 33 | return GaiaDialogProto.show.call(this) 34 | .then(() => this.els.dialog.open(e)); 35 | }, 36 | 37 | close: function() { 38 | return this.els.dialog.close() 39 | .then(GaiaDialogProto.hide.bind(this)); 40 | }, 41 | 42 | template: ` 43 | 44 |
45 |
46 | ` 99 | }); 100 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-component/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "fxos-component@^1.0.0", 5 | "/Users/wilsonpage/Documents/f/fxos-components/dom-scheduler/examples/demo/node_modules/fxos-dialog" 6 | ] 7 | ], 8 | "_from": "fxos-component@>=1.0.0 <2.0.0", 9 | "_id": "fxos-component@1.0.0", 10 | "_inCache": true, 11 | "_installable": true, 12 | "_location": "/fxos-component", 13 | "_nodeVersion": "5.1.0", 14 | "_npmUser": { 15 | "email": "wilsonpage@me.com", 16 | "name": "wilsonpage" 17 | }, 18 | "_npmVersion": "3.3.12", 19 | "_phantomChildren": {}, 20 | "_requested": { 21 | "name": "fxos-component", 22 | "raw": "fxos-component@^1.0.0", 23 | "rawSpec": "^1.0.0", 24 | "scope": null, 25 | "spec": ">=1.0.0 <2.0.0", 26 | "type": "range" 27 | }, 28 | "_requiredBy": [ 29 | "/fxos-dialog", 30 | "/fxos-text-input" 31 | ], 32 | "_resolved": "https://registry.npmjs.org/fxos-component/-/fxos-component-1.0.0.tgz", 33 | "_shasum": "e0941f7e8c8940b2e23e52834f1ce45d2a18deb1", 34 | "_shrinkwrap": null, 35 | "_spec": "fxos-component@^1.0.0", 36 | "_where": "/Users/wilsonpage/Documents/f/fxos-components/dom-scheduler/examples/demo/node_modules/fxos-dialog", 37 | "bugs": { 38 | "url": "https://github.com/fxos-components/fxos-component/issues" 39 | }, 40 | "dependencies": {}, 41 | "description": "A wrapper around `document.registerElement()` to define a custom-element with workarounds for gaps in the Gecko platform, plus some convenience methods.", 42 | "devDependencies": { 43 | "jshint": "^2.7.0", 44 | "karma": "^0.12.32", 45 | "karma-firefox-launcher": "^0.1.4", 46 | "karma-mocha": "^0.1.10", 47 | "karma-sinon-chai": "^0.3.0", 48 | "mozilla-download": "^1.0.5", 49 | "sinon": "^1.17.2" 50 | }, 51 | "directories": { 52 | "example": "examples", 53 | "src": "src", 54 | "test": "test" 55 | }, 56 | "dist": { 57 | "shasum": "e0941f7e8c8940b2e23e52834f1ce45d2a18deb1", 58 | "tarball": "http://registry.npmjs.org/fxos-component/-/fxos-component-1.0.0.tgz" 59 | }, 60 | "gitHead": "5313cef6c949c7f1994d1d852492fe304e7f294f", 61 | "homepage": "https://github.com/fxos-components/fxos-component", 62 | "main": "src/fxos-component.js", 63 | "maintainers": [ 64 | { 65 | "name": "wilsonpage", 66 | "email": "wilsonpage@me.com" 67 | } 68 | ], 69 | "name": "fxos-component", 70 | "optionalDependencies": {}, 71 | "readme": "ERROR: No README data found!", 72 | "repository": { 73 | "type": "git", 74 | "url": "git://github.com/fxos-components/fxos-component.git" 75 | }, 76 | "scripts": { 77 | "build": "webpack", 78 | "test": "npm run test-lint && npm run test-unit", 79 | "test-lint": "jshint src/*.js", 80 | "test-unit": "./node_modules/karma/bin/karma start test/karma.conf.js --single-run", 81 | "test-unit-dev": "./node_modules/karma/bin/karma start test/karma.conf.js", 82 | "watch": "webpack --watch" 83 | }, 84 | "version": "1.0.0" 85 | } 86 | -------------------------------------------------------------------------------- /examples/demo/sections/js/page.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | /*global BaconSource, FastList*/ 3 | 4 | 'use strict'; 5 | 6 | var endEvent = ('ontouchstart' in window) ? 'touchend' : 'mouseup'; 7 | 8 | window.addEventListener('load', function() { 9 | var listContainer = document.querySelector('section'); 10 | 11 | var source = new BaconSource(listContainer); 12 | var list = new FastList(source).plugin(fastListEdit); 13 | var scheduler = FastList.scheduler; 14 | 15 | function updateHeader() { 16 | return scheduler.mutation(function() { 17 | var h1 = document.querySelector('h1'); 18 | h1.textContent = 'Main List (' + source.getFullLength() + ')'; 19 | h1.scrollTop; // flush 20 | }); 21 | } 22 | updateHeader(); 23 | 24 | function openAlert(evt) { 25 | var detail = evt.detail; 26 | var li = source.getRecordAt(detail.index); 27 | li && alert(li.title + ' item clicked!'); 28 | } 29 | list.els.list.addEventListener('item-selected', openAlert); 30 | 31 | function clearNewIndicator() { 32 | var h1After = document.querySelector('#h1-after'); 33 | 34 | if (h1After.dataset.anim == 'reveal') { 35 | scheduler.transition(function() { 36 | h1After.dataset.anim = 'hide'; 37 | }, h1After, 'animationend'); 38 | } 39 | } 40 | listContainer.addEventListener('top-reached', clearNewIndicator); 41 | 42 | function updateNewIndicator() { 43 | var h1After = document.querySelector('#h1-after'); 44 | if (h1After.dataset.anim == 'reveal') { 45 | return; 46 | } 47 | 48 | scheduler.transition(function() { 49 | h1After.dataset.anim = 'reveal'; 50 | }, h1After, 'animationend'); 51 | } 52 | listContainer.addEventListener('hidden-new-content', updateNewIndicator); 53 | 54 | function newContentHandler() { 55 | var newContent = { 56 | title: 'NEW Bacon ' + Date.now().toString().slice(7, -1), 57 | body: 'Turkey BLT please.' 58 | }; 59 | 60 | source.insertAtIndex(0, newContent); 61 | list.insertedAtIndex(0); 62 | 63 | updateHeader(); 64 | } 65 | 66 | setInterval(newContentHandler, 15000); 67 | window.addEventListener('new-content', newContentHandler); 68 | 69 | window.pushNewContent = function() { 70 | window.dispatchEvent(new CustomEvent('new-content')); 71 | }; 72 | 73 | var button = document.querySelector('button'); 74 | button.addEventListener(endEvent, function() { 75 | Promise.all([toggleTransitioning(), list.toggleEditMode()]) 76 | .then(updateText) 77 | .then(toggleTransitioning); 78 | }); 79 | 80 | function updateText(text) { 81 | return scheduler.mutation(function() { 82 | button.textContent = list.editing ? 'Exit' : 'Edit'; 83 | }); 84 | } 85 | 86 | function toggleTransitioning() { 87 | return scheduler.feedback(function() { 88 | button.classList.toggle('transitioning'); 89 | }, button, 'transitionend'); 90 | } 91 | }); 92 | })(); 93 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-text-input/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "fxos-text-input@^1.0.1", 5 | "/Users/wilsonpage/Documents/f/fxos-components/dom-scheduler/examples/demo/node_modules/fxos-dialog" 6 | ] 7 | ], 8 | "_from": "fxos-text-input@>=1.0.1 <2.0.0", 9 | "_id": "fxos-text-input@1.0.1", 10 | "_inCache": true, 11 | "_installable": true, 12 | "_location": "/fxos-text-input", 13 | "_nodeVersion": "5.1.0", 14 | "_npmUser": { 15 | "email": "wilsonpage@me.com", 16 | "name": "wilsonpage" 17 | }, 18 | "_npmVersion": "3.3.12", 19 | "_phantomChildren": {}, 20 | "_requested": { 21 | "name": "fxos-text-input", 22 | "raw": "fxos-text-input@^1.0.1", 23 | "rawSpec": "^1.0.1", 24 | "scope": null, 25 | "spec": ">=1.0.1 <2.0.0", 26 | "type": "range" 27 | }, 28 | "_requiredBy": [ 29 | "/fxos-dialog" 30 | ], 31 | "_shasum": "53e3d95f64c88a7bc0e3c9a69b60f3e14ec4b316", 32 | "_shrinkwrap": null, 33 | "_spec": "fxos-text-input@^1.0.1", 34 | "_where": "/Users/wilsonpage/Documents/f/fxos-components/dom-scheduler/examples/demo/node_modules/fxos-dialog", 35 | "bugs": { 36 | "url": "https://github.com/fxos-components/fxos-text-input/issues" 37 | }, 38 | "dependencies": { 39 | "fxos-component": "^1.0.0", 40 | "fxos-icons": "^2.0.0" 41 | }, 42 | "description": "## Installation", 43 | "devDependencies": { 44 | "bower": "^1.6.5", 45 | "fxos-font": "^1.0.0", 46 | "fxos-theme": "^2.0.0", 47 | "jshint": "^2.8.0", 48 | "karma": "^0.13.15", 49 | "karma-firefox-launcher": "^0.1.6", 50 | "karma-mocha": "^0.2.0", 51 | "karma-sinon-chai": "^0.3.0", 52 | "mocha": "^2.3.3", 53 | "nws": "^0.6.1", 54 | "webpack": "^1.12.9" 55 | }, 56 | "directories": { 57 | "test": "test" 58 | }, 59 | "dist": { 60 | "shasum": "53e3d95f64c88a7bc0e3c9a69b60f3e14ec4b316", 61 | "tarball": "http://registry.npmjs.org/fxos-text-input/-/fxos-text-input-1.0.1.tgz" 62 | }, 63 | "gitHead": "59fbef9c6323e93018a1976fa7d06de5cd0a139b", 64 | "homepage": "https://github.com/fxos-components/fxos-text-input", 65 | "license": "ISC", 66 | "main": "fxos-text-input.js", 67 | "maintainers": [ 68 | { 69 | "name": "wilsonpage", 70 | "email": "wilsonpage@me.com" 71 | } 72 | ], 73 | "name": "fxos-text-input", 74 | "optionalDependencies": {}, 75 | "readme": "ERROR: No README data found!", 76 | "repository": { 77 | "type": "git", 78 | "url": "git://github.com/fxos-components/fxos-text-input.git" 79 | }, 80 | "scripts": { 81 | "build": "webpack", 82 | "gh-pages": "git co gh-pages; git merge master; git push upstream gh-pages; git co -", 83 | "start": "npm run -s watch & nws -d examples/text-cases/ -p 8005 -o", 84 | "test": "npm run --silent test-unit && npm run --silent test-lint", 85 | "test-dev": "npm run -s watch & karma start test/karma.conf.js", 86 | "test-lint": "jshint src/*.js", 87 | "test-unit": "karma start test/karma.conf.js --single-run", 88 | "watch": "webpack -w" 89 | }, 90 | "version": "1.0.1" 91 | } 92 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-dialog/src/fxos-dialog-action.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Dependencies 4 | */ 5 | 6 | var GaiaDialogProto = require('./fxos-dialog').prototype; 7 | var component = require('fxos-component'); 8 | 9 | /** 10 | * Exports 11 | */ 12 | module.exports = component.register('fxos-dialog-action', { 13 | created: function() { 14 | this.setupShadowRoot(); 15 | 16 | this.els = { 17 | dialog: this.shadowRoot.querySelector('fxos-dialog'), 18 | submit: this.shadowRoot.querySelector('.submit'), 19 | cancel: this.shadowRoot.querySelector('.cancel') 20 | }; 21 | 22 | this.els.dialog.addEventListener('closed', 23 | GaiaDialogProto.hide.bind(this)); 24 | }, 25 | 26 | open: function(e) { 27 | return GaiaDialogProto.show.call(this) 28 | .then(() => this.els.dialog.open(e)); 29 | }, 30 | 31 | close: function() { 32 | // First close (hide) inner dialog and then the container. 33 | return this.els.dialog.close() 34 | .then(GaiaDialogProto.hide.bind(this)); 35 | }, 36 | 37 | template: ` 38 | 39 |
40 | 41 |
42 | 43 | 44 |
45 | ` 113 | }); 114 | -------------------------------------------------------------------------------- /examples/demo/css/ul.css: -------------------------------------------------------------------------------- 1 | ul { 2 | list-style: none; 3 | padding: 0 0 0 0; 4 | margin: 0; 5 | } 6 | 7 | ul section { 8 | margin: 0; 9 | padding: 0; 10 | } 11 | 12 | ul h2 { 13 | position: sticky; 14 | position: -webkit-sticky; 15 | top: 0px; 16 | height: 32px; 17 | 18 | margin: 0; 19 | padding: 0 6px; 20 | box-sizing: border-box; 21 | 22 | font-size: 0.9em; 23 | line-height: 32px; 24 | 25 | background: #efefef; 26 | border-bottom: 1px #bcbcbc solid; 27 | 28 | z-index: 100; 29 | } 30 | 31 | ul .background { 32 | position: absolute; 33 | width: 100%; 34 | 35 | background: linear-gradient(to bottom, transparent 0px, transparent 45px, #EDEDED 45px, #EDEDED 70px, transparent 20px), 36 | linear-gradient(to bottom, transparent 0px, transparent 15px, #EDEDED 15px, #EDEDED 25px, transparent 20px), 37 | linear-gradient(to top, #bcbcbc, #bcbcbc 1px, white 1px, white); 38 | background-position: 20% 88px, 5% -88px, 0 -88px; 39 | background-size: 90% 88px, 60% 88px, 100% 88px; 40 | background-repeat: repeat-y; 41 | 42 | z-index: 0; 43 | } 44 | 45 | ul.reordering .background { 46 | background: linear-gradient(to top, #bcbcbc, #bcbcbc 1px, white 1px, white); 47 | background-position: 0 -88px; 48 | background-size: 100% 88px; 49 | } 50 | 51 | ul li { 52 | width: 100%; 53 | height: 88px; 54 | margin: 0; 55 | padding: 0; 56 | box-sizing: border-box; 57 | 58 | -moz-user-select: none; 59 | 60 | background: white; 61 | border-bottom: 1px #bcbcbc solid; 62 | 63 | z-index: 10; 64 | } 65 | 66 | ul li h3 { 67 | margin: 0; 68 | padding: 9px 6px 3px 6px; 69 | background: white; 70 | 71 | color: #424242; 72 | 73 | pointer-events: none; 74 | } 75 | 76 | ul li p { 77 | margin: 0; 78 | padding: 3px 6px 10px 6px; 79 | background: white; 80 | 81 | color: #797979; 82 | 83 | pointer-events: none; 84 | } 85 | 86 | li.new { 87 | opacity: 0; 88 | } 89 | 90 | li .overlay { 91 | position: absolute; 92 | top: 1px; 93 | left: 0; 94 | width: 100%; 95 | height: calc(100% - 2px); 96 | 97 | background: rgba(255, 255, 255, 0.7); 98 | 99 | opacity: 0; 100 | animation-duration: 0.2s; 101 | animation-timing-function: ease-in; 102 | animation-fill-mode: both; 103 | 104 | -webkit-animation-duration: 0.2s; 105 | -webkit-animation-timing-function: ease-in; 106 | -webkit-animation-fill-mode: both; 107 | 108 | pointer-events: none; 109 | } 110 | 111 | li.edit .overlay { 112 | opacity: 1; 113 | pointer-events: auto; 114 | } 115 | 116 | .overlay .cursor { 117 | margin: 17px auto; 118 | padding: 0; 119 | width: 50px; 120 | height: 50px; 121 | 122 | text-align: center; 123 | line-height: 50px; 124 | font-size: 25px; 125 | color: #0b0b0b; 126 | 127 | border-radius: 8px; 128 | background: rgba(0, 0, 0, 0.3); 129 | -moz-user-select: none; 130 | } 131 | 132 | li.dragging { 133 | transition: transform 0s linear; 134 | -webkit-transition: -webkit-transform 0s linear; 135 | } 136 | 137 | li.dragging .overlay { 138 | opacity: 0; 139 | } 140 | 141 | li[data-populated=false] { 142 | opacity: 0; 143 | } 144 | -------------------------------------------------------------------------------- /examples/demo/sections/css/ul.css: -------------------------------------------------------------------------------- 1 | ul { 2 | list-style: none; 3 | padding: 0 0 0 0; 4 | margin: 0; 5 | } 6 | 7 | ul section { 8 | margin: 0; 9 | padding: 0; 10 | } 11 | 12 | ul h2 { 13 | position: sticky; 14 | position: -webkit-sticky; 15 | top: 0px; 16 | height: 32px; 17 | 18 | margin: 0; 19 | padding: 0 6px; 20 | box-sizing: border-box; 21 | 22 | font-size: 0.9em; 23 | line-height: 32px; 24 | 25 | background: #efefef; 26 | border-bottom: 1px #bcbcbc solid; 27 | 28 | z-index: 100; 29 | } 30 | 31 | ul .background { 32 | position: absolute; 33 | width: 100%; 34 | 35 | background: linear-gradient(to bottom, transparent 0px, transparent 45px, #EDEDED 45px, #EDEDED 70px, transparent 20px), 36 | linear-gradient(to bottom, transparent 0px, transparent 15px, #EDEDED 15px, #EDEDED 25px, transparent 20px), 37 | linear-gradient(to top, #bcbcbc, #bcbcbc 1px, white 1px, white); 38 | background-position: 20% 88px, 5% -88px, 0 -88px; 39 | background-size: 90% 88px, 60% 88px, 100% 88px; 40 | background-repeat: repeat-y; 41 | 42 | z-index: 0; 43 | } 44 | 45 | ul.reordering .background { 46 | background: linear-gradient(to top, #bcbcbc, #bcbcbc 1px, white 1px, white); 47 | background-position: 0 -88px; 48 | background-size: 100% 88px; 49 | } 50 | 51 | ul li { 52 | width: 100%; 53 | height: 88px; 54 | margin: 0; 55 | padding: 0; 56 | box-sizing: border-box; 57 | 58 | -moz-user-select: none; 59 | 60 | background: white; 61 | border-bottom: 1px #bcbcbc solid; 62 | 63 | z-index: 10; 64 | } 65 | 66 | ul li h3 { 67 | margin: 0; 68 | padding: 9px 6px 3px 6px; 69 | background: white; 70 | 71 | color: #424242; 72 | 73 | pointer-events: none; 74 | } 75 | 76 | ul li p { 77 | margin: 0; 78 | padding: 3px 6px 10px 6px; 79 | background: white; 80 | 81 | color: #797979; 82 | 83 | pointer-events: none; 84 | } 85 | 86 | li.new { 87 | opacity: 0; 88 | } 89 | 90 | li .overlay { 91 | position: absolute; 92 | top: 1px; 93 | left: 0; 94 | width: 100%; 95 | height: calc(100% - 2px); 96 | 97 | background: rgba(255, 255, 255, 0.7); 98 | 99 | opacity: 0; 100 | animation-duration: 0.2s; 101 | animation-timing-function: ease-in; 102 | animation-fill-mode: both; 103 | 104 | -webkit-animation-duration: 0.2s; 105 | -webkit-animation-timing-function: ease-in; 106 | -webkit-animation-fill-mode: both; 107 | 108 | pointer-events: none; 109 | } 110 | 111 | li.edit .overlay { 112 | opacity: 1; 113 | pointer-events: auto; 114 | } 115 | 116 | .overlay .cursor { 117 | margin: 17px auto; 118 | padding: 0; 119 | width: 50px; 120 | height: 50px; 121 | 122 | text-align: center; 123 | line-height: 50px; 124 | font-size: 25px; 125 | color: #0b0b0b; 126 | 127 | border-radius: 8px; 128 | background: rgba(0, 0, 0, 0.3); 129 | -moz-user-select: none; 130 | } 131 | 132 | li.dragging { 133 | transition: transform 0s linear; 134 | -webkit-transition: -webkit-transform 0s linear; 135 | } 136 | 137 | li.dragging .overlay { 138 | opacity: 0; 139 | } 140 | 141 | li[data-populated=false] { 142 | opacity: 0; 143 | } 144 | -------------------------------------------------------------------------------- /examples/demo/js/app.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var debug = false; 3 | var headerHeight = 50; 4 | var maxItemCount = 28; 5 | 6 | var endEvent = ('ontouchstart' in window) ? 'touchend' : 'mouseup'; 7 | 8 | window.addEventListener('load', function() { 9 | var listContainer = document.querySelector('section'); 10 | var list = document.querySelector('ul'); 11 | 12 | var source = new BaconSource(listContainer, list); 13 | var list = new FastList(source); 14 | var dialog = document.querySelector('gaia-dialog-alert'); 15 | 16 | function updateHeader() { 17 | return scheduler.mutation(function() { 18 | var h1 = document.querySelector('h1'); 19 | h1.textContent = 'Main List (' + source.fullLength() + ')'; 20 | h1.scrollTop; // flush 21 | }); 22 | } 23 | updateHeader(); 24 | 25 | function clearNewIndicator() { 26 | var h1After = document.querySelector('#h1-after'); 27 | 28 | if (h1After.dataset.anim == 'reveal') { 29 | scheduler.transition(function() { 30 | h1After.dataset.anim = 'hide'; 31 | }, h1After, 'animationend'); 32 | } 33 | } 34 | listContainer.addEventListener('top-reached', clearNewIndicator); 35 | 36 | function updateNewIndicator() { 37 | var h1After = document.querySelector('#h1-after'); 38 | if (h1After.dataset.anim == 'reveal') { 39 | return; 40 | } 41 | 42 | scheduler.transition(function() { 43 | h1After.dataset.anim = 'reveal'; 44 | }, h1After, 'animationend'); 45 | } 46 | listContainer.addEventListener('hidden-new-content', updateNewIndicator); 47 | 48 | function openGaiaDialog(evt) { 49 | scheduler.mutation(function() { 50 | var detail = evt.detail; 51 | var li = source.getRecordAt(detail.index); 52 | dialog.textContent = li.title + ' item clicked!'; 53 | dialog.open(detail.clickEvt); 54 | }); 55 | } 56 | list.list.addEventListener('item-selected', openGaiaDialog); 57 | 58 | function newContentHandler() { 59 | var newContent = { 60 | title: 'NEW Bacon ' + Date.now().toString().slice(7, -1), 61 | body: 'Turkey BLT please.' 62 | }; 63 | 64 | source.insertAtIndex(0, newContent); 65 | list.insertedAtIndex(0); 66 | 67 | updateHeader(); 68 | } 69 | 70 | setInterval(newContentHandler, 15000); 71 | window.addEventListener('new-content', newContentHandler); 72 | 73 | window.pushNewContent = function() { 74 | window.dispatchEvent(new CustomEvent('new-content')); 75 | }; 76 | 77 | var button = document.querySelector('button'); 78 | button.addEventListener(endEvent, function() { 79 | Promise.all([toggleTransitioning(), list.toggleEditMode()]) 80 | .then(updateText) 81 | .then(toggleTransitioning); 82 | }); 83 | 84 | function updateText(text) { 85 | return scheduler.mutation(function() { 86 | button.textContent = list.editing ? 'Exit' : 'Edit'; 87 | }); 88 | } 89 | 90 | function toggleTransitioning() { 91 | return scheduler.feedback(function() { 92 | button.classList.toggle('transitioning'); 93 | }, button, 'transitionend'); 94 | } 95 | }); 96 | })(); 97 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-dialog/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "fxos-dialog", 5 | "/Users/wilsonpage/Documents/f/fxos-components/dom-scheduler/examples/demo" 6 | ] 7 | ], 8 | "_from": "fxos-dialog@*", 9 | "_id": "fxos-dialog@2.0.1", 10 | "_inCache": true, 11 | "_installable": true, 12 | "_location": "/fxos-dialog", 13 | "_nodeVersion": "5.1.0", 14 | "_npmUser": { 15 | "email": "wilsonpage@me.com", 16 | "name": "wilsonpage" 17 | }, 18 | "_npmVersion": "3.3.12", 19 | "_phantomChildren": {}, 20 | "_requested": { 21 | "name": "fxos-dialog", 22 | "raw": "fxos-dialog", 23 | "rawSpec": "", 24 | "scope": null, 25 | "spec": "*", 26 | "type": "range" 27 | }, 28 | "_requiredBy": [ 29 | "/" 30 | ], 31 | "_shasum": "2379839b489d4040b40c9ef0b9ee1ab5ef10d67c", 32 | "_shrinkwrap": null, 33 | "_spec": "fxos-dialog", 34 | "_where": "/Users/wilsonpage/Documents/f/fxos-components/dom-scheduler/examples/demo", 35 | "browser": "src/fxos-dialog.js", 36 | "bugs": { 37 | "url": "https://github.com/fxos-components/fxos-dialog/issues" 38 | }, 39 | "dependencies": { 40 | "fxos-component": "^1.0.0", 41 | "fxos-icons": "^2.0.0", 42 | "fxos-text-input": "^1.0.1" 43 | }, 44 | "description": "Several types of dialogs, including alert, confirm, prompt, action, select, and menu.", 45 | "devDependencies": { 46 | "chai": "^3.4.1", 47 | "fxos-button": "^1.0.0", 48 | "fxos-font": "^1.0.0", 49 | "fxos-theme": "^2.0.0", 50 | "jshint": "^2.9.1-rc1", 51 | "karma": "^0.13.15", 52 | "karma-chai-sinon": "^0.1.5", 53 | "karma-firefox-launcher": "^0.1.4", 54 | "karma-mocha": "^0.2.1", 55 | "marionette-client": "1.9.4", 56 | "marionette-firefox-host": "1.0.4", 57 | "marionette-helper": "0.3.2", 58 | "marionette-js-runner": "1.1.3", 59 | "mozilla-download": "^1.1.1", 60 | "sinon": "^1.17.2", 61 | "sinon-chai": "^2.8.0", 62 | "test-utils": "github:fxos-components/test-utils" 63 | }, 64 | "directories": {}, 65 | "dist": { 66 | "shasum": "2379839b489d4040b40c9ef0b9ee1ab5ef10d67c", 67 | "tarball": "http://registry.npmjs.org/fxos-dialog/-/fxos-dialog-2.0.1.tgz" 68 | }, 69 | "gitHead": "2a1e039985a2ed3337f8766b928f0f6f4aae42f5", 70 | "homepage": "https://github.com/fxos-components/fxos-dialog", 71 | "license": "MIT", 72 | "main": "fxos-dialog.js", 73 | "maintainers": [ 74 | { 75 | "name": "wilsonpage", 76 | "email": "wilsonpage@me.com" 77 | } 78 | ], 79 | "name": "fxos-dialog", 80 | "optionalDependencies": {}, 81 | "readme": "ERROR: No README data found!", 82 | "repository": { 83 | "type": "git", 84 | "url": "git://github.com/fxos-components/fxos-dialog.git" 85 | }, 86 | "scripts": { 87 | "build": "webpack", 88 | "start": "npm run -s watch & nws -p 8005 -d examples/demo/ -o", 89 | "test": "npm run -s test-lint && npm run -s test-unit && npm run -s test-integration", 90 | "test-integration": "marionette-mocha --reporter spec --host marionette-firefox-host --runtime $FIREFOX_NIGHTLY_BIN --timeout 6000s test/test-integration.js", 91 | "test-lint": "./node_modules/jshint/bin/jshint src/*.js", 92 | "test-unit": "./node_modules/karma/bin/karma start test/karma.conf.js --single-run", 93 | "test-unit-dev": "./node_modules/karma/bin/karma start test/karma.conf.js", 94 | "watch": "webpack -w" 95 | }, 96 | "version": "2.0.1" 97 | } 98 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-icons/README.md: -------------------------------------------------------------------------------- 1 | # fxos-icons 2 | 3 | ## Installation 4 | 5 | ```bash 6 | $ npm install fxos-icons 7 | ``` 8 | 9 | Then include folowing file in HTML 10 | 11 | ```html 12 | 13 | ``` 14 | 15 | ## Usage 16 | 17 | Use `i` tag to represent an icon. 18 | 19 | ```html 20 | 21 | ``` 22 | 23 | ## Examples 24 | 25 | - [Example](http://fxos-components.github.io/fxos-icons/) 26 | 27 | ## Accessibility 28 | 29 | `aria-label` will be added automatically when `data-l10n-id` attribute is specified in target element. 30 | 31 | ```html 32 | 33 | ``` 34 | 35 | If the icon is for present only, add `aria-hidden` attribute to make it unreachable by the screen reader. 36 | 37 | ```html 38 | 39 | ``` 40 | 41 | If the icon is included in certain component, try integrate it as component's `data-icon` attribute and handle the accessiblity related issues automatically. 42 | 43 | If `aria-hidden` is not used on the icon, it will always be accessible to the screen reader. Adding `data-l10n-id` to the element with `data-icon` that points to `{property}.ariaLabel` in the properties file (that will add an `aria-label` attribute to the same element and will not touch inner HTML). 44 | 45 | ## Contributions 46 | 47 | If you wish to make changes to the icon font you'll need to follow these steps: 48 | 49 | 1. Add, remove or change respective `.svg` files inside `images/`. 50 | 2. Run `$ npm install` to get pull in all the required build tools. 51 | 3. Make sure you have `fontforge` and `ttfautohint` installed on your machine. The [grunt-webfont](https://github.com/sapegin/grunt-webfont#installation) installation guide outlines the prerequisites. 52 | 4. Run `$ grunt`. 53 | 5. Load `index.html` locally in your browser and check your icon looks good. 54 | 6. Submit a pull request. 55 | 7. Module owner will review, land, and stamp a new version. 56 | 57 | ## Guidelines 58 | 59 | For best results, it is recommended to follow these guidelines: 60 | 61 | * Make the document 30px × 30px (In Inkscape: File > Document Properties... > Custom size). 62 | * Make the icon 24px × 24px. 63 | * Center the icon (In Inkscape: Object > Align and Distribute... > Align relative to page). 64 | * Make sure to have only one `` with no overlap per icon. 65 | * Optimise the icons using [svgo](https://github.com/svg/svgo), then export to plain SVG file (`$ inkscape -l icon.svg icon.svg`). 66 | 67 | Please also make sure new icons naming is consistent with existing ones: 68 | 69 | * Use lower case only. 70 | * Separate words with hyphens. 71 | * Use meaningful words rather than acronyms (e.g. `top-left-crop-corner` instead of `t-l-crop-corner`). 72 | 73 | ## Gaia usage 74 | 75 | Gaia hackers, please read the introduction to ['Version controlled packages in Gaia'](https://gist.github.com/wilsonpage/3d7f636a78db66f8f1d7) to find out how to use this package in your Gaia app. 76 | 77 | ## Get a report 78 | 79 | You can get a report of unused icons on a project by doing: 80 | ```bash 81 | $ node bin/report.js path/to/your/project/ 82 | ``` 83 | 84 | Please note, that dynamically inserted icons may still be marked as unused in the report. 85 | 86 | ## Lint check 87 | 88 | Run lint check with command: 89 | 90 | `$ npm run lint` 91 | 92 | ## Current owners 93 | 94 | - Wilson Page [:wilsonpage] 95 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-component/README.md: -------------------------------------------------------------------------------- 1 | # fxos-component [![](https://travis-ci.org/fxos-components/fxos-component.svg)](https://travis-ci.org/fxos-components/fxos-component) 2 | 3 | A wrapper around `document.registerElement()` to define a custom-element with workarounds for gaps in the Gecko platform, plus some convenience methods. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | $ npm install fxos-component 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```js 14 | var MyComponent = fxosComponent.register('my-component', { 15 | // extend component from the given prototype 16 | extends: HTMLButtonElement.prototype, 17 | 18 | created: function() { 19 | 20 | // Creates a shadow-root and 21 | // puts your template in it 22 | this.setupShadowRoot(); 23 | }, 24 | 25 | attributeChanged: function() {}, 26 | attached: function() { 27 | 28 | // Localizes shadow DOM of your 29 | // component and adds an observer for 30 | // when localization changes 31 | this.setupShadowL10n(); 32 | }, 33 | detached: function() {}, 34 | 35 | // You can override this function and 36 | // specify how the shadow DOM of your 37 | // component should be localized. 38 | localizeShadow: function(shadowRoot) {}, 39 | 40 | template: ` 41 | 42 | 45 | `, 46 | 47 | // Some CSS doesn't work 48 | // in shadow-dom stylesheets, 49 | // this CSS gets put in the 50 | globalCss: ` 51 | @keyframes my-animation { ... } 52 | @font-face { ... } 53 | `, 54 | 55 | // Property descriptors that get defined 56 | // on the prototype and get called 57 | // when matching attributes change 58 | attrs: { 59 | customAttr: { 60 | get: function() { return this._customAttr; }, 61 | set: function(value) { this._customAttr = value; } 62 | } 63 | } 64 | }); 65 | 66 | var myComponent = new MyComponent(); 67 | 68 | myComponent.setAttribute('custom-attr', 'foo'); 69 | myComponent.customAttr; //=> 'foo'; 70 | ``` 71 | 72 | ### Component Extension 73 | 74 | Components can extend other components: 75 | 76 | ```js 77 | var MyComponent = fxosComponent.register('my-component', { 78 | // extend component from the given prototype 79 | extends: HTMLButtonElement.prototype, 80 | ... 81 | }); 82 | 83 | var YourComponent = fxosComponent.register('your-component', { 84 | // extend component from the given prototype 85 | extends: MyComponent.prototype, 86 | ... 87 | }); 88 | ``` 89 | 90 | If the property ```extensible: false``` is set the registered component 91 | cannot be extended: 92 | 93 | ```js 94 | var MyComponent = fxosComponent.register('my-component', { 95 | // extend component from the given prototype 96 | extends: HTMLButtonElement.prototype, 97 | extensible: false, 98 | ... 99 | }); 100 | ``` 101 | 102 | Extensible components come with a little bit of overhead. For instance, 103 | they store a copy of the template string that derived components 104 | will use to properly inherit the styles. With the extensible flag set to false 105 | components won't store the template string and some memory will be saved. 106 | This is an optimization for production systems and large component hierarchies. 107 | Registed components are extensible by default. 108 | 109 | ## Tests 110 | 111 | If you would like to run both unit and lint tests: 112 | 113 | 1. Ensure Firefox Nightly is installed on your machine. 114 | 2. `$ npm install` 115 | 3. `$ npm test` 116 | 117 | If your would like unit tests to run on file change use: 118 | 119 | `$ npm run test-unit-dev` 120 | 121 | ## Lint check 122 | 123 | Run lint check with command: 124 | 125 | `$ npm run test-lint` 126 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-theme/lib/fxos-theme-selector.js: -------------------------------------------------------------------------------- 1 | (function(define){'use strict';define(function(require,exports,module){ 2 | 3 | /** 4 | * Extend from the `HTMLElement` prototype 5 | * 6 | * @type {Object} 7 | */ 8 | var proto = Object.create(HTMLElement.prototype); 9 | 10 | proto.createdCallback = function() { 11 | this.createShadowRoot().innerHTML = template; 12 | 13 | this.els = { 14 | themes: this.shadowRoot.querySelector('.themes'), 15 | rtl: this.shadowRoot.querySelector('.toggle-rtl'), 16 | inner: this.shadowRoot.querySelector('.inner') 17 | }; 18 | 19 | this.els.rtl.addEventListener('click', this.onRtlClick.bind(this)); 20 | this.els.themes.addEventListener('click', this.onThemeClick.bind(this)); 21 | 22 | if (this.hasAttribute('slidehide')) { 23 | this.els.inner.setAttribute('slidehide', ''); 24 | } 25 | 26 | this.styleHack(); 27 | this.set(localStorage.theme || 'system'); 28 | }; 29 | 30 | proto.styleHack = function() { 31 | var style = this.shadowRoot.querySelector('style'); 32 | style.setAttribute('scoped', ''); 33 | this.appendChild(style.cloneNode(true)); 34 | }; 35 | 36 | proto.onThemeClick = function(e) { 37 | var theme = e.target.value; 38 | if (theme) { this.set(theme); } 39 | }; 40 | 41 | proto.onRtlClick = function() { 42 | document.dir = this.els.rtl.checked ? 'rtl' : 'ltr'; 43 | }; 44 | 45 | proto.set = function(theme) { 46 | var radio = this.shadowRoot.querySelector('[value="' + theme + '"]'); 47 | document.documentElement.classList.remove('fxos-theme-' + this.theme); 48 | document.documentElement.classList.add('fxos-theme-' + theme); 49 | radio.checked = true; 50 | this.theme = theme; 51 | localStorage.theme = theme; 52 | }; 53 | 54 | var template = ` 55 | 115 | 116 |
117 |
118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |
126 |
`; 127 | 128 | document.registerElement('fxos-theme-selector', { prototype: proto }); 129 | 130 | });})((function(n,w){'use strict';return typeof define=='function'&&define.amd?define:typeof module=='object'?function(c){c(require,exports,module);}:function(c){var m={exports:{}},r=function(n){return w[n];};w[n]=c(r,m.exports,m)||m.exports;};})('fxos-theme-selector',this));/*jshint ignore:line*/ -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-dialog/fxos-dialog-alert.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(require("./fxos-dialog"), require("fxos-component")); 4 | else if(typeof define === 'function' && define.amd) 5 | define(["./fxos-dialog", "fxos-component"], factory); 6 | else if(typeof exports === 'object') 7 | exports["FXOSDialogAlert"] = factory(require("./fxos-dialog"), require("fxos-component")); 8 | else 9 | root["FXOSDialogAlert"] = factory(root["FXOSDialog"], root["fxosComponent"]); 10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__) { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | 39 | 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | 58 | /** 59 | * Dependencies 60 | */ 61 | 62 | var GaiaDialogProto = __webpack_require__(1).prototype; 63 | var component = __webpack_require__(2); 64 | 65 | /** 66 | * Exports 67 | */ 68 | module.exports = component.register('fxos-dialog-alert', { 69 | created: function() { 70 | this.setupShadowRoot(); 71 | this.els = { 72 | dialog: this.shadowRoot.querySelector('fxos-dialog') 73 | }; 74 | this.els.dialog.addEventListener('closed', 75 | GaiaDialogProto.hide.bind(this)); 76 | }, 77 | 78 | open: function(e) { 79 | return GaiaDialogProto.show.call(this) 80 | .then(() => this.els.dialog.open(e)); 81 | }, 82 | 83 | close: function() { 84 | return this.els.dialog.close() 85 | .then(GaiaDialogProto.hide.bind(this)); 86 | }, 87 | 88 | template: ` 89 | 90 |
91 |

92 |
93 |
94 | 95 |
96 |
97 | ` 113 | }); 114 | 115 | 116 | /***/ }, 117 | /* 1 */ 118 | /***/ function(module, exports) { 119 | 120 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__; 121 | 122 | /***/ }, 123 | /* 2 */ 124 | /***/ function(module, exports) { 125 | 126 | module.exports = __WEBPACK_EXTERNAL_MODULE_2__; 127 | 128 | /***/ } 129 | /******/ ]) 130 | }); 131 | ; -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-text-input/src/fxos-text-input-multiline.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Dependencies 4 | */ 5 | 6 | var component = require('fxos-component'); 7 | 8 | /** 9 | * Exports 10 | */ 11 | 12 | module.exports = component.register('fxos-text-input-multiline', { 13 | created() { 14 | this.setupShadowRoot(); 15 | 16 | this.els = { 17 | inner: this.shadowRoot.querySelector('.inner'), 18 | field: this.shadowRoot.querySelector('textarea') 19 | }; 20 | 21 | this.type = this.getAttribute('type'); 22 | this.disabled = this.hasAttribute('disabled'); 23 | this.placeholder = this.getAttribute('placeholder'); 24 | this.required = this.getAttribute('required'); 25 | this.value = this.getAttribute('value'); 26 | }, 27 | 28 | clear(e) { 29 | this.value = ''; 30 | }, 31 | 32 | attrs: { 33 | placeholder: { 34 | get() { return this.els.field.placeholder; }, 35 | set(value) { this.els.field.placeholder = value || ''; } 36 | }, 37 | 38 | value: { 39 | get() { return this.els.field.value; }, 40 | set(value) { this.els.field.value = value; } 41 | }, 42 | 43 | required: { 44 | get() { return this.els.field.required; }, 45 | set(value) { this.els.field.required = value; } 46 | }, 47 | 48 | disabled: { 49 | get() { return this.els.field.disabled; }, 50 | set(value) { 51 | value = !!(value === '' || value); 52 | this.els.field.disabled = value; 53 | } 54 | } 55 | }, 56 | 57 | template: `
58 | 59 |
60 | 61 |
62 |
63 |
64 |
65 | ` 155 | }); 156 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-dialog/fxos-dialog-confirm.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(require("./fxos-dialog"), require("fxos-component")); 4 | else if(typeof define === 'function' && define.amd) 5 | define(["./fxos-dialog", "fxos-component"], factory); 6 | else if(typeof exports === 'object') 7 | exports["FXOSDialogConfirm"] = factory(require("./fxos-dialog"), require("fxos-component")); 8 | else 9 | root["FXOSDialogConfirm"] = factory(root["FXOSDialog"], root["fxosComponent"]); 10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__) { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | 39 | 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | 58 | /** 59 | * Dependencies 60 | */ 61 | 62 | var GaiaDialogProto = __webpack_require__(1).prototype; 63 | var component = __webpack_require__(2); 64 | 65 | /** 66 | * Exports 67 | */ 68 | module.exports = component.register('fxos-dialog-confirm', { 69 | created: function() { 70 | this.setupShadowRoot(); 71 | 72 | this.els = { 73 | dialog: this.shadowRoot.querySelector('fxos-dialog'), 74 | submit: this.shadowRoot.querySelector('.submit'), 75 | cancel: this.shadowRoot.querySelector('.cancel') 76 | }; 77 | 78 | this.els.cancel.addEventListener('click', this.close.bind(this)); 79 | this.els.submit.addEventListener('click', this.close.bind(this)); 80 | }, 81 | 82 | open: function(e) { 83 | return GaiaDialogProto.show.call(this) 84 | .then(() => this.els.dialog.open(e)); 85 | }, 86 | 87 | close: function() { 88 | // First close (hide) inner dialog and then the container. 89 | return this.els.dialog.close() 90 | .then(GaiaDialogProto.hide.bind(this)); 91 | }, 92 | 93 | template: ` 94 | 95 |
96 |

97 |
98 |
99 | 100 | 101 |
102 |
103 | 104 | ` 121 | }); 122 | 123 | 124 | /***/ }, 125 | /* 1 */ 126 | /***/ function(module, exports) { 127 | 128 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__; 129 | 130 | /***/ }, 131 | /* 2 */ 132 | /***/ function(module, exports) { 133 | 134 | module.exports = __WEBPACK_EXTERNAL_MODULE_2__; 135 | 136 | /***/ } 137 | /******/ ]) 138 | }); 139 | ; -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-dialog/README.md: -------------------------------------------------------------------------------- 1 | # <fxos-dialog> [![](https://travis-ci.org/fxos-components/fxos-dialog.svg)](https://travis-ci.org/fxos-components/fxos-dialog) 2 | 3 | Several types of dialogs, including alert, confirm, prompt, action, select, and menu. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | $ npm install fxos-dialog 9 | ``` 10 | 11 | ## Platform dependencies 12 | 13 | - CSS Custom Properties 14 | - Custom Elements 15 | - Shadow DOM 16 | 17 | ## Alert Dialog 18 | 19 | Include folowing files in HTML 20 | 21 | ```html 22 | 23 | 24 | 25 | ``` 26 | 27 | ### Usage 28 | 29 | ```html 30 | No SIM card is present 31 | ``` 32 | 33 | ## Confirm Dialog 34 | 35 | Include folowing files in HTML 36 | 37 | ```html 38 | 39 | 40 | 41 | ``` 42 | 43 | ### Usage 44 | 45 | ```html 46 | Are you sure you want to delete this contact? 47 | ``` 48 | 49 | ## Prompt Dialog 50 | 51 | Include folowing files in HTML 52 | 53 | ```html 54 | 55 | 56 | 57 | ``` 58 | 59 | ### Usage 60 | 61 | ```html 62 | Device name 63 | ``` 64 | 65 | ## Action Dialog 66 | 67 | Include folowing files in HTML 68 | 69 | ```html 70 | 71 | 72 | 73 | ``` 74 | 75 | ### Usage 76 | 77 | ```html 78 | 79 |

Descriptions...

80 | 81 | 82 |
83 | ``` 84 | 85 | ## Select Dialog 86 | 87 | Include folowing files in HTML 88 | 89 | ```html 90 | 91 | 92 | 93 | ``` 94 | 95 | ### Usage 96 | 97 | ```html 98 | 99 |

Ring tone

100 |
  • Classic prism
  • 101 |
  • Wallphone
  • 102 |
    103 | ``` 104 | 105 | #### Multiple Select 106 | 107 | Add `multiple` attribute in `fxos-dialog-select` element to enable multiple selection. 108 | 109 | ```html 110 | 111 |

    Ring tone

    112 |
  • Classic prism
  • 113 |
  • Wallphone
  • 114 |
    115 | ``` 116 | 117 | ## Menu Dialog 118 | 119 | Include folowing files in HTML 120 | 121 | ```html 122 | 123 | 124 | 125 | ``` 126 | 127 | ### Usage 128 | 129 | ```html 130 | 131 | 132 | 133 | 134 | 135 | 136 | ``` 137 | 138 | ## Examples 139 | 140 | - [Example](http://fxos-components.github.io/fxos-dialog/) 141 | 142 | ## Readiness 143 | 144 | - [ ] Accessibility 145 | - [ ] Test Coverage 146 | - [ ] Performance 147 | - [ ] Visual/UX 148 | - [ ] RTL 149 | 150 | ## Developing locally 151 | 152 | 1. `git clone https://github.com/fxos-components/fxos-switch.git` 153 | 2. `cd fxos-switch` 154 | 3. `npm install` (NPM3) 155 | 4. `npm start` 156 | 157 | ## Tests 158 | 159 | 1. Ensure Firefox Nightly is installed on your machine. (Visit: [Firefox Nightly Page](https://nightly.mozilla.org/)) 160 | 2. To run unit tests you need npm >= 3 installed. 161 | 3. `$ npm install` 162 | 4. `$ npm run test-unit` 163 | 164 | If you would like tests to run on file change use: 165 | 166 | `$ npm run test-unit-dev` 167 | 168 | If your would like run integration tests, use: 169 | 170 | `$ export FIREFOX_NIGHTLY_BIN=/absolute/path/to/nightly/firefox-bin` 171 | `$ npm run test-integration` 172 | 173 | ## Lint check 174 | 175 | Run lint check with command: 176 | 177 | `$ npm run test-lint` 178 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-theme/fxos-theme.css: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Colors 4 | */ 5 | 6 | html { 7 | 8 | /* greys */ 9 | --fxos-color-grey-1: #fff; 10 | --fxos-color-grey-2: #f4f4f4; 11 | --fxos-color-grey-3: #e7e7e7; 12 | --fxos-color-grey-4: #c7c7c7; 13 | --fxos-color-grey-5: #a6a6a6; 14 | --fxos-color-grey-6: #858585; 15 | --fxos-color-grey-7: #5f5f5f; 16 | --fxos-color-grey-8: #4d4d4d; 17 | --fxos-color-grey-9: #333; 18 | 19 | /* brand/theme */ 20 | --fxos-color-darkblue: #00539f; 21 | --fxos-color-blue: #00caf2; 22 | --fxos-color-turquoise: #27c8c2; 23 | --fxos-color-darkorange: #e66000; 24 | --fxos-color-orange: #ff9500; 25 | --fxos-color-yellow: #ffcb00; 26 | --fxos-color-violet: #c40c84; 27 | 28 | /* semantic */ 29 | --fxos-color-warning: #fbbd3c; 30 | --fxos-color-destructive: #e2443a; 31 | --fxos-color-preffered: #00ba91; 32 | } 33 | 34 | /** 35 | * Default (system) 36 | */ 37 | 38 | html { 39 | 40 | /* backgrounds */ 41 | --fxos-background: var(--fxos-color-grey-2); 42 | --fxos-background-plus: white; 43 | --fxos-background-minus: var(--fxos-color-grey-6); 44 | --fxos-background-minus-minus: var(--fxos-color-grey-5); 45 | 46 | /* colors */ 47 | --fxos-brand-color: var(--fxos-color-blue); 48 | --fxos-border-color: var(--fxos-color-grey-3); 49 | --fxos-color: var(--fxos-color-grey-8); 50 | --fxos-color-minus: var(--fxos-color-grey-5); 51 | --fxos-title-color: var(--fxos-background-minus); 52 | 53 | /* misc */ 54 | --fxos-transition-duration: 200ms; 55 | 56 | /* fxos-button */ 57 | --fxos-button-background: var(--fxos-background-plus); 58 | 59 | /* fxos-switch */ 60 | --fxos-switch-background: var(--fxos-background-minus-minus); 61 | --fxos-switch-handle-background: var(--fxos-background-plus); 62 | 63 | /* fxos-checkbox */ 64 | --fxos-checkbox-border-color: var(--fxos-background-minus-minus); 65 | --fxos-checkbox-background: var(--fxos-background-plus); 66 | 67 | /* fxos-header */ 68 | --fxos-header-color: var(--fxos-title-color); 69 | --fxos-header-button-color: var(--fxos-brand-color); 70 | --fxos-header-disabled-color: var(--fxos-color-grey-4); 71 | 72 | /* fxos-text-input */ 73 | --fxos-text-input-background: var(--fxos-background-plus); 74 | --fxos-text-input-placeholder-color: var(--fxos-color-minus); 75 | } 76 | 77 | /** 78 | * Productivity 79 | */ 80 | 81 | .fxos-theme-productivity, 82 | .fxos-theme-communications { 83 | 84 | /* backgrounds */ 85 | --fxos-background: white; 86 | --fxos-background-minus: var(--fxos-color-grey-5); 87 | 88 | /* colors */ 89 | --fxos-brand-color: var(--fxos-color-orange); 90 | --fxos-border-color: var(--fxos-color-grey-3); 91 | --fxos-color: var(--fxos-color-grey-8); 92 | --fxos-color-minus: var(--fxos-color-grey-4); 93 | 94 | /* fxos-header */ 95 | --fxos-header-color: white; 96 | --fxos-header-icon-color: white; 97 | --fxos-header-button-color: white; 98 | --fxos-header-disabled-color: rgba(255,255,255,0.3); 99 | --fxos-header-background: var(--fxos-brand-color); 100 | 101 | /* fxos-button */ 102 | --fxos-button-color: var(--fxos-color); 103 | --fxos-button-background: var(--fxos-color-grey-2); 104 | 105 | /* fxos-switch */ 106 | --fxos-switch-background: var(--fxos-background-minus); 107 | --fxos-switch-handle-background: white; 108 | 109 | /* fxos-checkbox */ 110 | --fxos-checkbox-border-color: var(--fxos-background-minus-minus); 111 | 112 | /* fxos-dialog */ 113 | --fxos-dialog-background: rgba(199,199,199,0.85); 114 | } 115 | 116 | /** 117 | * Communications 118 | */ 119 | 120 | .fxos-theme-communications { 121 | --fxos-brand-color: var(--fxos-color-turquoise); 122 | } 123 | 124 | /** 125 | * Media 126 | */ 127 | 128 | .fxos-theme-media { 129 | 130 | /* backgrounds */ 131 | --fxos-background: var(--fxos-color-grey-9); 132 | --fxos-background-plus: var(--fxos-color-grey-8); 133 | --fxos-background-minus: #2B2B2B; 134 | --fxos-background-minus-minus: #1a1a1a; 135 | 136 | /* colors */ 137 | --fxos-color: white; 138 | --fxos-brand-color: var(--fxos-color-blue); 139 | --fxos-border-color: var(--fxos-color-grey-8); 140 | --fxos-title-color: var(--fxos-color); 141 | 142 | /* fxos-header */ 143 | --fxos-header-button-color: var(--fxos-brand-color); 144 | --fxos-header-action-button-color: var(--fxos-color); 145 | --fxos-header-disabled-color: rgba(255,255,255,0.3); 146 | 147 | /* fxos-checkbox */ 148 | --fxos-checkbox-border-color: var(--fxos-background-minus-minus); 149 | --fxos-checkbox-background: var(--fxos-background-plus); 150 | 151 | /* fxos-switch */ 152 | --fxos-switch-background: var(--fxos-background-minus-minus); 153 | --fxos-switch-handle-background: var(--fxos-background-plus); 154 | 155 | /* fxos-button */ 156 | --fxos-button-background: var(--fxos-background-plus); 157 | --fxos-button-box-shadow: none; 158 | 159 | /* fxos-text-input */ 160 | --fxos-text-input-background: var(--fxos-background-minus); 161 | 162 | /* fxos-dialog */ 163 | --fxos-dialog-background: rgba(199,199,199,0.5); 164 | } 165 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-text-input/src/fxos-text-input-pin.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Dependencies 4 | */ 5 | 6 | var component = require('fxos-component'); 7 | 8 | /** 9 | * Locals 10 | */ 11 | 12 | var DEFAULT_LENGTH = 4; 13 | 14 | /** 15 | * Exports 16 | */ 17 | 18 | module.exports = component.register('fxos-text-input-pin', { 19 | created() { 20 | this.setupShadowRoot(); 21 | 22 | this.els = { 23 | inner: this.shadowRoot.querySelector('.inner'), 24 | fields: this.shadowRoot.querySelector('.fields'), 25 | input: this.shadowRoot.querySelector('input') 26 | }; 27 | 28 | this.disabled = this.hasAttribute('disabled'); 29 | this.length = this.getAttribute('length') || DEFAULT_LENGTH; 30 | 31 | this.addEventListener('keyup', () => this.updateCells()); 32 | }, 33 | 34 | updateCells() { 35 | var l = this.els.input.value.length; 36 | this.els.cells.forEach((cell, i) => { 37 | cell.classList.toggle('populated', i < l); 38 | cell.classList.toggle('focused', i == l); 39 | }); 40 | }, 41 | 42 | onBackspace(e) { 43 | var input = e.target; 44 | var empty = !input.value; 45 | var previous = input.previousElementSibling; 46 | 47 | if (!empty && previous) { 48 | previous.clear(); 49 | previous.focus(); 50 | } else if (empty && previous) { 51 | previous.focus(); 52 | } 53 | }, 54 | 55 | setupFields() { 56 | this.els.fields.innerHTML = ''; 57 | this.els.cells = []; 58 | 59 | for (var i = 0, l = this.length; i < l; i++) { 60 | var el = document.createElement('div'); 61 | el.className = 'cell'; 62 | this.els.fields.appendChild(el); 63 | this.els.cells.push(el); 64 | } 65 | }, 66 | 67 | clear(e) { 68 | this.value = ''; 69 | this.updateCells(); 70 | }, 71 | 72 | focus(e) { 73 | this.els.input.focus(); 74 | }, 75 | 76 | /** 77 | * Attributes 78 | */ 79 | 80 | attrs: { 81 | length: { 82 | get() { return this._length; }, 83 | set(value) { 84 | value = Number(value); 85 | this._length = value; 86 | this.els.input.setAttribute('maxlength', this.length); 87 | this.setupFields(); 88 | } 89 | }, 90 | 91 | value: { 92 | get() { return this.els.input.value; }, 93 | set(value) { this.els.input.value = value; } 94 | }, 95 | 96 | disabled: { 97 | get() { return this.els.input.disabled; }, 98 | set(value) { 99 | value = !!(value === '' || value); 100 | this.els.input.disabled = value; 101 | } 102 | } 103 | }, 104 | 105 | template: ` 106 |
    107 | 108 |
    109 | 110 |
    111 |
    112 |
    113 | ` 212 | }); 213 | -------------------------------------------------------------------------------- /examples/demo/js/bacon-source.js: -------------------------------------------------------------------------------- 1 | (function(exports) { 2 | var listSize = 1042; 3 | var sectionCount = 25; 4 | 5 | var headerHeight = 32; 6 | var itemHeight = 88; 7 | 8 | function makeContent(prefix, name) { 9 | return { 10 | title: prefix + ' Bacon ipsum dolor ' + 11 | Date.now().toString().slice(7, -1), 12 | body: 'Turkey pig boudin ' + name.toLowerCase() + 13 | ' de mignon drums and all ' + prefix + '.' 14 | } 15 | } 16 | 17 | exports.BaconSource = function BaconSource(container, ul) { 18 | this.sections = []; 19 | this.contentMap = new Map(); 20 | this._cachedLength = null; 21 | this.container = container; 22 | this.list = ul; 23 | 24 | for (var i = 0; i < sectionCount; i++) { 25 | this.sections.push('Section ' + i); 26 | } 27 | 28 | this.sections.forEach(function(name) { 29 | var content = []; 30 | for (var i = 0; i < (Math.random() * listSize); i++) { 31 | content.push(makeContent(i, name)); 32 | } 33 | this.contentMap.set(name, content); 34 | }, this); 35 | }; 36 | 37 | exports.BaconSource.prototype = { 38 | populateItem: function(item, i) { 39 | // Simulates the data source not being ready to populate 40 | /*if (parseInt(Date.now() / 10) % 8 === 0) {*/ 41 | /*return new Promise(function(resolve, reject) {*/ 42 | /*setTimeout(resolve, Math.random() * 1000);*/ 43 | /*});*/ 44 | /*}*/ 45 | 46 | var title = item.firstChild; 47 | var body = title.nextSibling; 48 | var record = this.getRecordAt(i); 49 | 50 | title.firstChild.data = record.title; 51 | body.firstChild.data = record.body; 52 | }, 53 | 54 | getSections: function() { 55 | return this.contentMap.keys(); 56 | }, 57 | 58 | getSectionHeaderHeight: function() { 59 | return headerHeight; 60 | }, 61 | 62 | getFullSectionHeight: function(key) { 63 | return this.contentMap.get(key).length * itemHeight; 64 | }, 65 | 66 | fullSectionLength: function(key) { 67 | return this.contentMap(key).length; 68 | }, 69 | 70 | getRecordAt: function(index) { 71 | for (var section of this.contentMap.values()) { 72 | if (index < section.length) { 73 | return section[index]; 74 | } 75 | index -= section.length; 76 | } 77 | }, 78 | 79 | getSectionFor: function(index) { 80 | for (var entry of this.contentMap.entries()) { 81 | if (index < entry[1].length) { 82 | return entry[0]; 83 | } 84 | index -= entry[1].length; 85 | } 86 | }, 87 | 88 | getIndexAtPosition: function(pos) { 89 | var index = 0; 90 | for (var section of this.contentMap.values()) { 91 | pos -= headerHeight; 92 | var sectionHeight = section.length * itemHeight; 93 | if (pos > sectionHeight) { 94 | pos -= sectionHeight; 95 | index += section.length; 96 | continue; 97 | } 98 | for (var item of section) { 99 | pos -= itemHeight; 100 | index++; 101 | if (pos <= 0 || index === this.fullLength() - 1) { 102 | return index; 103 | } 104 | } 105 | } 106 | }, 107 | 108 | getPositionForIndex: function(index) { 109 | var top = 0; 110 | for (var section of this.contentMap.values()) { 111 | top += headerHeight; 112 | if (index < section.length) { 113 | top += index * itemHeight; 114 | return top; 115 | } 116 | index -= section.length; 117 | top += section.length * itemHeight; 118 | } 119 | }, 120 | 121 | getFullLength: function() { 122 | if (this._cachedLength) { 123 | return this._cachedLength; 124 | } 125 | 126 | var length = 0; 127 | for (var section of this.contentMap.values()) { 128 | length += section.length; 129 | } 130 | this._cachedLength = length; 131 | return length; 132 | }, 133 | 134 | getItemHeight: function() { 135 | return itemHeight; 136 | }, 137 | 138 | getFullHeight: function() { 139 | var height = 0; 140 | for (var section of this.contentMap.values()) { 141 | height += headerHeight + section.length * itemHeight; 142 | } 143 | return height; 144 | }, 145 | 146 | insertAtIndex: function(index, record, toSection) { 147 | this._cachedLength = null; 148 | for (var entry of this.contentMap.entries()) { 149 | if (index < entry[1].length || entry[0] === toSection) { 150 | return entry[1].splice(index, 0, record); 151 | } 152 | index -= entry[1].length; 153 | } 154 | }, 155 | 156 | replaceAtIndex: function(index, record) { 157 | for (var section of this.contentMap.values()) { 158 | if (index < section.length) { 159 | return section.splice(index, 1, record); 160 | } 161 | index -= section.length; 162 | } 163 | }, 164 | 165 | removeAtIndex: function(index) { 166 | this._cachedLength = null; 167 | 168 | for (var section of this.contentMap.values()) { 169 | if (index < section.length) { 170 | return section.splice(index, 1)[0]; 171 | } 172 | index -= section.length; 173 | } 174 | } 175 | }; 176 | })(window); 177 | 178 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-dialog/fxos-dialog-menu.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(require("./fxos-dialog"), require("fxos-component")); 4 | else if(typeof define === 'function' && define.amd) 5 | define(["./fxos-dialog", "fxos-component"], factory); 6 | else if(typeof exports === 'object') 7 | exports["FXOSDialogMenu"] = factory(require("./fxos-dialog"), require("fxos-component")); 8 | else 9 | root["FXOSDialogMenu"] = factory(root["FXOSDialog"], root["fxosComponent"]); 10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__) { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | 39 | 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | 58 | /** 59 | * Dependencies 60 | */ 61 | 62 | var GaiaDialogProto = __webpack_require__(1).prototype; 63 | var component = __webpack_require__(2); 64 | 65 | /** 66 | * Exports 67 | */ 68 | module.exports = component.register('fxos-dialog-menu', { 69 | created: function() { 70 | this.setupShadowRoot(); 71 | this.els = { 72 | dialog: this.shadowRoot.querySelector('fxos-dialog'), 73 | items: this.shadowRoot.querySelector('.items') 74 | }; 75 | 76 | this.els.dialog.addEventListener('closed', 77 | GaiaDialogProto.hide.bind(this)); 78 | 79 | setTimeout(() => this.makeAccessible()); 80 | }, 81 | 82 | makeAccessible() { 83 | this.els.items.setAttribute('role', 'menu'); 84 | [].forEach.call(this.querySelectorAll('button'), 85 | menuitem => menuitem.setAttribute('role', 'menuitem')); 86 | }, 87 | 88 | open: function(e) { 89 | return GaiaDialogProto.show.call(this) 90 | .then(() => this.els.dialog.open(e)); 91 | }, 92 | 93 | close: function() { 94 | return this.els.dialog.close() 95 | .then(GaiaDialogProto.hide.bind(this)); 96 | }, 97 | 98 | template: ` 99 | 100 |
    101 |
    102 | ` 155 | }); 156 | 157 | 158 | /***/ }, 159 | /* 1 */ 160 | /***/ function(module, exports) { 161 | 162 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__; 163 | 164 | /***/ }, 165 | /* 2 */ 166 | /***/ function(module, exports) { 167 | 168 | module.exports = __WEBPACK_EXTERNAL_MODULE_2__; 169 | 170 | /***/ } 171 | /******/ ]) 172 | }); 173 | ; -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-dialog/fxos-dialog-action.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(require("./fxos-dialog"), require("fxos-component")); 4 | else if(typeof define === 'function' && define.amd) 5 | define(["./fxos-dialog", "fxos-component"], factory); 6 | else if(typeof exports === 'object') 7 | exports["FXOSDialogAction"] = factory(require("./fxos-dialog"), require("fxos-component")); 8 | else 9 | root["FXOSDialogAction"] = factory(root["FXOSDialog"], root["fxosComponent"]); 10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__) { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | 39 | 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | 58 | /** 59 | * Dependencies 60 | */ 61 | 62 | var GaiaDialogProto = __webpack_require__(1).prototype; 63 | var component = __webpack_require__(2); 64 | 65 | /** 66 | * Exports 67 | */ 68 | module.exports = component.register('fxos-dialog-action', { 69 | created: function() { 70 | this.setupShadowRoot(); 71 | 72 | this.els = { 73 | dialog: this.shadowRoot.querySelector('fxos-dialog'), 74 | submit: this.shadowRoot.querySelector('.submit'), 75 | cancel: this.shadowRoot.querySelector('.cancel') 76 | }; 77 | 78 | this.els.dialog.addEventListener('closed', 79 | GaiaDialogProto.hide.bind(this)); 80 | }, 81 | 82 | open: function(e) { 83 | return GaiaDialogProto.show.call(this) 84 | .then(() => this.els.dialog.open(e)); 85 | }, 86 | 87 | close: function() { 88 | // First close (hide) inner dialog and then the container. 89 | return this.els.dialog.close() 90 | .then(GaiaDialogProto.hide.bind(this)); 91 | }, 92 | 93 | template: ` 94 | 95 |
    96 | 97 |
    98 | 99 | 100 |
    101 | ` 169 | }); 170 | 171 | 172 | /***/ }, 173 | /* 1 */ 174 | /***/ function(module, exports) { 175 | 176 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__; 177 | 178 | /***/ }, 179 | /* 2 */ 180 | /***/ function(module, exports) { 181 | 182 | module.exports = __WEBPACK_EXTERNAL_MODULE_2__; 183 | 184 | /***/ } 185 | /******/ ]) 186 | }); 187 | ; -------------------------------------------------------------------------------- /examples/demo/sections/js/bacon-source.js: -------------------------------------------------------------------------------- 1 | (function(exports) { 2 | /*global popel*/ 3 | 4 | 'use strict'; 5 | 6 | var listSize = 1042; 7 | var sectionCount = 25; 8 | 9 | var headerHeight = 32; 10 | var itemHeight = 88; 11 | 12 | function makeContent(prefix, name) { 13 | return { 14 | title: prefix + ' Bacon ipsum dolor ' + 15 | Date.now().toString().slice(7, -1), 16 | body: 'Turkey pig boudin ' + name.toLowerCase() + 17 | ' de mignon drums and all ' + prefix + '.' 18 | }; 19 | } 20 | 21 | exports.BaconSource = function BaconSource(container) { 22 | this.container = container; 23 | this.sections = []; 24 | this.contentMap = new Map(); 25 | this._cachedLength = null; 26 | 27 | for (var i = 0; i < sectionCount; i++) { 28 | this.sections.push('Section ' + i); 29 | } 30 | 31 | this.sections.forEach(function(name) { 32 | var content = []; 33 | for (var i = 0; i < (Math.random() * listSize); i++) { 34 | content.push(makeContent(i, name)); 35 | } 36 | this.contentMap.set(name, content); 37 | }, this); 38 | }; 39 | 40 | exports.BaconSource.prototype = { 41 | createItem: function() { 42 | return popel('
  • ${title}

    ${body}

    ' + 43 | '
    ↕︎
  • '); 44 | }, 45 | 46 | populateItem: function(el, i) { 47 | var record = this.getRecordAt(i); 48 | popel.populate(el, record); 49 | }, 50 | 51 | createSection: function() { 52 | var section = document.createElement('section'); 53 | section.innerHTML = '

    '; 54 | return section; 55 | }, 56 | 57 | populateSection: function(el, section, i) { 58 | var title = el.firstChild; 59 | var height = this.getFullSectionHeight(section); 60 | var background = title.nextSibling; 61 | 62 | background.style.height = height + 'px'; 63 | title.firstChild.data = section; 64 | }, 65 | 66 | getSections: function() { 67 | var sections = []; 68 | for (var section of this.contentMap.keys()) { 69 | sections.push(section); 70 | } 71 | return sections; 72 | }, 73 | 74 | getSectionHeaderHeight: function() { 75 | return headerHeight; 76 | }, 77 | 78 | getFullSectionHeight: function(key) { 79 | return this.contentMap.get(key).length * itemHeight; 80 | }, 81 | 82 | getFullSectionLength: function(key) { 83 | return this.contentMap(key).length; 84 | }, 85 | 86 | getRecordAt: function(index) { 87 | for (var section of this.contentMap.values()) { 88 | if (index < section.length) { 89 | return section[index]; 90 | } 91 | index -= section.length; 92 | } 93 | }, 94 | 95 | getSectionFor: function(index) { 96 | for (var entry of this.contentMap.entries()) { 97 | if (index < entry[1].length) { 98 | return entry[0]; 99 | } 100 | index -= entry[1].length; 101 | } 102 | }, 103 | 104 | getIndexAtPosition: function(pos) { 105 | var index = 0; 106 | 107 | for (var section of this.contentMap.values()) { 108 | pos -= headerHeight; 109 | var sectionHeight = section.length * itemHeight; 110 | 111 | if (pos > sectionHeight) { 112 | pos -= sectionHeight; 113 | index += section.length; 114 | continue; 115 | } 116 | 117 | for (var item of section) { 118 | if (pos <= 0 || index === this.getFullLength() - 1) { 119 | return index; 120 | } 121 | 122 | pos -= itemHeight; 123 | index++; 124 | } 125 | } 126 | }, 127 | 128 | getPositionForIndex: function(index) { 129 | var top = 0; 130 | for (var section of this.contentMap.values()) { 131 | top += headerHeight; 132 | if (index < section.length) { 133 | top += index * itemHeight; 134 | return top; 135 | } 136 | index -= section.length; 137 | top += section.length * itemHeight; 138 | } 139 | }, 140 | 141 | getFullLength: function() { 142 | if (this._cachedLength) { 143 | return this._cachedLength; 144 | } 145 | 146 | var length = 0; 147 | for (var section of this.contentMap.values()) { 148 | length += section.length; 149 | } 150 | this._cachedLength = length; 151 | return length; 152 | }, 153 | 154 | getItemHeight: function() { 155 | return itemHeight; 156 | }, 157 | 158 | getFullHeight: function() { 159 | var height = 0; 160 | for (var section of this.contentMap.values()) { 161 | height += headerHeight + section.length * itemHeight; 162 | } 163 | return height; 164 | }, 165 | 166 | insertAtIndex: function(index, record, toSection) { 167 | this._cachedLength = null; 168 | for (var entry of this.contentMap.entries()) { 169 | if (index < entry[1].length || entry[0] === toSection) { 170 | return entry[1].splice(index, 0, record); 171 | } 172 | index -= entry[1].length; 173 | } 174 | }, 175 | 176 | replaceAtIndex: function(index, record) { 177 | for (var section of this.contentMap.values()) { 178 | if (index < section.length) { 179 | return section.splice(index, 1, record); 180 | } 181 | index -= section.length; 182 | } 183 | }, 184 | 185 | removeAtIndex: function(index) { 186 | this._cachedLength = null; 187 | 188 | for (var section of this.contentMap.values()) { 189 | if (index < section.length) { 190 | return section.splice(index, 1)[0]; 191 | } 192 | index -= section.length; 193 | } 194 | } 195 | }; 196 | })(window); 197 | 198 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-text-input/fxos-text-input-multiline.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(require("fxos-component")); 4 | else if(typeof define === 'function' && define.amd) 5 | define(["fxos-component"], factory); 6 | else if(typeof exports === 'object') 7 | exports["FXOSTextInputMultiline"] = factory(require("fxos-component")); 8 | else 9 | root["FXOSTextInputMultiline"] = factory(root["fxosComponent"]); 10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__) { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | 39 | 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | 58 | /** 59 | * Dependencies 60 | */ 61 | 62 | var component = __webpack_require__(1); 63 | 64 | /** 65 | * Exports 66 | */ 67 | 68 | module.exports = component.register('fxos-text-input-multiline', { 69 | created() { 70 | this.setupShadowRoot(); 71 | 72 | this.els = { 73 | inner: this.shadowRoot.querySelector('.inner'), 74 | field: this.shadowRoot.querySelector('textarea') 75 | }; 76 | 77 | this.type = this.getAttribute('type'); 78 | this.disabled = this.hasAttribute('disabled'); 79 | this.placeholder = this.getAttribute('placeholder'); 80 | this.required = this.getAttribute('required'); 81 | this.value = this.getAttribute('value'); 82 | }, 83 | 84 | clear(e) { 85 | this.value = ''; 86 | }, 87 | 88 | attrs: { 89 | placeholder: { 90 | get() { return this.els.field.placeholder; }, 91 | set(value) { this.els.field.placeholder = value || ''; } 92 | }, 93 | 94 | value: { 95 | get() { return this.els.field.value; }, 96 | set(value) { this.els.field.value = value; } 97 | }, 98 | 99 | required: { 100 | get() { return this.els.field.required; }, 101 | set(value) { this.els.field.required = value; } 102 | }, 103 | 104 | disabled: { 105 | get() { return this.els.field.disabled; }, 106 | set(value) { 107 | value = !!(value === '' || value); 108 | this.els.field.disabled = value; 109 | } 110 | } 111 | }, 112 | 113 | template: `
    114 | 115 |
    116 | 117 |
    118 |
    119 |
    120 |
    121 | ` 211 | }); 212 | 213 | 214 | /***/ }, 215 | /* 1 */ 216 | /***/ function(module, exports) { 217 | 218 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__; 219 | 220 | /***/ } 221 | /******/ ]) 222 | }); 223 | ; -------------------------------------------------------------------------------- /examples/demo/node_modules/fastlist/README.md: -------------------------------------------------------------------------------- 1 | # Fast List [![](https://travis-ci.org/fxos-components/fastlist.svg)](https://travis-ci.org/fxos-components/fastlist) 2 | 3 | ## Installation 4 | 5 | ```bash 6 | npm install fastlist` 7 | ``` 8 | 9 | The FastList is a virtual-list implementation based on the DomScheduler. See [``](https://github.com/fxos-components/fxos-fastlist) for the simpler, more opinionated web-component. 10 | 11 | The content of the list comes from a `DataSource` that needs to implement the API described 12 | below. When the content is edited from the list "Edit mode", the list will trigger calls to the source itself. 13 | 14 | But there's no observation going on, so if the content changes for other reasons the list needs to be made aware of that. 15 | 16 | ```js 17 | var myList = new FastList({ 18 | 19 | /** 20 | * The element your list sits inside. 21 | * NOTE: Must be `position: relative|absolute` 22 | * @type {HTMLElement} 23 | */ 24 | container: document.querySelector('.my-container'), 25 | 26 | /** 27 | * Should return a unique element to 28 | * be used as a list item. 29 | * @return {HTMLElement} 30 | */ 31 | createItem: function() {}, 32 | 33 | /** 34 | * Should return a unique element to 35 | * be used as a section. 36 | * @return {HTMLElement} 37 | */ 38 | createSection: function() {}, 39 | 40 | /** 41 | * Called each time a list item needs rendering. 42 | * @param {HTMLElement} el Your listTemplate 43 | * @param {Number} index 44 | */ 45 | populateItem: function(el, index) { ... }, 46 | 47 | /** 48 | * Called when the ressources allows it to do more expensive rendering 49 | * (ie. images) 50 | * This method isn't mandatory. 51 | * @param {HTMLElement} el Your listTemplate 52 | * @param {Number} index 53 | */ 54 | populateItemDetail: function(el, index) { ... }, 55 | 56 | /** 57 | * Called when an item is recycled to undo/cleanup the detail 58 | * rendering 59 | * This method isn't mandatory. 60 | * @param {HTMLElement} el Your listTemplate 61 | */ 62 | unpopulateItemDetail: function(el) { ... }, 63 | 64 | /** 65 | * Called each time a section needs rendering. 66 | * @param {HTMLElement} el Your sectionTemplate 67 | * @param {Section} section 68 | * @param {Number} index 69 | */ 70 | populateSection: function(el, section, index) { ... } 71 | 72 | /** 73 | * Should return a list of sections. 74 | * Return empty array if not using sections. 75 | * @return {Array} 76 | */ 77 | getSections() { ... }, 78 | 79 | /** 80 | * Should return the height of the section header. 81 | * Can return 0 is not using sections. 82 | * @return {Number} 83 | */ 84 | getSectionHeaderHeight() { ... }, 85 | 86 | /** 87 | * Should return the height of all 88 | * the items in a section. 89 | * @return {Number} 90 | */ 91 | getFullSectionHeight() { ... }, 92 | 93 | /** 94 | * Should return the total number of sections. 95 | * @return {Number} 96 | */ 97 | getFullSectionLength() { ... }, 98 | 99 | /** 100 | * Should return the section data for the item. 101 | * @return {*} 102 | */ 103 | getSectionFor(index) { ... }, 104 | 105 | /** 106 | * Should return the item at index. 107 | * @param {Number} index 108 | * @return {Object} 109 | */ 110 | getRecordAt: function(index) { ... }, 111 | 112 | /** 113 | * Should return the item index at 114 | * the given y-offset position. 115 | 116 | * @param {Number} pos 117 | * @return {Index} 118 | */ 119 | getIndexAtPosition: function(pos) { ... }, 120 | 121 | /** 122 | * Should return the y-offset of 123 | * the given item index. 124 | * @param {Number} index 125 | * @return {Number} 126 | */ 127 | getPositionForIndex: function(index) { ... }, 128 | 129 | /** 130 | * Should return the full list length. 131 | * @return {Number} 132 | */ 133 | getFullLength: function() { ... }, 134 | 135 | /** 136 | * Should return the item px height. 137 | * @return {Number} 138 | */ 139 | getItemHeight: function() { ... }, 140 | 141 | /** 142 | * Should return the full height of the list 143 | * including all items and section headers. 144 | * @return {Number} 145 | */ 146 | getFullHeight: function() { ... }, 147 | 148 | /** 149 | * An optional parameter to allow you to 150 | * provide the list viewport height in 151 | * a more efficient way than .offsetHeight. 152 | * @return {Number} 153 | */ 154 | getViewportHeight: function() {}, 155 | 156 | /** 157 | * SHould insert the given record into the your 158 | * source data list at the given index. 159 | * @param {Number} index 160 | * @param {Object} record 161 | * @param {*} toSection 162 | */ 163 | insertAtIndex: function(index, record, toSection) { ... }, 164 | 165 | /** 166 | * Should replace a record in the source data 167 | * list at the given index. 168 | * @param {Number} index 169 | * @param {Object} record 170 | */ 171 | replaceAtIndex: function(index, record) { ... } 172 | }); 173 | ``` 174 | 175 | ## If the content is not ready by the time it needs to be rendered 176 | When the source is not ready to _populate_ an item, maybe because the 177 | IndexedDB cursor hasn't caught up with scrolling yet it should do the 178 | following. 179 | 180 | * return a Promise from `populateItem`, resolving once the content is 181 | ready 182 | * return `false` if it implements the `populateItemDetail` method 183 | 184 | Once the promise resolves, the list will try again to call `populateItem` / `populateItemDetail`. 185 | 186 | ## API 187 | 188 | ### Notifying of new content insertion 189 | 190 | ```js 191 | list.insertedAtIndex(0); 192 | ``` 193 | 194 | To insert with a nice transition. 195 | 196 | ```js 197 | list.reloadData() 198 | ``` 199 | 200 | For bigger, instantaneous changes. 201 | 202 | ### Scrolling 203 | 204 | ```js 205 | list.scrollTop 206 | ``` 207 | 208 | Will give you the *cached* scroll top position (not causing a reflow). 209 | 210 | ```js 211 | list.scrollInstantly(by) 212 | ``` 213 | 214 | Will do as it says. 215 | 216 | ```js 217 | list.updateListHeight() 218 | ``` 219 | 220 | Can be called if the number of items in the list has changed, it'll return a scheduler promise fulfilled after the mutation is executed. This will also cause the scrollbar to flash. 221 | 222 | ### Edit mode 223 | The edit mode support leaves in a plugin, to enable in you need to load 224 | `plugins/edit.js` and initialize the `FastList` as follow. 225 | ```js 226 | var list = new FastList(config).plugin(fastListEdit); 227 | ``` 228 | 229 | ```js 230 | /** 231 | * Toggles the edit mode on and off 232 | * 233 | * Returns the DomScheduler promise of the edit mode transition 234 | * 235 | * @return {Promise} 236 | */ 237 | list.toggleEditMode() 238 | ``` 239 | 240 | This method returns a promise, fulfilled once the transition is done. 241 | 242 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-dialog/src/fxos-dialog-select.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Dependencies 4 | */ 5 | 6 | var GaiaDialogProto = require('./fxos-dialog').prototype; 7 | var component = require('fxos-component'); 8 | 9 | /** 10 | * Exports 11 | */ 12 | module.exports = component.register('fxos-dialog-select', { 13 | created: function() { 14 | this.setupShadowRoot(); 15 | 16 | this.els = { 17 | dialog: this.shadowRoot.querySelector('fxos-dialog'), 18 | submit: this.shadowRoot.querySelector('.submit'), 19 | cancel: this.shadowRoot.querySelector('.cancel'), 20 | list: this.shadowRoot.querySelector('ul') 21 | }; 22 | 23 | this.multiple = this.hasAttribute('multiple'); 24 | this.els.list.addEventListener('click', this.onListClick.bind(this)); 25 | this.els.submit.addEventListener('click', this.close.bind(this)); 26 | this.els.cancel.addEventListener('click', this.close.bind(this)); 27 | 28 | setTimeout(() => this.makeAccessible()); 29 | }, 30 | 31 | makeAccessible() { 32 | this.els.list.setAttribute('role', 'listbox'); 33 | if (this.multiple) { 34 | this.els.list.setAttribute('aria-multiselectable', true); 35 | } 36 | 37 | [].forEach.call(this.options, option => { 38 | option.setAttribute('role', 'option'); 39 | option.setAttribute('tabindex', '0'); 40 | }); 41 | }, 42 | 43 | open: function(e) { 44 | return GaiaDialogProto.show.call(this) 45 | .then(() => this.els.dialog.open(e)); 46 | }, 47 | 48 | close: function() { 49 | return this.els.dialog.close() 50 | .then(GaiaDialogProto.hide.bind(this)); 51 | }, 52 | 53 | onListClick: function(e) { 54 | var el = e.target.closest('li'); 55 | var selected = el.getAttribute('aria-selected') === 'true'; 56 | 57 | if (this.multiple) { this.onChangeMultiple(el, selected); } 58 | else { this.onChange(el, selected); } 59 | }, 60 | 61 | onChange: function(el, selected) { 62 | this.clearSelected(); 63 | if (!selected) { el.setAttribute('aria-selected', !selected); } 64 | this.fireChange(); 65 | this.close(); 66 | }, 67 | 68 | onChangeMultiple: function(el, selected) { 69 | el.setAttribute('aria-selected', !selected); 70 | this.fireChange(); 71 | }, 72 | 73 | clearSelected: function() { 74 | [].forEach.call(this.selectedOptions, function(option) { 75 | option.removeAttribute('aria-selected'); 76 | }); 77 | }, 78 | 79 | fireChange: function() { 80 | var e = new CustomEvent('change', { detail: { value: this.valueString }}); 81 | this.dispatchEvent(e); 82 | }, 83 | 84 | attrs: { 85 | multiple: { 86 | get: function() { return !!this._multiple; }, 87 | set: function(value) { 88 | value = value === '' ? true : value; 89 | if (value === this._multiple) { return; } 90 | if (!value) { 91 | this._multiple = false; 92 | this.removeAttribute('multiple'); 93 | this.els.list.removeAttribute('aria-multiselectable'); 94 | } else { 95 | this._multiple = true; 96 | this.setAttribute('multiple', ''); 97 | this.els.list.setAttribute('aria-multiselectable', true); 98 | } 99 | } 100 | }, 101 | 102 | options: { 103 | get: function() { return this.querySelectorAll('li'); } 104 | }, 105 | 106 | selectedOptions: { 107 | get: function() { 108 | return this.querySelectorAll('li[aria-selected="true"]'); 109 | } 110 | }, 111 | 112 | selected: { 113 | get: function() { return this.selectedOptions[0]; } 114 | }, 115 | 116 | value: { 117 | get: function() { 118 | var selected = this.selectedOptions[0]; 119 | return selected && selected.getAttribute('value'); 120 | } 121 | }, 122 | 123 | valueString: { 124 | get: function() { 125 | var selected = this.selected; 126 | return selected && selected.textContent; 127 | } 128 | }, 129 | 130 | length: { 131 | get: function() { return this.options.length; } 132 | } 133 | }, 134 | 135 | template: ` 136 | 137 | 138 |
    139 |
    140 | 141 | 142 |
    143 |
    144 | ` 242 | }); 243 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-text-input/fxos-text-input-pin.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(require("fxos-component")); 4 | else if(typeof define === 'function' && define.amd) 5 | define(["fxos-component"], factory); 6 | else if(typeof exports === 'object') 7 | exports["FXOSTextInputPin"] = factory(require("fxos-component")); 8 | else 9 | root["FXOSTextInputPin"] = factory(root["fxosComponent"]); 10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__) { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | 39 | 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | 58 | /** 59 | * Dependencies 60 | */ 61 | 62 | var component = __webpack_require__(1); 63 | 64 | /** 65 | * Locals 66 | */ 67 | 68 | var DEFAULT_LENGTH = 4; 69 | 70 | /** 71 | * Exports 72 | */ 73 | 74 | module.exports = component.register('fxos-text-input-pin', { 75 | created() { 76 | this.setupShadowRoot(); 77 | 78 | this.els = { 79 | inner: this.shadowRoot.querySelector('.inner'), 80 | fields: this.shadowRoot.querySelector('.fields'), 81 | input: this.shadowRoot.querySelector('input') 82 | }; 83 | 84 | this.disabled = this.hasAttribute('disabled'); 85 | this.length = this.getAttribute('length') || DEFAULT_LENGTH; 86 | 87 | this.addEventListener('keyup', () => this.updateCells()); 88 | }, 89 | 90 | updateCells() { 91 | var l = this.els.input.value.length; 92 | this.els.cells.forEach((cell, i) => { 93 | cell.classList.toggle('populated', i < l); 94 | cell.classList.toggle('focused', i == l); 95 | }); 96 | }, 97 | 98 | onBackspace(e) { 99 | var input = e.target; 100 | var empty = !input.value; 101 | var previous = input.previousElementSibling; 102 | 103 | if (!empty && previous) { 104 | previous.clear(); 105 | previous.focus(); 106 | } else if (empty && previous) { 107 | previous.focus(); 108 | } 109 | }, 110 | 111 | setupFields() { 112 | this.els.fields.innerHTML = ''; 113 | this.els.cells = []; 114 | 115 | for (var i = 0, l = this.length; i < l; i++) { 116 | var el = document.createElement('div'); 117 | el.className = 'cell'; 118 | this.els.fields.appendChild(el); 119 | this.els.cells.push(el); 120 | } 121 | }, 122 | 123 | clear(e) { 124 | this.value = ''; 125 | this.updateCells(); 126 | }, 127 | 128 | focus(e) { 129 | this.els.input.focus(); 130 | }, 131 | 132 | /** 133 | * Attributes 134 | */ 135 | 136 | attrs: { 137 | length: { 138 | get() { return this._length; }, 139 | set(value) { 140 | value = Number(value); 141 | this._length = value; 142 | this.els.input.setAttribute('maxlength', this.length); 143 | this.setupFields(); 144 | } 145 | }, 146 | 147 | value: { 148 | get() { return this.els.input.value; }, 149 | set(value) { this.els.input.value = value; } 150 | }, 151 | 152 | disabled: { 153 | get() { return this.els.input.disabled; }, 154 | set(value) { 155 | value = !!(value === '' || value); 156 | this.els.input.disabled = value; 157 | } 158 | } 159 | }, 160 | 161 | template: ` 162 |
    163 | 164 |
    165 | 166 |
    167 |
    168 |
    169 | ` 268 | }); 269 | 270 | 271 | /***/ }, 272 | /* 1 */ 273 | /***/ function(module, exports) { 274 | 275 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__; 276 | 277 | /***/ } 278 | /******/ ]) 279 | }); 280 | ; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dom-scheduler [![](https://travis-ci.org/fxos-components/dom-scheduler.svg)](https://travis-ci.org/fxos-components/dom-scheduler) 2 | 3 | The DOM is fast, layout is fast, CSS transitions are smooth; but doing any at the same time can cause nasty performance glitches. This explains why it's easy to demo a 60fps transition, but larger apps are often janky. 4 | 5 | `dom-scheduler` helps express the types of operation you're doing, and in which order. Under the hood `dom-scheduler` ensures everything happens with the **best perceived performance**. 6 | 7 | ## Installation 8 | 9 | ```bash 10 | $ npm install dom-scheduler 11 | ``` 12 | 13 | ## Usage 14 | 15 | ```html 16 | 17 | ``` 18 | 19 | ## Concept 20 | 21 | This project has **2 main goals**: 22 | 23 | - Preventing trivial DOM changes in some unrelated part of your code from ruining a transition. 24 | - Enabling developers to easily express the ideal sequence for a change happening in phases (with Promise chains). 25 | 26 | ### Operations types by priority 27 | 28 | - `scheduler.attachDirect()` - Responding to long interactions 29 | - `scheduler.feedback()` - Showing feedback to quick interaction 30 | - `scheduler.transition()` - Animations/transitions 31 | - `scheduler.mutation()` - Mutating the DOM 32 | 33 | #### What type of operation should I use? 34 | 35 | As a rule of thumb 36 | 37 | - Anything that takes more than 16ms (including engine work) should be kept out of direct blocks 38 | - `.feedback()` and `.transition()` blocks should mainly contain hardware accelerated CSS transitions/animations 39 | - In mutation blocks, anything goes 40 | 41 | Using debug mode with a browser timeline profiler can help you spot issues (eg. a feedback block causing a reflow). You can always refer to the excellent [csstriggers.com](http://csstriggers.com/) while writing new code. 42 | 43 | ### What's a typical ideal sequence? 44 | 45 | Let's take a simple example like adding an item at the top of a list. To do that smoothly we want to: 46 | 47 | - `.transition()` everything down to make room for the new item 48 | - `.mutation()` to insert the new item into the DOM (outside of the viewport, so the item doesn't flash on screen) 49 | - `.transition()` the new item in the viewport 50 | 51 | Without `dom-scheduler` this means: 52 | 53 | ```js 54 | setupTransitionOnElements(); 55 | container.addEventListener('transitionend', function trWait() { 56 | container.removeEventListener('transitionend'); 57 | writeToTheDOM(); 58 | setupTransitionOnNewElement(); 59 | el.addEventListener('transitionend', function stillWaiting() { 60 | el.removeEventListener('transitionend', stillWaiting); 61 | cleanUp(); 62 | }); 63 | }); 64 | ``` 65 | 66 | But we'd rather use promises to express this kind of sequence: 67 | 68 | ```js 69 | pushDown(elements) 70 | .then(insertInDocument) 71 | .then(slideIn) 72 | .then(cleanUp) 73 | ``` 74 | 75 | Another badass sequence, using a promise-based storage system might be 76 | something like 77 | 78 | ```js 79 | Promise.all([reflectChangeWithTransitions(), persistChange()]) 80 | .then(reflectChangeInDocument) 81 | .then(cleanUp) 82 | ``` 83 | 84 | - `reflectChangeWithTransition()` is a scheduled transition 85 | - `persitChange()` is your backend call 86 | - `reflectChangeInDocument` is a scheduled mutation 87 | - `cleanUp` is a scheduled mutation 88 | 89 | ## Adopting dom-scheduler 90 | 91 | To reap all the benefits from the scheduled approach you want to 92 | 93 | - _"annotate"_ a maximum of your code, especially the mutations 94 | - use the shared scheduler instance (exported as `scheduler`) 95 | - use the debug mode (see below) 96 | 97 | ## API 98 | 99 | ### scheduler.attachDirect() 100 | 101 | Direct blocks should be used for direct manipulation (touchevents, 102 | scrollevents...). As such they have the **highest priority**. 103 | 104 | You _"attach"_ a direct block to a specific event. The scheduler takes care of adding and removing event listeners. The event object will be passed to the `block` as the first parameter. 105 | 106 | #### Attaching a handler 107 | 108 | ```js 109 | scheduler.attachDirect(elm, evt, block) 110 | ``` 111 | 112 | #### Detaching a handler 113 | 114 | ```js 115 | scheduler.detachDirect(elm, evt, block) 116 | ``` 117 | 118 | #### Example 119 | 120 | ```js 121 | scheduler.attachDirect(el, 'touchmove', evt => { 122 | el.style.transform = computeTransform(evt); 123 | }); 124 | ``` 125 | 126 | ### scheduler.feedback() 127 | 128 | ```js 129 | scheduler.feedback(block, elm, evt, timeout) 130 | ``` 131 | 132 | Feedback blocks should be used to encapsulate CSS transitions/animations triggered in direct response to a user interaction (eg. button pressed state). 133 | 134 | They will be protected from `scheduler.mutation()`s to perform smoothly and 135 | return a promise, fulfilled once `evt` is received on `elm` or after `timeout`ms. 136 | 137 | The `scheduler.feedback()` has the same priority as `scheduler.attachDirect()`. 138 | 139 | ```js 140 | scheduler.feedback(() => { 141 | el.classList.add('pressed'); 142 | }, el, 'transitionend').then(() => { 143 | el.classList.remove('pressed'); 144 | }); 145 | ``` 146 | 147 | ### scheduler.transition() 148 | 149 | ```js 150 | scheduler.transition(block, elm, evt, timeout); 151 | ``` 152 | 153 | `scheduler.transition()` should be used to protect CSS transitions/animations. When in progress they prevent any scheduled `scheduler.mutation()` tasks running to maintain a smooth framerate. They return a promise, fulfilled once `evt` is received on `elm` or after `timeout`ms. 154 | 155 | ```js 156 | scheduler.transition(() => { 157 | el.style.transition = 'transform 0.25s ease'; 158 | el.classList.remove('new'); 159 | }, el, 'transitionend').then(() => { 160 | el.style.transition = ''; 161 | }); 162 | ``` 163 | 164 | ### scheduler.mutation() 165 | 166 | ```js 167 | scheduler.mutation(block); 168 | ``` 169 | 170 | Mutations blocks should be used to write to the DOM or perform actions requiring layout to be computed. 171 | 172 | **We shoud always aim for the document to be (almost) visually identical _before_ and _after_ a mutation block. Any big change in layout/size will cause a flash/jump.** 173 | 174 | `scheduler.mutation()` blocks might be delayed (eg. when a `scheduler.transition()` is in progress). They return a promise, fullfilled once the task is eventually executed; this also allows chaining. 175 | 176 | When used for measurement (eg. `getBoundingClientRect()`) a block can `return` a result that will be propagated through the promise chain. 177 | 178 | ```js 179 | scheduler.mutation(() => { 180 | el.textContent = 'Main List (' + items.length + ')'; 181 | }); 182 | ``` 183 | 184 | ## Scheduling heuristics (TBD) 185 | - `.direct()` blocks are called inside `requestAnimationFrame` 186 | - `.attachDirect()` and `.feedback()` blocks have the highest priority and delay the rest. 187 | - `.transition()` delays executuon of `.mutation()` tasks. 188 | - `.transition()`s are postponed while delayed `mutation()`s are being flushed 189 | - When both `.transition()`s and `.mutation()`s are queued because of `.attachDirect()` 190 | manipulation, `.transition()`s are run first 191 | 192 | ## Debug mode 193 | 194 | While it can have a negative impact on performance, it's recommended to turn the debug mode on from time to time during development to catch frequent mistakes early on. 195 | 196 | Currently the debug mode will warn you about 197 | 198 | - Transition block for which we never get an "end" event 199 | - Direct blocks taking longer than 16ms 200 | 201 | We're also using `console.time` / `console.timeEnd` to flag the 202 | following in the profiler: 203 | - `animating`, when a feedback or transition is ongoing 204 | - `protecting`, when a direct protection window is ongoing 205 | 206 | You can turn on the debug mode by setting `debug` to `true` in [`dom-scheduler.js`](dom-scheduler.js). 207 | 208 | ## Demo APP 209 | 210 | To illustrate the benefits of the scheduling approach the project comes with a demo app: a **big** re-orderable (virtual) list where new content comes in every few seconds. At random, the data source will sometime simulate a case where the content isn't ready. And delay populating the content. 211 | 212 | The interesting part is of course the _"real life"_ behaviors: 213 | - when new content comes in while scrolling 214 | - when the edit mode is toggled while the system is busy scrolling 215 | - when anything happens during a re-order manipulation 216 | 217 | _(You can turn on the `naive` flag in [`dom-scheduler.js`](dom-scheduler.js) to disable scheduling and compare.)_ 218 | 219 | ### Web page version 220 | 221 | The `index.html` at the root of this repository is meant for broad device and browser testing so we try to keep gecko/webkit/blink compatibility. 222 | 223 | A (potentially outdated) version of the demo is usually accessible at [http://sgz.fr/ds](http://sgz.fr/ds) and should work on any modern browser. 224 | 225 | ### Packaged version 226 | 227 | The `examples/demo` is a 'certified' packaged-app where we experiment with web components and other stuff. 228 | 229 | ## Tests 230 | 231 | 1. `$ npm install` 232 | 2. `$ npm test` 233 | 234 | If you would like tests to run on file change use: 235 | 236 | `$ npm run test-dev` 237 | 238 | ## Lint check 239 | 240 | Run lint check with command: 241 | 242 | `$ npm run lint` 243 | 244 | ## License 245 | 246 | Mozilla Public License 2.0 247 | 248 | http://mozilla.org/MPL/2.0/ 249 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-text-input/src/fxos-text-input.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Dependencies 4 | */ 5 | 6 | var component = require('fxos-component'); 7 | require('fxos-icons'); 8 | 9 | /** 10 | * Mini logger 11 | * 12 | * @type {Function} 13 | */ 14 | var debug = 0 15 | ? (...args) => console.log('[fxos-text-input]', ...args) 16 | : () => {}; 17 | 18 | /** 19 | * Exports 20 | */ 21 | 22 | module.exports = component.register('fxos-text-input', { 23 | created() { 24 | this.setupShadowRoot(); 25 | 26 | this.els = { 27 | inner: this.shadowRoot.querySelector('.inner'), 28 | input: this.shadowRoot.querySelector('input'), 29 | clear: this.shadowRoot.querySelector('.clear') 30 | }; 31 | 32 | this.type = this.getAttribute('type'); 33 | this.inputmode = this.getAttribute('x-inputmode'); 34 | this.disabled = this.hasAttribute('disabled'); 35 | this.clearable = this.hasAttribute('clearable'); 36 | this.placeholder = this.getAttribute('placeholder'); 37 | this.required = this.getAttribute('required'); 38 | this.value = this.getAttribute('value'); 39 | 40 | // Don't take focus from the input field 41 | this.els.clear.addEventListener('mousedown', e => e.preventDefault()); 42 | 43 | this.els.clear.addEventListener('click', e => this.clear(e)); 44 | this.els.input.addEventListener('input', e => this.onInput(e)); 45 | this.els.input.addEventListener('focus', e => this.onFocus(e)); 46 | this.els.input.addEventListener('blur', e => this.onBlur(e)); 47 | }, 48 | 49 | /** 50 | * Clear the field. 51 | * 52 | * @public 53 | */ 54 | clear() { 55 | debug('clear'); 56 | this.value = ''; 57 | this.emit('clear'); 58 | }, 59 | 60 | /** 61 | * Focus the field. 62 | * 63 | * @public 64 | */ 65 | focus() { 66 | debug('focus'); 67 | this.els.input.focus(); 68 | }, 69 | 70 | /** 71 | * Unfocus the field. 72 | * 73 | * @public 74 | */ 75 | blur() { 76 | debug('blur'); 77 | this.els.input.blur(); 78 | }, 79 | 80 | /** 81 | * Runs when the field is focused. 82 | * 83 | * @private 84 | */ 85 | onFocus() { 86 | debug('on focus'); 87 | this.els.inner.classList.add('focused'); 88 | this.emit('focus'); 89 | }, 90 | 91 | /** 92 | * Runs when the field is unfocused. 93 | * 94 | * @private 95 | */ 96 | onBlur() { 97 | debug('on blur'); 98 | this.els.inner.classList.remove('focused'); 99 | this.emit('blur'); 100 | }, 101 | 102 | /** 103 | * Runs when the value of the 104 | * input is manually changed. 105 | * 106 | * @private 107 | */ 108 | onInput() { 109 | debug('on input'); 110 | this.onValueChanged(); 111 | }, 112 | 113 | /** 114 | * Runs when the values changes 115 | * programatically or via keystrokes. 116 | * 117 | * @private 118 | */ 119 | onValueChanged() { 120 | debug('value changed'); 121 | var hasValue = !!this.value.length; 122 | this.els.inner.classList.toggle('has-value', hasValue); 123 | this.emit('input'); 124 | }, 125 | 126 | /** 127 | * Emit a DOM event on the component. 128 | * 129 | * @param {String} name 130 | * @param {*} detail 131 | * @private 132 | */ 133 | emit(name, detail) { 134 | var e = new CustomEvent(name, { detail: detail }); 135 | this.dispatchEvent(e); 136 | }, 137 | 138 | /** 139 | * Attributes 140 | */ 141 | 142 | attrs: { 143 | type: { 144 | get: function() { return this.els.input.getAttribute('type'); }, 145 | set: function(value) { 146 | if (!value) { return; } 147 | this.els.inner.setAttribute('type', value); 148 | this.els.input.setAttribute('type', value); 149 | } 150 | }, 151 | 152 | inputmode: { 153 | get: function() { return this.els.input.getAttribute('x-inputmode'); }, 154 | set: function(value) { 155 | if (!value) { 156 | this.els.input.removeAttribute('x-inputmode'); 157 | return; 158 | } 159 | this.els.input.setAttribute('x-inputmode', value); 160 | } 161 | }, 162 | 163 | placeholder: { 164 | get: function() { return this.field.placeholder; }, 165 | set: function(value) { 166 | if (!value && value !== '') { return; } 167 | this.els.input.placeholder = value; 168 | } 169 | }, 170 | 171 | clearable: { 172 | get: function() { return this._clearable; }, 173 | set: function(value) { 174 | var clearable = !!(value === '' || value); 175 | if (clearable === this.clearable) { return; } 176 | 177 | if (clearable) this.setAttr('clearable', ''); 178 | else this.removeAttr('clearable'); 179 | 180 | this._clearable = clearable; 181 | } 182 | }, 183 | 184 | value: { 185 | get() { return this.els.input.value; }, 186 | set(value) { 187 | debug('set value', value); 188 | this.els.input.value = value; 189 | this.onValueChanged(); 190 | } 191 | }, 192 | 193 | required: { 194 | get() { return this.els.input.required; }, 195 | set(value) { this.els.input.required = value; } 196 | }, 197 | 198 | maxlength: { 199 | get() { return this.els.input.getAttribute('maxlength'); }, 200 | set(value) { this.els.input.setAttribute('maxlength', value); } 201 | }, 202 | 203 | disabled: { 204 | get() { return this.els.input.disabled; }, 205 | set(value) { 206 | value = !!(value === '' || value); 207 | this.els.input.disabled = value; 208 | } 209 | } 210 | }, 211 | 212 | template: ` 213 |
    214 | 215 |
    216 | 217 | 218 |
    219 |
    220 |
    221 | ` 353 | }); 354 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-dialog/fxos-dialog-select.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(require("./fxos-dialog"), require("fxos-component")); 4 | else if(typeof define === 'function' && define.amd) 5 | define(["./fxos-dialog", "fxos-component"], factory); 6 | else if(typeof exports === 'object') 7 | exports["FXOSDialogSelect"] = factory(require("./fxos-dialog"), require("fxos-component")); 8 | else 9 | root["FXOSDialogSelect"] = factory(root["FXOSDialog"], root["fxosComponent"]); 10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__) { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | 39 | 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | 58 | /** 59 | * Dependencies 60 | */ 61 | 62 | var GaiaDialogProto = __webpack_require__(1).prototype; 63 | var component = __webpack_require__(2); 64 | 65 | /** 66 | * Exports 67 | */ 68 | module.exports = component.register('fxos-dialog-select', { 69 | created: function() { 70 | this.setupShadowRoot(); 71 | 72 | this.els = { 73 | dialog: this.shadowRoot.querySelector('fxos-dialog'), 74 | submit: this.shadowRoot.querySelector('.submit'), 75 | cancel: this.shadowRoot.querySelector('.cancel'), 76 | list: this.shadowRoot.querySelector('ul') 77 | }; 78 | 79 | this.multiple = this.hasAttribute('multiple'); 80 | this.els.list.addEventListener('click', this.onListClick.bind(this)); 81 | this.els.submit.addEventListener('click', this.close.bind(this)); 82 | this.els.cancel.addEventListener('click', this.close.bind(this)); 83 | 84 | setTimeout(() => this.makeAccessible()); 85 | }, 86 | 87 | makeAccessible() { 88 | this.els.list.setAttribute('role', 'listbox'); 89 | if (this.multiple) { 90 | this.els.list.setAttribute('aria-multiselectable', true); 91 | } 92 | 93 | [].forEach.call(this.options, option => { 94 | option.setAttribute('role', 'option'); 95 | option.setAttribute('tabindex', '0'); 96 | }); 97 | }, 98 | 99 | open: function(e) { 100 | return GaiaDialogProto.show.call(this) 101 | .then(() => this.els.dialog.open(e)); 102 | }, 103 | 104 | close: function() { 105 | return this.els.dialog.close() 106 | .then(GaiaDialogProto.hide.bind(this)); 107 | }, 108 | 109 | onListClick: function(e) { 110 | var el = e.target.closest('li'); 111 | var selected = el.getAttribute('aria-selected') === 'true'; 112 | 113 | if (this.multiple) { this.onChangeMultiple(el, selected); } 114 | else { this.onChange(el, selected); } 115 | }, 116 | 117 | onChange: function(el, selected) { 118 | this.clearSelected(); 119 | if (!selected) { el.setAttribute('aria-selected', !selected); } 120 | this.fireChange(); 121 | this.close(); 122 | }, 123 | 124 | onChangeMultiple: function(el, selected) { 125 | el.setAttribute('aria-selected', !selected); 126 | this.fireChange(); 127 | }, 128 | 129 | clearSelected: function() { 130 | [].forEach.call(this.selectedOptions, function(option) { 131 | option.removeAttribute('aria-selected'); 132 | }); 133 | }, 134 | 135 | fireChange: function() { 136 | var e = new CustomEvent('change', { detail: { value: this.valueString }}); 137 | this.dispatchEvent(e); 138 | }, 139 | 140 | attrs: { 141 | multiple: { 142 | get: function() { return !!this._multiple; }, 143 | set: function(value) { 144 | value = value === '' ? true : value; 145 | if (value === this._multiple) { return; } 146 | if (!value) { 147 | this._multiple = false; 148 | this.removeAttribute('multiple'); 149 | this.els.list.removeAttribute('aria-multiselectable'); 150 | } else { 151 | this._multiple = true; 152 | this.setAttribute('multiple', ''); 153 | this.els.list.setAttribute('aria-multiselectable', true); 154 | } 155 | } 156 | }, 157 | 158 | options: { 159 | get: function() { return this.querySelectorAll('li'); } 160 | }, 161 | 162 | selectedOptions: { 163 | get: function() { 164 | return this.querySelectorAll('li[aria-selected="true"]'); 165 | } 166 | }, 167 | 168 | selected: { 169 | get: function() { return this.selectedOptions[0]; } 170 | }, 171 | 172 | value: { 173 | get: function() { 174 | var selected = this.selectedOptions[0]; 175 | return selected && selected.getAttribute('value'); 176 | } 177 | }, 178 | 179 | valueString: { 180 | get: function() { 181 | var selected = this.selected; 182 | return selected && selected.textContent; 183 | } 184 | }, 185 | 186 | length: { 187 | get: function() { return this.options.length; } 188 | } 189 | }, 190 | 191 | template: ` 192 | 193 | 194 |
    195 |
    196 | 197 | 198 |
    199 |
    200 | ` 298 | }); 299 | 300 | 301 | /***/ }, 302 | /* 1 */ 303 | /***/ function(module, exports) { 304 | 305 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__; 306 | 307 | /***/ }, 308 | /* 2 */ 309 | /***/ function(module, exports) { 310 | 311 | module.exports = __WEBPACK_EXTERNAL_MODULE_2__; 312 | 313 | /***/ } 314 | /******/ ]) 315 | }); 316 | ; -------------------------------------------------------------------------------- /dom-scheduler.js: -------------------------------------------------------------------------------- 1 | (function(exports) { 2 | 'use strict'; 3 | 4 | var debug = false; 5 | function log(str) { 6 | if (!debug) { 7 | return; 8 | } 9 | 10 | console.log('🎶 ', str); 11 | } 12 | 13 | // The naive mode just executes all blocks right away 14 | // (useful for performance comparison). 15 | var naive = false; 16 | var naiveExec = function(block) { 17 | block(); 18 | return Promise.resolve(); 19 | }; 20 | 21 | var directProtectionWindow = 360; 22 | 23 | var Scheduler = function() { 24 | this._directTracking = new Map(); 25 | 26 | this._ongoingTransitions = 0; 27 | this._queuedTransitions = []; 28 | 29 | this._flushing = false; 30 | this._pendingMutations = []; 31 | }; 32 | 33 | Scheduler.prototype = { 34 | // *Direct* blocks should be used for direct manipulation use cases 35 | // (touchevents, scrollevents...). 36 | // They're exectuted in a requestAnimationFrame block and are protected 37 | // from mutations. Request might be cancelled by subsequent direct blocks if 38 | // the event loop gets too busy. 39 | attachDirect: function(elm, evtType, block) { 40 | var tracking = this._directTracking; 41 | if (!tracking.get(elm)) { 42 | tracking.set(elm, {}); 43 | } 44 | var tElm = tracking.get(elm); 45 | 46 | if (!(evtType in tElm)) { 47 | tElm[evtType] = { 48 | rafID: null, 49 | protectionTimeout: null, 50 | blocks: [] 51 | }; 52 | } 53 | var tEvt = tElm[evtType]; 54 | 55 | if (!tEvt.blocks.length) { 56 | elm.addEventListener(evtType, this); 57 | } 58 | 59 | tEvt.blocks.push(block); 60 | }, 61 | 62 | detachDirect: function(elm, evtType, block) { 63 | var tEvt = this._trackingFor(elm, evtType); 64 | if (!tEvt) { 65 | return; 66 | } 67 | 68 | if (tEvt.rafID) { 69 | window.cancelAnimationFrame(tEvt.rafID); 70 | } 71 | 72 | if (tEvt.protectionTimeout) { 73 | clearTimeout(tEvt.protectionTimeout); 74 | tEvt.protectionTimeout = null; 75 | this._stopProtectingDirect(); 76 | } 77 | 78 | var toRemove = tEvt.blocks.indexOf(block); 79 | if (toRemove === -1) { 80 | log('wrong detach block'); 81 | return; 82 | } 83 | 84 | tEvt.blocks.splice(toRemove, 1); 85 | 86 | if (!tEvt.blocks.length) { 87 | elm.removeEventListener(evtType, this); 88 | var tElm = this._directTracking.get(elm); 89 | delete tElm[evtType]; 90 | 91 | if (Object.keys(tElm).length === 0) { 92 | this._directTracking.delete(elm); 93 | } 94 | } 95 | }, 96 | 97 | _trackingFor: function(elm, evtType) { 98 | var tracking = this._directTracking; 99 | if (!tracking.get(elm)) { 100 | log('wrong detach element'); 101 | return; 102 | } 103 | var tElm = tracking.get(elm); 104 | 105 | if (!(evtType in tElm)) { 106 | log('wrong detach event'); 107 | return; 108 | } 109 | return tElm[evtType]; 110 | }, 111 | 112 | _shouldProtectDirect: function() { 113 | var tracking = this._directTracking; 114 | 115 | for (var elm of tracking.keys()) { 116 | var tElm = tracking.get(elm); 117 | for (var evtType in tElm) { 118 | var tEvt = tElm[evtType]; 119 | if (tEvt.protectionTimeout !== null) { 120 | return true; 121 | } 122 | } 123 | } 124 | return false; 125 | }, 126 | 127 | _stopProtectingDirect: function() { 128 | this._dequeueTransitions(); 129 | this._flushMutations(); 130 | if (debug) { 131 | console.timeEnd('protecting'); 132 | } 133 | }, 134 | 135 | handleEvent: function(evt) { 136 | var tEvt = this._trackingFor(evt.currentTarget, evt.type); 137 | if (!tEvt) { 138 | return; 139 | } 140 | 141 | if (naive) { 142 | tEvt.blocks.forEach(function(block) { 143 | block(evt); 144 | }); 145 | return; 146 | } 147 | 148 | if (tEvt.protectionTimeout) { 149 | clearTimeout(tEvt.protectionTimeout); 150 | } else { 151 | if (debug) { 152 | console.time('protecting'); 153 | } 154 | } 155 | 156 | tEvt.protectionTimeout = setTimeout((function() { 157 | tEvt.protectionTimeout = null; 158 | this._stopProtectingDirect(); 159 | }).bind(this), directProtectionWindow); 160 | 161 | if (tEvt.rafID) { 162 | window.cancelAnimationFrame(tEvt.rafID); 163 | } 164 | 165 | tEvt.rafID = window.requestAnimationFrame(function() { 166 | tEvt.blocks.forEach(function(block) { 167 | var startDate; 168 | if (debug) { 169 | startDate = performance.now(); 170 | } 171 | 172 | block(evt); 173 | 174 | if (debug) { 175 | var blockDuration = performance.now() - startDate; 176 | if (blockDuration > 16) { 177 | log('Direct block took more than a frame (' + 178 | blockDuration.toString() + 'ms)'); 179 | } 180 | } 181 | }); 182 | }); 183 | }, 184 | 185 | // *Feedbacks* blocks have a built in 'transitionend' wait mechanism. 186 | // They're protected from mutation and will be delayed during a mutation 187 | // flush. 188 | // They have the same priority as `direct` blocks. 189 | // 190 | // -> Returns a promise fullfilled at the end of the transition for chaining 191 | feedback: function(block, elm, evt, timeout) { 192 | return this._transition(block, true, elm, evt, timeout); 193 | }, 194 | 195 | // *Transitions* blocks have a built in 'transitionend' wait mechanism. 196 | // They're protected from mutation and will be delayed during a mutation 197 | // flush. 198 | // They will also be delayed by `direct` blocks. 199 | // 200 | // -> Returns a promise fullfilled at the end of the transition for chaining 201 | transition: function(block, elm, evt, timeout) { 202 | return this._transition(block, false, elm, evt, timeout); 203 | }, 204 | 205 | _transition: function(block, feedback, elm, evt, timeout) { 206 | if (naive) { 207 | return naiveExec(block); 208 | } 209 | 210 | timeout = timeout || 500; 211 | 212 | return new Promise((function(resolve, reject) { 213 | var content = (function() { 214 | this._ongoingTransitions++; 215 | 216 | block(); 217 | 218 | if (!elm || !evt) { 219 | this._ongoingTransitions--; 220 | resolve(); 221 | return; 222 | } 223 | 224 | if (debug) { 225 | console.time('animating'); 226 | } 227 | 228 | var finishTimeout; 229 | 230 | var done = (function() { 231 | clearTimeout(finishTimeout); 232 | elm.removeEventListener(evt, done); 233 | 234 | this._ongoingTransitions--; 235 | if (debug) { 236 | console.timeEnd('animating'); 237 | } 238 | 239 | if (this._ongoingTransitions === 0) { 240 | setTimeout(this._flushMutations.bind(this)); 241 | } 242 | 243 | resolve(); 244 | }).bind(this); 245 | 246 | elm.addEventListener(evt, done); 247 | finishTimeout = setTimeout(function() { 248 | done(); 249 | log('Transition block saved by a timeout of ' + timeout + ' ~ ' + 250 | elm.style.transition); 251 | }, timeout); 252 | }).bind(this); 253 | 254 | if (this._flushing || (this._shouldProtectDirect() && !feedback)) { 255 | this._queuedTransitions.push(content); 256 | } else { 257 | content(); 258 | } 259 | }).bind(this)); 260 | }, 261 | 262 | _dequeueTransitions: function() { 263 | if (this._queuedTransitions.length === 0) { 264 | return; 265 | } 266 | 267 | if (this._flushing || this._shouldProtectDirect()) { 268 | return; 269 | } 270 | 271 | var transitions = this._queuedTransitions; 272 | transitions.forEach(function(transition) { 273 | transition(); 274 | }); 275 | this._queuedTransitions = []; 276 | }, 277 | 278 | // *Mutations* blocks should be used to write to the DOM or perform 279 | // actions requiring a reflow that are not direct manipulations. 280 | // We shoud always aim for the document to be almost visually identical 281 | // _before_ and _after_ a mutation block. 282 | // Any big change in layout/size will cause a flash/jump. 283 | // 284 | // -> Returns a promise fullfilled after the reflow for chaining 285 | mutation: function(block) { 286 | if (naive) { 287 | return naiveExec(block); 288 | } 289 | 290 | return new Promise((function(resolve, reject) { 291 | if (this._shouldProtectDirect() || this._ongoingTransitions > 0) { 292 | this._pendingMutations.push({ 293 | block: block, 294 | resolve: resolve 295 | }); 296 | } else { 297 | Promise.resolve() 298 | .then(block) 299 | .then(resolve); 300 | } 301 | }).bind(this)); 302 | }, 303 | 304 | _flushMutations: function() { 305 | if (this._pendingMutations.length === 0) { 306 | return; 307 | } 308 | 309 | if (this._shouldProtectDirect() || this._ongoingTransitions > 0) { 310 | return; 311 | } 312 | 313 | this._flushing = true; 314 | 315 | var fulfilments = 316 | this._pendingMutations 317 | .map(function(obj) { return obj.resolve; }); 318 | 319 | var mutations = this._pendingMutations; 320 | var results = mutations.map(function(mutation) { 321 | return mutation.block(); 322 | }); 323 | this._pendingMutations = []; 324 | this._flushing = false; 325 | 326 | fulfilments.forEach(function(resolve, i) { resolve(results[i]); }); 327 | 328 | this._dequeueTransitions(); 329 | } 330 | }; 331 | 332 | exports.DomScheduler = Scheduler; 333 | 334 | // We only ever want there to be 335 | // one instance of the scheduler 336 | exports.scheduler = exports.scheduler || new Scheduler(); 337 | })(window); 338 | -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-text-input/fxos-text-input.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(require("fxos-component")); 4 | else if(typeof define === 'function' && define.amd) 5 | define(["fxos-component"], factory); 6 | else if(typeof exports === 'object') 7 | exports["FXOSTextInput"] = factory(require("fxos-component")); 8 | else 9 | root["FXOSTextInput"] = factory(root["fxosComponent"]); 10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__) { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | 39 | 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | 58 | /** 59 | * Dependencies 60 | */ 61 | 62 | var component = __webpack_require__(1); 63 | __webpack_require__(2); 64 | 65 | /** 66 | * Mini logger 67 | * 68 | * @type {Function} 69 | */ 70 | var debug = 0 ? (...args) => console.log('[fxos-text-input]', ...args) : () => {}; 71 | 72 | /** 73 | * Exports 74 | */ 75 | 76 | module.exports = component.register('fxos-text-input', { 77 | created() { 78 | this.setupShadowRoot(); 79 | 80 | this.els = { 81 | inner: this.shadowRoot.querySelector('.inner'), 82 | input: this.shadowRoot.querySelector('input'), 83 | clear: this.shadowRoot.querySelector('.clear') 84 | }; 85 | 86 | this.type = this.getAttribute('type'); 87 | this.inputmode = this.getAttribute('x-inputmode'); 88 | this.disabled = this.hasAttribute('disabled'); 89 | this.clearable = this.hasAttribute('clearable'); 90 | this.placeholder = this.getAttribute('placeholder'); 91 | this.required = this.getAttribute('required'); 92 | this.value = this.getAttribute('value'); 93 | 94 | // Don't take focus from the input field 95 | this.els.clear.addEventListener('mousedown', e => e.preventDefault()); 96 | 97 | this.els.clear.addEventListener('click', e => this.clear(e)); 98 | this.els.input.addEventListener('input', e => this.onInput(e)); 99 | this.els.input.addEventListener('focus', e => this.onFocus(e)); 100 | this.els.input.addEventListener('blur', e => this.onBlur(e)); 101 | }, 102 | 103 | /** 104 | * Clear the field. 105 | * 106 | * @public 107 | */ 108 | clear() { 109 | debug('clear'); 110 | this.value = ''; 111 | this.emit('clear'); 112 | }, 113 | 114 | /** 115 | * Focus the field. 116 | * 117 | * @public 118 | */ 119 | focus() { 120 | debug('focus'); 121 | this.els.input.focus(); 122 | }, 123 | 124 | /** 125 | * Unfocus the field. 126 | * 127 | * @public 128 | */ 129 | blur() { 130 | debug('blur'); 131 | this.els.input.blur(); 132 | }, 133 | 134 | /** 135 | * Runs when the field is focused. 136 | * 137 | * @private 138 | */ 139 | onFocus() { 140 | debug('on focus'); 141 | this.els.inner.classList.add('focused'); 142 | this.emit('focus'); 143 | }, 144 | 145 | /** 146 | * Runs when the field is unfocused. 147 | * 148 | * @private 149 | */ 150 | onBlur() { 151 | debug('on blur'); 152 | this.els.inner.classList.remove('focused'); 153 | this.emit('blur'); 154 | }, 155 | 156 | /** 157 | * Runs when the value of the 158 | * input is manually changed. 159 | * 160 | * @private 161 | */ 162 | onInput() { 163 | debug('on input'); 164 | this.onValueChanged(); 165 | }, 166 | 167 | /** 168 | * Runs when the values changes 169 | * programatically or via keystrokes. 170 | * 171 | * @private 172 | */ 173 | onValueChanged() { 174 | debug('value changed'); 175 | var hasValue = !!this.value.length; 176 | this.els.inner.classList.toggle('has-value', hasValue); 177 | this.emit('input'); 178 | }, 179 | 180 | /** 181 | * Emit a DOM event on the component. 182 | * 183 | * @param {String} name 184 | * @param {*} detail 185 | * @private 186 | */ 187 | emit(name, detail) { 188 | var e = new CustomEvent(name, { detail: detail }); 189 | this.dispatchEvent(e); 190 | }, 191 | 192 | /** 193 | * Attributes 194 | */ 195 | 196 | attrs: { 197 | type: { 198 | get: function() { return this.els.input.getAttribute('type'); }, 199 | set: function(value) { 200 | if (!value) { return; } 201 | this.els.inner.setAttribute('type', value); 202 | this.els.input.setAttribute('type', value); 203 | } 204 | }, 205 | 206 | inputmode: { 207 | get: function() { return this.els.input.getAttribute('x-inputmode'); }, 208 | set: function(value) { 209 | if (!value) { 210 | this.els.input.removeAttribute('x-inputmode'); 211 | return; 212 | } 213 | this.els.input.setAttribute('x-inputmode', value); 214 | } 215 | }, 216 | 217 | placeholder: { 218 | get: function() { return this.field.placeholder; }, 219 | set: function(value) { 220 | if (!value && value !== '') { return; } 221 | this.els.input.placeholder = value; 222 | } 223 | }, 224 | 225 | clearable: { 226 | get: function() { return this._clearable; }, 227 | set: function(value) { 228 | var clearable = !!(value === '' || value); 229 | if (clearable === this.clearable) { return; } 230 | 231 | if (clearable) this.setAttr('clearable', ''); 232 | else this.removeAttr('clearable'); 233 | 234 | this._clearable = clearable; 235 | } 236 | }, 237 | 238 | value: { 239 | get() { return this.els.input.value; }, 240 | set(value) { 241 | debug('set value', value); 242 | this.els.input.value = value; 243 | this.onValueChanged(); 244 | } 245 | }, 246 | 247 | required: { 248 | get() { return this.els.input.required; }, 249 | set(value) { this.els.input.required = value; } 250 | }, 251 | 252 | maxlength: { 253 | get() { return this.els.input.getAttribute('maxlength'); }, 254 | set(value) { this.els.input.setAttribute('maxlength', value); } 255 | }, 256 | 257 | disabled: { 258 | get() { return this.els.input.disabled; }, 259 | set(value) { 260 | value = !!(value === '' || value); 261 | this.els.input.disabled = value; 262 | } 263 | } 264 | }, 265 | 266 | template: ` 267 |
    268 | 269 |
    270 | 271 | 272 |
    273 |
    274 |
    275 | ` 407 | }); 408 | 409 | 410 | /***/ }, 411 | /* 1 */ 412 | /***/ function(module, exports) { 413 | 414 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__; 415 | 416 | /***/ }, 417 | /* 2 */ 418 | /***/ function(module, exports, __webpack_require__) { 419 | 420 | var __WEBPACK_AMD_DEFINE_RESULT__;(function(define){'use strict';!(__WEBPACK_AMD_DEFINE_RESULT__ = function(require,exports,module){ 421 | 422 | /** 423 | * Exports 424 | */ 425 | 426 | var base = window.FXOS_ICONS_BASE_URL 427 | || window.COMPONENTS_BASE_URL 428 | || 'node_modules/'; 429 | 430 | // Load it! 431 | if (!document.documentElement) addEventListener('load', load); 432 | else load(); 433 | 434 | function load() { 435 | if (isLoaded()) return; 436 | var link = document.createElement('link'); 437 | link.rel = 'stylesheet'; 438 | link.type = 'text/css'; 439 | link.href = base + 'fxos-icons/fxos-icons.css'; 440 | document.head.appendChild(link); 441 | exports.loaded = true; 442 | } 443 | 444 | function isLoaded() { 445 | return exports.loaded 446 | || document.querySelector('link[href*=fxos-icons]') 447 | || document.documentElement.classList.contains('fxos-icons-loaded'); 448 | } 449 | 450 | }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));})(__webpack_require__(3));/*jshint ignore:line*/ 451 | 452 | 453 | /***/ }, 454 | /* 3 */ 455 | /***/ function(module, exports) { 456 | 457 | module.exports = function() { throw new Error("define cannot be used indirect"); }; 458 | 459 | 460 | /***/ } 461 | /******/ ]) 462 | }); 463 | ; -------------------------------------------------------------------------------- /examples/demo/node_modules/fxos-dialog/src/fxos-dialog.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Dependencies 4 | */ 5 | var component = require('fxos-component'); 6 | 7 | /** 8 | * Simple logger (toggle 0) 9 | * 10 | * @type {Function} 11 | */ 12 | var debug = 0 ? console.log.bind(console, '[fxos-dialog]') : () => {}; 13 | 14 | /** 15 | * Use the dom-scheduler if it's around, 16 | * else fallback to fake shim. 17 | * 18 | * @type {Object} 19 | */ 20 | var schedule = window.scheduler || { 21 | mutation: block => Promise.resolve(block()), 22 | transition: (block, el, event, timeout) => { 23 | block(); 24 | return after(el, event, timeout || 500); 25 | } 26 | }; 27 | 28 | /** 29 | * Exports 30 | */ 31 | 32 | module.exports = component.register('fxos-dialog', { 33 | created() { 34 | this.setupShadowRoot(); 35 | 36 | this.els = { 37 | inner: this.shadowRoot.querySelector('.dialog-inner'), 38 | background: this.shadowRoot.querySelector('.background'), 39 | window: this.shadowRoot.querySelector('.window') 40 | }; 41 | 42 | this.shadowRoot.addEventListener('click', e => this.onClick(e)); 43 | 44 | setTimeout(() => this.makeAccessible()); 45 | }, 46 | 47 | makeAccessible() { 48 | this.setAttribute('role', 'dialog'); 49 | }, 50 | 51 | onClick(e) { 52 | var el = e.target.closest('[on-click]'); 53 | if (!el) { return; } 54 | debug('onClick'); 55 | var method = el.getAttribute('on-click'); 56 | if (typeof this[method] == 'function') { this[method](); } 57 | }, 58 | 59 | open(options) { 60 | if (this.isOpen) { return; } 61 | debug('open dialog'); 62 | this.isOpen = true; 63 | 64 | return this.show() 65 | .then(() => this.animateBackgroundIn(options)) 66 | .then(() => this.animateWindowIn()) 67 | .then(() => this.dispatch('opened')); 68 | }, 69 | 70 | close(options) { 71 | if (!this.isOpen) { return; } 72 | debug('close dialog'); 73 | this.isOpen = false; 74 | 75 | return this.animateWindowOut() 76 | .then(() => this.animateBackgroundOut()) 77 | .then(() => this.hide()) 78 | .then(() => this.dispatch('closed')); 79 | }, 80 | 81 | animateBackgroundIn(options) { 82 | if (options) { return this.animateBackgroundInFrom(options); } 83 | 84 | var el = this.els.background; 85 | return schedule.transition(() => { 86 | debug('animate background in'); 87 | el.classList.remove('animate-out'); 88 | el.classList.add('animate-in'); 89 | }, el, 'animationend'); 90 | }, 91 | 92 | animateBackgroundOut() { 93 | var el = this.els.background; 94 | return schedule.transition(() => { 95 | debug('animate background out'); 96 | el.classList.add('animate-out'); 97 | el.classList.remove('animate-in'); 98 | }, el, 'animationend') 99 | .then(() => el.style = ''); 100 | }, 101 | 102 | animateBackgroundInFrom(pos) { 103 | var el = this.els.background; 104 | var scale = Math.sqrt(window.innerWidth * window.innerHeight) / 15; 105 | var duration = scale * 9; 106 | 107 | return schedule.mutation(() => { 108 | el.classList.add('circular'); 109 | el.classList.remove('animate-out'); 110 | el.style.transform = `translate(${pos.clientX}px, ${pos.clientY}px)`; 111 | el.style.transitionDuration = duration + 'ms'; 112 | el.offsetTop; // Hack, any ideas? 113 | }) 114 | 115 | .then(() => { 116 | return schedule.transition(() => { 117 | debug('animate background in from', pos); 118 | el.style.transform += ` scale(${scale})`; 119 | el.style.opacity = 1; 120 | }, el, 'transitionend', duration * 1.5); 121 | }); 122 | }, 123 | 124 | show() { 125 | return schedule.mutation(() => { 126 | debug('show'); 127 | this.style.display = 'block'; 128 | }); 129 | }, 130 | 131 | hide() { 132 | return schedule.mutation(() => { 133 | debug('hide'); 134 | this.style.display = 'none'; 135 | }); 136 | }, 137 | 138 | animateWindowIn() { 139 | debug('animate window in'); 140 | var el = this.els.window; 141 | return schedule.transition(() => { 142 | debug('animate window in'); 143 | el.classList.add('animate-in'); 144 | el.classList.remove('animate-out'); 145 | }, el, 'animationend'); 146 | }, 147 | 148 | animateWindowOut() { 149 | debug('animate window out'); 150 | var el = this.els.window; 151 | return schedule.transition(() => { 152 | el.classList.add('animate-out'); 153 | el.classList.remove('animate-in'); 154 | }, el, 'animationend') 155 | .then(() => el.classList.remove('animate-out')); 156 | }, 157 | 158 | dispatch(name) { 159 | this.dispatchEvent(new CustomEvent(name)); 160 | }, 161 | 162 | attrs: { 163 | opened: { 164 | get: function() { return !!this.isOpen; }, 165 | set: function(value) { 166 | value = value === '' || value; 167 | if (!value) { this.close(); } 168 | else { this.open(); } 169 | } 170 | } 171 | }, 172 | 173 | template: ` 174 |
    175 |
    176 |
    177 |
    178 | `, 463 | 464 | globalCss: ` 465 | @keyframes fxos-dialog-entrance { 466 | 0% { transform: translateY(100px); } 467 | 100% { transform: translateY(0px); } 468 | } 469 | 470 | @keyframes fxos-dialog-fade-in { 471 | 0% { opacity: 0 } 472 | 100% { opacity: 1 } 473 | } 474 | 475 | @keyframes fxos-dialog-fade-out { 476 | 0% { opacity: 1 } 477 | 100% { opacity: 0 } 478 | }` 479 | }); 480 | 481 | /** 482 | * Utils 483 | */ 484 | 485 | function after(target, event, timeout) { 486 | return new Promise(resolve => { 487 | var timer = timeout && setTimeout(cb, timeout); 488 | target.addEventListener(event, cb); 489 | function cb() { 490 | target.removeEventListener(event, cb); 491 | clearTimeout(timer); 492 | resolve(); 493 | } 494 | }); 495 | } 496 | --------------------------------------------------------------------------------