From 6b68d022d8608b40d1309a402a2b9762dd220b2a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sat, 19 Sep 2015 08:34:58 -0400 Subject: [PATCH] feature(smalltalk) add --- .bowerrc | 3 + .gitignore | 2 + LICENSE | 21 +++ README.md | 70 ++++++++ bower.json | 32 ++++ css/smalltalk.css | 181 +++++++++++++++++++++ dist/smalltalk.js | 174 ++++++++++++++++++++ dist/smalltalk.min.css | 1 + dist/smalltalk.min.js | 1 + dist/smalltalk.poly.min.js | 2 + example/index.html | 15 ++ img/IDR_CLOSE_DIALOG.png | Bin 0 -> 139 bytes img/IDR_CLOSE_DIALOG_H.png | Bin 0 -> 214 bytes modules/promise-polyfill/.bower.json | 38 +++++ modules/promise-polyfill/Gruntfile.js | 23 +++ modules/promise-polyfill/LICENSE | 20 +++ modules/promise-polyfill/Promise.js | 190 ++++++++++++++++++++++ modules/promise-polyfill/Promise.min.js | 2 + modules/promise-polyfill/README.md | 63 ++++++++ modules/promise-polyfill/bower.json | 28 ++++ modules/promise-polyfill/jasmine.json | 7 + modules/promise-polyfill/package.json | 32 ++++ package.json | 40 +++++ src/smalltalk.js | 206 ++++++++++++++++++++++++ 24 files changed, 1151 insertions(+) create mode 100644 .bowerrc create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 bower.json create mode 100644 css/smalltalk.css create mode 100644 dist/smalltalk.js create mode 100644 dist/smalltalk.min.css create mode 100644 dist/smalltalk.min.js create mode 100644 dist/smalltalk.poly.min.js create mode 100644 example/index.html create mode 100644 img/IDR_CLOSE_DIALOG.png create mode 100644 img/IDR_CLOSE_DIALOG_H.png create mode 100644 modules/promise-polyfill/.bower.json create mode 100644 modules/promise-polyfill/Gruntfile.js create mode 100644 modules/promise-polyfill/LICENSE create mode 100644 modules/promise-polyfill/Promise.js create mode 100644 modules/promise-polyfill/Promise.min.js create mode 100644 modules/promise-polyfill/README.md create mode 100644 modules/promise-polyfill/bower.json create mode 100644 modules/promise-polyfill/jasmine.json create mode 100644 modules/promise-polyfill/package.json create mode 100644 package.json create mode 100644 src/smalltalk.js diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 0000000..0ab1766 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory" : "modules" +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93f1361 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +npm-debug.log diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a103b66 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 coderaiser + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b0fae7d --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +Smalltalk +==== + +Simple replacement of native Alert, Confirm and Prompt. + +# Benefits + +- `5kb` javascript +- `4kb` css +- [Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise) based + +# Install +With help of [bower](http://bower.io "Bower"). + +``` +bower install smalltalk +``` + +Or npm: + +``` +npm i smalltalk +``` + +# API + +## smalltalk.alert(title, message) +```js +smalltalk.alert('Error', 'There was error!').then(function() { + console.log('ok'); +}) +``` + +## smalltalk.confirm(title, message) +```js +smalltalk.confirm('Question', 'Are you sure?').then(function() { + console.log('yes'); +}, function() { + console.log('no'); +}) +``` + +## smalltalk.prompt(title, message, value) +```js +smalltalk.prompt('Question', 'How old are you?', '10').then(function(value) { + console.log(value); +}, function() { + console.log('cancel'); +}) +``` + +#How use? +Create `html` page: + +```html + + + +``` + +#License +MIT diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..c13d684 --- /dev/null +++ b/bower.json @@ -0,0 +1,32 @@ +{ + "name": "smalltalk", + "version": "1.0.0", + "homepage": "https://github.com/coderaiser/smalltalk", + "authors": [ + "coderaiser " + ], + "description": "Modal Alert, Confirm and Prompt", + "main": "dist/smalltalk.min.js", + "moduleType": [ + "globals", + "node" + ], + "keywords": [ + "modal", + "alert", + "prompt", + "confirm" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "modules", + "test", + "tests" + ], + "dependencies": { + "promise-polyfill": "~2.1.0" + } +} diff --git a/css/smalltalk.css b/css/smalltalk.css new file mode 100644 index 0000000..5f3a942 --- /dev/null +++ b/css/smalltalk.css @@ -0,0 +1,181 @@ +.smalltalk { + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + + align-items: center; + flex-direction: column; + justify-content: center; + + -webkit-transition: 200ms opacity; + -moz-transition: 200ms opacity; + -ms-transition: 200ms opacity; + -o-transition: 200ms opacity; + transition: 200ms opacity; + + background-color: rgb(255, 255, 255); + background-color: rgba(255, 255, 255, 0.75); + bottom: 0; + left: 0; + overflow: auto; + padding: 20px; + position: fixed; + right: 0; + top: 0; +} + +.smalltalk .page { + border-radius: 3px; + background: white; + box-shadow: 0 4px 23px 5px rgba(0, 0, 0, 0.2), 0 2px 6px rgba(0,0,0,0.15); + color: #333; + min-width: 400px; + padding: 0; + position: relative; + z-index: 0; +} + +@media only screen and (max-width: 500px) { + .smalltalk .page { + min-width: 0; + } +} + +.smalltalk .page > .close-button { + background-image: url(../img/IDR_CLOSE_DIALOG.png); + background-position: center; + background-repeat: no-repeat; + height: 14px; + position: absolute; + right: 7px; + top: 7px; + width: 14px; + z-index: 1; +} + +.smalltalk .page > .close-button:hover { + background-image: url(../img/IDR_CLOSE_DIALOG_H.png); +} + +.smalltalk .page h1 { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + color: #333; + font-size: 120%; + margin: 0; + padding: 14px 17px 14px; + text-shadow: white 0 1px 2px; +} + +.smalltalk .page .content-area { + overflow: auto; + padding: 6px 17px 6px; + position: relative; +} + +.smalltalk .page .action-area { + padding: 14px 17px; +} + +.smalltalk .page .button-strip { + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display:flex; + + flex-direction: row; + justify-content: flex-end; +} + +.smalltalk .page .button-strip > button { + -webkit-margin-start: 10px; + -moz-margin-start: 10px; + -ms-margin-start: 10px; +} + +.smalltalk button:enabled:focus, .smalltalk input:enabled:focus { + -webkit-transition: border-color 200ms; + -moz-transition: border-color 200ms; + -ms-transition: border-color 200ms; + -o-transition: border-color 200ms; + transition: border-color 200ms; + border-color: rgb(77, 144, 254); + outline: none; +} + +.smalltalk button:enabled:active { + background-image: -webkit-linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7); + background-image: -moz-linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7); + background-image: -ms-linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7); + background-image: linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7); + + box-shadow: none; + text-shadow: none; +} + +.smalltalk button, .smalltalk .smalltalk { + min-height: 2em; + min-width: 4em; +} + +.smalltalk button::-moz-focus-inner { + border: 0; +} + +.smalltalk button { + -webkit-appearance: none; + -moz-appearance: none; + -ms-appearance: none; + appearance: none; + + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + background-image: -webkit-linear-gradient(#ededed, #ededed 38%, #dedede); + background-image: -moz-linear-gradient(#ededed, #ededed 38%, #dedede); + background-image: -ms-linear-gradient(#ededed, #ededed 38%, #dedede); + background-image: linear-gradient(#ededed, #ededed 38%, #dedede); + + border: 1px solid rgb(0, 0, 0); + border: 1px solid rgba(0, 0, 0, 0.25); + border-radius: 2px; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.08), inset 0 1px 2px rgba(255, 255, 255, 0.75); + color: #444; + font: inherit; + margin: 0 1px 0 0; + outline: none; + text-shadow: 0 1px 0 rgb(240, 240, 240); +} + +.smalltalk button:enabled:focus, .smalltalk input:enabled:focus { + -webkit-transition: border-color 200ms; + -moz-transition: border-color 200ms; + -ms-transition: border-color 200ms; + -o-transition: border-color 200ms; + transition: border-color 200ms; + border-color: rgb(77, 144, 254); + outline: none; +} + +.smalltalk input { + width: 100%; + border: 1px solid #bfbfbf; + border-radius: 2px; + box-sizing: border-box; + color: #444; + font: inherit; + margin: 0; + min-height: 2em; + padding: 3px; + outline: none; +} + + +button { + font-family: Ubuntu, Arial, sans-serif; +} diff --git a/dist/smalltalk.js b/dist/smalltalk.js new file mode 100644 index 0000000..dfe68c5 --- /dev/null +++ b/dist/smalltalk.js @@ -0,0 +1,174 @@ +'use strict'; + +(function (global) { + 'use strict'; + + if (typeof module !== 'undefined' && module.exports) module.exports = SmallTalk();else global.smalltalk = SmallTalk(); + + function SmallTalk(callback) { + if (!(this instanceof SmallTalk)) return new SmallTalk(callback); + + var remove = bind(removeEl, '.smalltalk'); + + var BUTTON_OK = ['OK']; + var BUTTON_OK_CANCEL = ['Cancel', 'OK']; + + this.alert = function (title, msg) { + var empty = function empty() {}; + + return showDialog(title, msg, '', BUTTON_OK)['catch'](empty); + }; + + this.prompt = function (title, msg, value) { + var val = value || ''; + var valueStr = ''; + + return showDialog(title, msg, valueStr, BUTTON_OK_CANCEL); + }; + + this.confirm = function (title, msg) { + return showDialog(title, msg, '', BUTTON_OK_CANCEL); + }; + + function getTemplate(title, msg, value, buttons) { + if (!Array.isArray(buttons)) throw Error('buttons should be array!'); + + return '
\n
\n

' + title + '

\n
\n ' + msg + '\n ' + value + '\n
\n
\n
' + buttons.map(function (name, i) { + return ''; + }).join('') + '\n
\n
\n
'; + } + + function showDialog(title, msg, value, buttons) { + var dialog = document.createElement('div'), + closeButtons = ['cancel', 'close', 'ok'], + ok = undefined, + cancel = undefined, + promise = new Promise(function (resolve, reject) { + ok = resolve; + cancel = reject; + }), + tmpl = getTemplate(title, msg, value, buttons); + + dialog.innerHTML = tmpl; + dialog.className = 'smalltalk'; + + document.body.appendChild(dialog); + + find(dialog, ['ok']).forEach(function (el) { + return el.focus(); + }); + + find(dialog, ['input']).forEach(function (el) { + return el.setSelectionRange(0, value.length); + }); + + addListeterAll('click', dialog, closeButtons, function (event) { + return closeDialog(event.target, dialog, ok, cancel); + }); + + ['click', 'contextmenu'].forEach(function (event) { + return dialog.addEventListener(event, function () { + return find(dialog, ['ok', 'input']).forEach(function (el) { + return el.focus(); + }); + }); + }); + + dialog.addEventListener('keydown', function (event) { + var ENTER = 13; + var ESC = 27; + var TAB = 9; + + var keyCode = event.keyCode, + el = event.target; + + switch (keyCode) { + case ENTER: + closeDialog(el, dialog, ok, cancel); + break; + + case ESC: + remove(); + cancel(); + break; + + case TAB: + var namesAll = ['cancel', 'ok', 'input'], + names = find(dialog, namesAll).map(function (el) { + return el.getAttribute('data-name').replace('js-', ''); + }); + + if (event.shiftKey) tab(dialog, names); + + tab(dialog, names); + event.preventDefault(); + break; + } + }); + + return promise; + } + + function tab(dialog, names) { + var active = document.activeElement, + activeName = active.getAttribute('data-name').replace('js-', ''), + count = names.length - 1, + index = names.indexOf(activeName); + + if (index === count) index = 0;else if (index < count) ++index; + + var name = names[index]; + + find(dialog, [name]).forEach(function (el) { + return el.focus(); + }); + } + + function closeDialog(el, dialog, ok, cancel) { + var value = undefined, + name = el.getAttribute('data-name').replace('js-', ''); + + if (/close|cancel/.test(name)) { + cancel(); + } else { + value = find(dialog, ['input']).reduce(function (value, el) { + return el.value; + }, null); + + ok(value); + } + + remove(); + } + + function find(element, names) { + var elements = names.map(function (name) { + return element.querySelector('[data-name="js-' + name + '"]'); + }).filter(function (el) { + return el; + }); + + return elements; + } + + function addListeterAll(event, parent, elements, fn) { + find(parent, elements).forEach(function (el) { + return el.addEventListener(event, function (event) { + return fn(event); + }); + }); + } + + function removeEl(name) { + var el = document.querySelector(name); + + el.parentElement.removeChild(el); + } + + function bind(fn) { + var args = [].slice.call(arguments, 1); + + return fn.bind(null, args); + } + } +})(typeof window !== 'undefined' && window); \ No newline at end of file diff --git a/dist/smalltalk.min.css b/dist/smalltalk.min.css new file mode 100644 index 0000000..82b9707 --- /dev/null +++ b/dist/smalltalk.min.css @@ -0,0 +1 @@ +.smalltalk{display:-webkit-flex;display:-moz-box;display:-ms-flexbox;display:flex;align-items:center;flex-direction:column;justify-content:center;-webkit-transition:.2s opacity;-moz-transition:.2s opacity;-ms-transition:.2s opacity;-o-transition:.2s opacity;transition:.2s opacity;background-color:#fff;background-color:rgba(255,255,255,.75);bottom:0;left:0;overflow:auto;padding:20px;position:fixed;right:0;top:0}.smalltalk .page{border-radius:3px;background:#fff;box-shadow:0 4px 23px 5px rgba(0,0,0,.2),0 2px 6px rgba(0,0,0,.15);color:#333;min-width:400px;padding:0;position:relative;z-index:0}@media only screen and (max-width:500px){.smalltalk .page{min-width:0}}.smalltalk .page>.close-button{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAQAAAC1QeVaAAAAUklEQVR4XqXPYQrAIAhAYW/gXd8NJxTopVqsGEhtf+L9/ERU2k/HSMFQpKcYJeNFI9Be0LCMij8cYyjj5EHIivGBkwLfrbX3IF8PqumVmnDpEG+eDsKibPG2JwAAAABJRU5ErkJggg==);background-position:center;background-repeat:no-repeat;height:14px;position:absolute;right:7px;top:7px;width:14px;z-index:1}.smalltalk .page>.close-button:hover{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAQAAAC1QeVaAAAAnUlEQVR4XoWQQQ6CQAxFewjkJkMCyXgJPMk7AiYczyBeZEAX6AKctGIaN+bt+trk9wtGQc/IkhnoKGxqqiWxOSZalapWFZ6VrIUDExsN0a5JRBq9LoVOR0eEQMoEhKizXhhsn0p1sCWVo7CwOf1RytPL8CPvwuBUoHL6ugeK30CVD1TqK7V/hdpe+VNChhOzV8xWny/+xosHF8578W/Hmc1OOC3wmwAAAABJRU5ErkJggg==)}.smalltalk .page h1{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;color:#333;font-size:120%;margin:0;padding:14px 17px;text-shadow:#fff 0 1px 2px}.smalltalk .page .content-area{overflow:auto;padding:6px 17px;position:relative}.smalltalk .page .action-area{padding:14px 17px}.smalltalk .page .button-strip{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:flex;flex-direction:row;justify-content:flex-end}.smalltalk .page .button-strip>button{-webkit-margin-start:10px;-moz-margin-start:10px;-ms-margin-start:10px}.smalltalk button:enabled:active{background-image:-webkit-linear-gradient(#e7e7e7,#e7e7e7 38%,#d7d7d7);background-image:-moz-linear-gradient(#e7e7e7,#e7e7e7 38%,#d7d7d7);background-image:-ms-linear-gradient(#e7e7e7,#e7e7e7 38%,#d7d7d7);background-image:linear-gradient(#e7e7e7,#e7e7e7 38%,#d7d7d7);box-shadow:none;text-shadow:none}.smalltalk .smalltalk,.smalltalk button{min-height:2em;min-width:4em}.smalltalk button::-moz-focus-inner{border:0}.smalltalk button{-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;appearance:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:-webkit-linear-gradient(#ededed,#ededed 38%,#dedede);background-image:-moz-linear-gradient(#ededed,#ededed 38%,#dedede);background-image:-ms-linear-gradient(#ededed,#ededed 38%,#dedede);background-image:linear-gradient(#ededed,#ededed 38%,#dedede);border:1px solid #000;border:1px solid rgba(0,0,0,.25);border-radius:2px;box-shadow:0 1px 0 rgba(0,0,0,.08),inset 0 1px 2px rgba(255,255,255,.75);color:#444;font:inherit;margin:0 1px 0 0;outline:0;text-shadow:0 1px 0 #f0f0f0}.smalltalk button:enabled:focus,.smalltalk input:enabled:focus{-webkit-transition:border-color .2s;-moz-transition:border-color .2s;-ms-transition:border-color .2s;-o-transition:border-color .2s;transition:border-color .2s;border-color:#4d90fe;outline:0}.smalltalk input{width:100%;border:1px solid #bfbfbf;border-radius:2px;box-sizing:border-box;color:#444;font:inherit;margin:0;min-height:2em;padding:3px;outline:0}button{font-family:Ubuntu,Arial,sans-serif} diff --git a/dist/smalltalk.min.js b/dist/smalltalk.min.js new file mode 100644 index 0000000..3fd8af5 --- /dev/null +++ b/dist/smalltalk.min.js @@ -0,0 +1 @@ +"use strict";!function(n){function t(n){function e(n,t,e,r){if(!Array.isArray(r))throw Error("buttons should be array!");return'
\n
\n

'+n+'

\n
\n '+t+"\n "+e+'\n
\n
\n
'+r.map(function(n,t){return""}).join("")+"\n
\n
\n
"}function r(n,t,r,c){var s=document.createElement("div"),d=["cancel","close","ok"],f=void 0,v=void 0,m=new Promise(function(n,t){f=n,v=t}),p=e(n,t,r,c);return s.innerHTML=p,s.className="smalltalk",document.body.appendChild(s),i(s,["ok"]).forEach(function(n){return n.focus()}),i(s,["input"]).forEach(function(n){return n.setSelectionRange(0,r.length)}),o("click",s,d,function(n){return u(n.target,s,f,v)}),["click","contextmenu"].forEach(function(n){return s.addEventListener(n,function(){return i(s,["ok","input"]).forEach(function(n){return n.focus()})})}),s.addEventListener("keydown",function(n){var t=13,e=27,r=9,o=n.keyCode,c=n.target;switch(o){case t:u(c,s,f,v);break;case e:l(),v();break;case r:var d=["cancel","ok","input"],m=i(s,d).map(function(n){return n.getAttribute("data-name").replace("js-","")});n.shiftKey&&a(s,m),a(s,m),n.preventDefault()}}),m}function a(n,t){var e=document.activeElement,r=e.getAttribute("data-name").replace("js-",""),a=t.length-1,u=t.indexOf(r);u===a?u=0:a>u&&++u;var o=t[u];i(n,[o]).forEach(function(n){return n.focus()})}function u(n,t,e,r){var a=void 0,u=n.getAttribute("data-name").replace("js-","");/close|cancel/.test(u)?r():(a=i(t,["input"]).reduce(function(n,t){return t.value},null),e(a)),l()}function i(n,t){var e=t.map(function(t){return n.querySelector('[data-name="js-'+t+'"]')}).filter(function(n){return n});return e}function o(n,t,e,r){i(t,e).forEach(function(t){return t.addEventListener(n,function(n){return r(n)})})}function c(n){var t=document.querySelector(n);t.parentElement.removeChild(t)}function s(n){var t=[].slice.call(arguments,1);return n.bind(null,t)}if(!(this instanceof t))return new t(n);var l=s(c,".smalltalk"),d=["OK"],f=["Cancel","OK"];this.alert=function(n,t){var e=function(){};return r(n,t,"",d)["catch"](e)},this.prompt=function(n,t,e){var a=e||"",u='';return r(n,t,u,f)},this.confirm=function(n,t){return r(n,t,"",f)}}"undefined"!=typeof module&&module.exports?module.exports=t():n.smalltalk=t()}("undefined"!=typeof window&&window); diff --git a/dist/smalltalk.poly.min.js b/dist/smalltalk.poly.min.js new file mode 100644 index 0000000..cff3688 --- /dev/null +++ b/dist/smalltalk.poly.min.js @@ -0,0 +1,2 @@ +/*! promise-polyfill 2.1.0 */ +!function(a){function b(a,b){return function(){a.apply(b,arguments)}}function c(a){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof a)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],i(a,b(e,this),b(f,this))}function d(a){var b=this;return null===this._state?void this._deferreds.push(a):void j(function(){var c=b._state?a.onFulfilled:a.onRejected;if(null===c)return void(b._state?a.resolve:a.reject)(b._value);var d;try{d=c(b._value)}catch(e){return void a.reject(e)}a.resolve(d)})}function e(a){try{if(a===this)throw new TypeError("A promise cannot be resolved with itself.");if(a&&("object"==typeof a||"function"==typeof a)){var c=a.then;if("function"==typeof c)return void i(b(c,a),b(e,this),b(f,this))}this._state=!0,this._value=a,g.call(this)}catch(d){f.call(this,d)}}function f(a){this._state=!1,this._value=a,g.call(this)}function g(){for(var a=0,b=this._deferreds.length;b>a;a++)d.call(this,this._deferreds[a]);this._deferreds=null}function h(a,b,c,d){this.onFulfilled="function"==typeof a?a:null,this.onRejected="function"==typeof b?b:null,this.resolve=c,this.reject=d}function i(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(e){if(d)return;d=!0,c(e)}}var j="function"==typeof setImmediate&&setImmediate||function(a){setTimeout(a,1)},k=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)};c.prototype["catch"]=function(a){return this.then(null,a)},c.prototype.then=function(a,b){var e=this;return new c(function(c,f){d.call(e,new h(a,b,c,f))})},c.all=function(){var a=Array.prototype.slice.call(1===arguments.length&&k(arguments[0])?arguments[0]:arguments);return new c(function(b,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h)return void h.call(g,function(a){d(f,a)},c)}a[f]=g,0===--e&&b(a)}catch(i){c(i)}}if(0===a.length)return b([]);for(var e=a.length,f=0;fd;d++)a[d].then(b,c)})},c._setImmediateFn=function(a){j=a},"undefined"!=typeof module&&module.exports?module.exports=c:a.Promise||(a.Promise=c)}(this);"use strict";!function(n){function t(n){function e(n,t,e,r){if(!Array.isArray(r))throw Error("buttons should be array!");return'
\n
\n

'+n+'

\n
\n '+t+"\n "+e+'\n
\n
\n
'+r.map(function(n,t){return""}).join("")+"\n
\n
\n
"}function r(n,t,r,c){var s=document.createElement("div"),d=["cancel","close","ok"],f=void 0,v=void 0,m=new Promise(function(n,t){f=n,v=t}),p=e(n,t,r,c);return s.innerHTML=p,s.className="smalltalk",document.body.appendChild(s),i(s,["ok"]).forEach(function(n){return n.focus()}),i(s,["input"]).forEach(function(n){return n.setSelectionRange(0,r.length)}),o("click",s,d,function(n){return u(n.target,s,f,v)}),["click","contextmenu"].forEach(function(n){return s.addEventListener(n,function(){return i(s,["ok","input"]).forEach(function(n){return n.focus()})})}),s.addEventListener("keydown",function(n){var t=13,e=27,r=9,o=n.keyCode,c=n.target;switch(o){case t:u(c,s,f,v);break;case e:l(),v();break;case r:var d=["cancel","ok","input"],m=i(s,d).map(function(n){return n.getAttribute("data-name").replace("js-","")});n.shiftKey&&a(s,m),a(s,m),n.preventDefault()}}),m}function a(n,t){var e=document.activeElement,r=e.getAttribute("data-name").replace("js-",""),a=t.length-1,u=t.indexOf(r);u===a?u=0:a>u&&++u;var o=t[u];i(n,[o]).forEach(function(n){return n.focus()})}function u(n,t,e,r){var a=void 0,u=n.getAttribute("data-name").replace("js-","");/close|cancel/.test(u)?r():(a=i(t,["input"]).reduce(function(n,t){return t.value},null),e(a)),l()}function i(n,t){var e=t.map(function(t){return n.querySelector('[data-name="js-'+t+'"]')}).filter(function(n){return n});return e}function o(n,t,e,r){i(t,e).forEach(function(t){return t.addEventListener(n,function(n){return r(n)})})}function c(n){var t=document.querySelector(n);t.parentElement.removeChild(t)}function s(n){var t=[].slice.call(arguments,1);return n.bind(null,t)}if(!(this instanceof t))return new t(n);var l=s(c,".smalltalk"),d=["OK"],f=["Cancel","OK"];this.alert=function(n,t){var e=function(){};return r(n,t,"",d)["catch"](e)},this.prompt=function(n,t,e){var a=e||"",u='';return r(n,t,u,f)},this.confirm=function(n,t){return r(n,t,"",f)}}"undefined"!=typeof module&&module.exports?module.exports=t():n.smalltalk=t()}("undefined"!=typeof window&&window); diff --git a/example/index.html b/example/index.html new file mode 100644 index 0000000..1bd3e1a --- /dev/null +++ b/example/index.html @@ -0,0 +1,15 @@ + + + + + + diff --git a/img/IDR_CLOSE_DIALOG.png b/img/IDR_CLOSE_DIALOG.png new file mode 100644 index 0000000000000000000000000000000000000000..9e2956d9de2f4f0943ef75448a25d96b6420f93b GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh0wlLOK8*rWL7py-Ar)~;&nI#nP~dP#%zqGj zpI2Sv#nPxX5+1qrkN*B~3AyEe+~Z)tlI0Srk6o27#9i3X(`7G{tnv7X>ru6{1-oD!M-z=NloUwLR^Z2_zR{vTau*ApS}Hc>&bU+H^=iQCP}`~$jMrzy3tZCYU-*m z(RowXv@#1z^Ilx%=_0jPuhq}py~W`aOUsJQaS}Q6y-GKzPF=iVgXQ1AQ <%= pkg.version %> */\n' + }, + dist: { + files: { + 'Promise.min.js': ['Promise.js'] + } + } + } + + }); + + grunt.loadNpmTasks('grunt-contrib-uglify'); + + grunt.registerTask('build', ['uglify']); + +}; diff --git a/modules/promise-polyfill/LICENSE b/modules/promise-polyfill/LICENSE new file mode 100644 index 0000000..94b9dac --- /dev/null +++ b/modules/promise-polyfill/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2014 Taylor Hakes +Copyright (c) 2014 Forbes Lindesay + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/modules/promise-polyfill/Promise.js b/modules/promise-polyfill/Promise.js new file mode 100644 index 0000000..47453e5 --- /dev/null +++ b/modules/promise-polyfill/Promise.js @@ -0,0 +1,190 @@ +(function(root) { + + // Use polyfill for setImmediate for performance gains + var asap = (typeof setImmediate === 'function' && setImmediate) || + function(fn) { setTimeout(fn, 1); }; + + // Polyfill for Function.prototype.bind + function bind(fn, thisArg) { + return function() { + fn.apply(thisArg, arguments); + } + } + + var isArray = Array.isArray || function(value) { return Object.prototype.toString.call(value) === "[object Array]" }; + + function Promise(fn) { + if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new'); + if (typeof fn !== 'function') throw new TypeError('not a function'); + this._state = null; + this._value = null; + this._deferreds = [] + + doResolve(fn, bind(resolve, this), bind(reject, this)) + } + + function handle(deferred) { + var me = this; + if (this._state === null) { + this._deferreds.push(deferred); + return + } + asap(function() { + var cb = me._state ? deferred.onFulfilled : deferred.onRejected + if (cb === null) { + (me._state ? deferred.resolve : deferred.reject)(me._value); + return; + } + var ret; + try { + ret = cb(me._value); + } + catch (e) { + deferred.reject(e); + return; + } + deferred.resolve(ret); + }) + } + + function resolve(newValue) { + try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure + if (newValue === this) throw new TypeError('A promise cannot be resolved with itself.'); + if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { + var then = newValue.then; + if (typeof then === 'function') { + doResolve(bind(then, newValue), bind(resolve, this), bind(reject, this)); + return; + } + } + this._state = true; + this._value = newValue; + finale.call(this); + } catch (e) { reject.call(this, e); } + } + + function reject(newValue) { + this._state = false; + this._value = newValue; + finale.call(this); + } + + function finale() { + for (var i = 0, len = this._deferreds.length; i < len; i++) { + handle.call(this, this._deferreds[i]); + } + this._deferreds = null; + } + + function Handler(onFulfilled, onRejected, resolve, reject){ + this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; + this.onRejected = typeof onRejected === 'function' ? onRejected : null; + this.resolve = resolve; + this.reject = reject; + } + + /** + * Take a potentially misbehaving resolver function and make sure + * onFulfilled and onRejected are only called once. + * + * Makes no guarantees about asynchrony. + */ + function doResolve(fn, onFulfilled, onRejected) { + var done = false; + try { + fn(function (value) { + if (done) return; + done = true; + onFulfilled(value); + }, function (reason) { + if (done) return; + done = true; + onRejected(reason); + }) + } catch (ex) { + if (done) return; + done = true; + onRejected(ex); + } + } + + Promise.prototype['catch'] = function (onRejected) { + return this.then(null, onRejected); + }; + + Promise.prototype.then = function(onFulfilled, onRejected) { + var me = this; + return new Promise(function(resolve, reject) { + handle.call(me, new Handler(onFulfilled, onRejected, resolve, reject)); + }) + }; + + Promise.all = function () { + var args = Array.prototype.slice.call(arguments.length === 1 && isArray(arguments[0]) ? arguments[0] : arguments); + + return new Promise(function (resolve, reject) { + if (args.length === 0) return resolve([]); + var remaining = args.length; + function res(i, val) { + try { + if (val && (typeof val === 'object' || typeof val === 'function')) { + var then = val.then; + if (typeof then === 'function') { + then.call(val, function (val) { res(i, val) }, reject); + return; + } + } + args[i] = val; + if (--remaining === 0) { + resolve(args); + } + } catch (ex) { + reject(ex); + } + } + for (var i = 0; i < args.length; i++) { + res(i, args[i]); + } + }); + }; + + Promise.resolve = function (value) { + if (value && typeof value === 'object' && value.constructor === Promise) { + return value; + } + + return new Promise(function (resolve) { + resolve(value); + }); + }; + + Promise.reject = function (value) { + return new Promise(function (resolve, reject) { + reject(value); + }); + }; + + Promise.race = function (values) { + return new Promise(function (resolve, reject) { + for(var i = 0, len = values.length; i < len; i++) { + values[i].then(resolve, reject); + } + }); + }; + + /** + * Set the immediate function to execute callbacks + * @param fn {function} Function to execute + * @private + */ + Promise._setImmediateFn = function _setImmediateFn(fn) { + asap = fn; + }; + + if (typeof module !== 'undefined' && module.exports) { + module.exports = Promise; + } else if (!root.Promise) { + root.Promise = Promise; + } + +})(this); \ No newline at end of file diff --git a/modules/promise-polyfill/Promise.min.js b/modules/promise-polyfill/Promise.min.js new file mode 100644 index 0000000..fe61cac --- /dev/null +++ b/modules/promise-polyfill/Promise.min.js @@ -0,0 +1,2 @@ +/*! promise-polyfill 2.1.0 */ +!function(a){function b(a,b){return function(){a.apply(b,arguments)}}function c(a){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof a)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],i(a,b(e,this),b(f,this))}function d(a){var b=this;return null===this._state?void this._deferreds.push(a):void j(function(){var c=b._state?a.onFulfilled:a.onRejected;if(null===c)return void(b._state?a.resolve:a.reject)(b._value);var d;try{d=c(b._value)}catch(e){return void a.reject(e)}a.resolve(d)})}function e(a){try{if(a===this)throw new TypeError("A promise cannot be resolved with itself.");if(a&&("object"==typeof a||"function"==typeof a)){var c=a.then;if("function"==typeof c)return void i(b(c,a),b(e,this),b(f,this))}this._state=!0,this._value=a,g.call(this)}catch(d){f.call(this,d)}}function f(a){this._state=!1,this._value=a,g.call(this)}function g(){for(var a=0,b=this._deferreds.length;b>a;a++)d.call(this,this._deferreds[a]);this._deferreds=null}function h(a,b,c,d){this.onFulfilled="function"==typeof a?a:null,this.onRejected="function"==typeof b?b:null,this.resolve=c,this.reject=d}function i(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(e){if(d)return;d=!0,c(e)}}var j="function"==typeof setImmediate&&setImmediate||function(a){setTimeout(a,1)},k=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)};c.prototype["catch"]=function(a){return this.then(null,a)},c.prototype.then=function(a,b){var e=this;return new c(function(c,f){d.call(e,new h(a,b,c,f))})},c.all=function(){var a=Array.prototype.slice.call(1===arguments.length&&k(arguments[0])?arguments[0]:arguments);return new c(function(b,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h)return void h.call(g,function(a){d(f,a)},c)}a[f]=g,0===--e&&b(a)}catch(i){c(i)}}if(0===a.length)return b([]);for(var e=a.length,f=0;fd;d++)a[d].then(b,c)})},c._setImmediateFn=function(a){j=a},"undefined"!=typeof module&&module.exports?module.exports=c:a.Promise||(a.Promise=c)}(this); \ No newline at end of file diff --git a/modules/promise-polyfill/README.md b/modules/promise-polyfill/README.md new file mode 100644 index 0000000..41c8b9c --- /dev/null +++ b/modules/promise-polyfill/README.md @@ -0,0 +1,63 @@ + + Promises/A+ logo + +Promise [![Build Status](https://travis-ci.org/taylorhakes/promise-polyfill.png?branch=master)](https://travis-ci.org/taylorhakes/promise-polyfill) +============= + +Lightweight promise polyfill for the browser and node. A+ Compliant. It is a perfect polyfill IE, Firefox or any other browser that does not support native promises. + +This implementation is based on [then/promise](https://github.com/then/promise). It has been changed to use the prototype for performance and memory reasons. + +For API information about Promises, please check out this article [HTML5Rocks article](http://www.html5rocks.com/en/tutorials/es6/promises/). + +It is extremely lightweight. ***< 1kb Gzipped*** + +## Browser Support +IE8+, Chrome, Firefox, IOS 4+, Safari 5+, Opera + +## Downloads + +- [Promise](https://raw.github.com/taylorhakes/promise-polyfill/master/Promise.js) +- [Promise-min](https://raw.github.com/taylorhakes/promise-polyfill/master/Promise.min.js) + +### Node +``` +npm install promise-polyfill +``` +### Bower +``` +bower install promise-polyfill +``` + +## Simple use +``` +var prom = new Promise(function(resolve, reject) { + // do a thing, possibly async, then… + + if (/* everything turned out fine */) { + resolve("Stuff worked!"); + } else { + reject(new Error("It broke")); + } +}); + +// Do something when async done +prom.then(function() { + ... +}); +``` +## Performance +By default promise-polyfill uses `setImmediate`, but falls back to `setTimeout` for executing asynchronously. If a browser does not support `setImmediate`, you may see performance issues. +Use a `setImmediate` polyfill to fix this issue. [setAsap](https://github.com/taylorhakes/setAsap) or [setImmediate](https://github.com/YuzuJS/setImmediate) work well. + +If you polyfill `window.setImmediate` or use `Promise._setImmediateFn(immedateFn)` it will be used instead of `window.setTimeout` + +## Testing +``` +npm install +npm test +``` + +## License +MIT diff --git a/modules/promise-polyfill/bower.json b/modules/promise-polyfill/bower.json new file mode 100644 index 0000000..d7c12b6 --- /dev/null +++ b/modules/promise-polyfill/bower.json @@ -0,0 +1,28 @@ +{ + "name": "promise-polyfill", + "version": "2.1.0", + "homepage": "https://github.com/taylorhakes/promise-polyfill", + "authors": [ + "Taylor Hakes" + ], + "description": "Lightweight promise polyfill for the browser and node. A+ Compliant.", + "main": "Promise.js", + "moduleType": [ + "globals", + "node" + ], + "keywords": [ + "promise", + "es6", + "polyfill", + "html5" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/modules/promise-polyfill/jasmine.json b/modules/promise-polyfill/jasmine.json new file mode 100644 index 0000000..9f4542e --- /dev/null +++ b/modules/promise-polyfill/jasmine.json @@ -0,0 +1,7 @@ +{ + "spec_dir": "tests", + "spec_files": [ + "**/*.spec.js" + ], + "helpers": [] +} \ No newline at end of file diff --git a/modules/promise-polyfill/package.json b/modules/promise-polyfill/package.json new file mode 100644 index 0000000..8817f83 --- /dev/null +++ b/modules/promise-polyfill/package.json @@ -0,0 +1,32 @@ +{ + "name": "promise-polyfill", + "version": "2.1.0", + "description": "Lightweight promise polyfill. A+ compliant", + "main": "Promise.js", + "scripts": { + "test": "./node_modules/.bin/promises-aplus-tests tests/adapter.js && JASMINE_CONFIG_PATH=jasmine.json ./node_modules/jasmine/bin/jasmine.js;" + }, + "repository": { + "type": "git", + "url": "https://taylorhakes@github.com/taylorhakes/promise-polyfill.git" + }, + "author": "Taylor Hakes", + "license": "MIT", + "bugs": { + "url": "https://github.com/taylorhakes/promise-polyfill/issues" + }, + "homepage": "https://github.com/taylorhakes/promise-polyfill", + "devDependencies": { + "grunt": "^0.4.4", + "grunt-contrib-uglify": "^0.4.0", + "jasmine": "^2.3.1", + "promises-aplus-tests": "*" + }, + "keywords": [ + "promise", + "promise-polyfill", + "ES6", + "promises-aplus" + ], + "dependencies": {} +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8856b1e --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "smalltalk", + "version": "1.0.0", + "description": "Modal Alert, Confirm and Prompt", + "homepage": "http://github.com/coderaiser/smalltalk", + "repository": { + "type": "git", + "url": "git://github.com/coderaiser/smalltalk.git" + }, + "main": "dist/smalltalk.min.js", + "scripts": { + "watch": "watch 'npm run 6to5' dist", + "6to5": "babel -d dist src", + "build-css": "minify css/smalltalk.css > dist/smalltalk.min.css", + "build-js": "npm run 6to5 && minify dist/smalltalk.js > dist/smalltalk.min.js", + "build-poly": "cat modules/promise-polyfill/Promise.min.js dist/smalltalk.min.js > dist/smalltalk.poly.min.js", + "build": "npm run build-css && npm run build-js", + "cat": "node -e \"var p=process,fs=require('fs');p.argv.slice(1).map(n => p.stdout.write(fs.readFileSync(n, 'utf8')))\"", + "bower": "bower", + "babel": "babel", + "minify": "minify", + "wisdom": "npm run build" + }, + "keywords": [ + "modal", + "alert", + "confirm", + "prompt" + ], + "author": "coderaiser (http://coderaiser.github.io/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/coderaiser/smalltalk/issues" + }, + "devDependencies": { + "babel": "~5.8.23", + "minify": "~1.4.20", + "watch": "~0.16.0" + } +} diff --git a/src/smalltalk.js b/src/smalltalk.js new file mode 100644 index 0000000..656dcf8 --- /dev/null +++ b/src/smalltalk.js @@ -0,0 +1,206 @@ +(function(global) { + 'use strict'; + + if (typeof module !== 'undefined' && module.exports) + module.exports = SmallTalk(); + else + global.smalltalk = SmallTalk(); + + function SmallTalk(callback) { + if (!(this instanceof SmallTalk)) + return new SmallTalk(callback); + + let remove = bind(removeEl, '.smalltalk'); + + const BUTTON_OK = ['OK']; + const BUTTON_OK_CANCEL = ['Cancel', 'OK']; + + this.alert = (title, msg) => { + let empty = () => {}; + + return showDialog(title, msg, '', BUTTON_OK).catch(empty); + }; + + this.prompt = (title, msg, value) => { + let val = value || ''; + let valueStr = ``; + + return showDialog(title, msg, valueStr, BUTTON_OK_CANCEL); + }; + + this.confirm = (title, msg) => { + return showDialog(title, msg, '', BUTTON_OK_CANCEL); + }; + + function getTemplate(title, msg, value, buttons) { + if (!Array.isArray(buttons)) + throw Error('buttons should be array!'); + + return `
+
+

${ title }

+
+ ${ msg } + ${ value } +
+
+
${ + buttons.map((name, i) => + `` + ).join('') + } +
+
+
`; + } + + function showDialog(title, msg, value, buttons) { + let dialog = document.createElement('div'), + + closeButtons = [ + 'cancel', + 'close', + 'ok' + ], + + ok, cancel, + + promise = new Promise((resolve, reject) => { + ok = resolve; + cancel = reject; + }), + + tmpl = getTemplate(title, msg, value, buttons); + + dialog.innerHTML = tmpl; + dialog.className = 'smalltalk'; + + document.body.appendChild(dialog); + + find(dialog, ['ok']).forEach(el => + el.focus() + ); + + find(dialog, ['input']).forEach(el => + el.setSelectionRange(0, value.length) + ); + + addListeterAll('click', dialog, closeButtons, event => + closeDialog(event.target, dialog, ok, cancel) + ); + + ['click', 'contextmenu'].forEach(event => + dialog.addEventListener(event, () => + find(dialog, ['ok', 'input']).forEach(el => + el.focus() + ) + ) + ); + + dialog.addEventListener('keydown', event => { + const ENTER = 13; + const ESC = 27; + const TAB = 9; + + let keyCode = event.keyCode, + el = event.target; + + switch(keyCode) { + case ENTER: + closeDialog(el, dialog, ok, cancel); + break; + + case ESC: + remove(); + cancel(); + break; + + case TAB: + let namesAll = ['cancel', 'ok', 'input'], + names = find(dialog, namesAll).map(el => + el.getAttribute('data-name').replace('js-', '') + ); + + if (event.shiftKey) + tab(dialog, names); + + tab(dialog, names); + event.preventDefault(); + break; + } + }); + + return promise; + } + + function tab(dialog, names) { + let active = document.activeElement, + activeName = active.getAttribute('data-name') + .replace('js-', ''), + + count = names.length - 1, + index = names.indexOf(activeName); + + if (index === count) + index = 0; + else if (index < count) + ++index; + + let name = names[index]; + + find(dialog, [name]).forEach(el => + el.focus() + ); + } + + function closeDialog(el, dialog, ok, cancel) { + let value, + name = el + .getAttribute('data-name') + .replace('js-', ''); + + if (/close|cancel/.test(name)) { + cancel(); + } else { + value = find(dialog, ['input']).reduce((value, el) => { + return el.value; + }, null); + + ok(value); + } + + remove(); + } + + function find(element, names) { + let elements = names.map(name => + element.querySelector(`[data-name="js-${ name }"]`) + ).filter(el => + el + ); + + return elements; + } + + function addListeterAll(event, parent, elements, fn) { + find(parent, elements).forEach(el => + el.addEventListener(event, event => + fn(event) + ) + ); + } + + function removeEl(name) { + var el = document.querySelector(name); + + el.parentElement.removeChild(el); + } + + function bind(fn) { + var args = [].slice.call(arguments, 1); + + return fn.bind(null, args); + } + } + +})(typeof window !== 'undefined' && window);