diff --git a/HELP.md b/HELP.md index c2bc28fb..c9e43274 100644 --- a/HELP.md +++ b/HELP.md @@ -71,11 +71,13 @@ Cloud Commander supports command line parameters: | `--port` | set port number | `--minify` | enable minification | `--progress` | show progress of file operations +| `--html-dialogs` | show html dialogs | `--no-server` | do not start server | `--no-auth` | disable authorization | `--no-online` | load scripts from local server | `--no-minify` | disable minification | `--no-progress` | do not show progress of file operations +| `--no-html-dialogs` | do not use html dialogs If no parameters given Cloud Commander reads information from `~/.cloudcmd.json` and use port from it (`8000` default). if port variables `PORT` or `VCAP_APP_PORT` isn't exist. @@ -218,6 +220,7 @@ Here is description of options: "root" : "/", /* root directory */ "prefix" : "", /* url prefix */ "progress" : true /* show progress of file operations */ + "htmlDialogs" : false /* show html dialogs */ } ``` diff --git a/bin/cloudcmd.js b/bin/cloudcmd.js index 8a7557df..0606c526 100755 --- a/bin/cloudcmd.js +++ b/bin/cloudcmd.js @@ -32,7 +32,8 @@ 'online', 'minify', 'progress', - 'progress-of-copying' + 'progress-of-copying', + 'html-dialogs' ], default: { server : true, @@ -45,6 +46,8 @@ root : config('root') || '/', prefix : config('prefix') || '', progress : config('progress') || config('progressOfCopying'), + + 'html-dialogs' : config('htmlDialogs') }, alias: { v: 'version', @@ -80,6 +83,7 @@ config('progress', args.progress); config('prefix', args.prefix); config('root', args.root); + config('htmlDialogs', args['html-dialogs']); deprecate('progress-of-copying', 'progress'); diff --git a/bower.json b/bower.json index f1a0b89b..2a8889e6 100644 --- a/bower.json +++ b/bower.json @@ -21,16 +21,17 @@ "test" ], "dependencies": { - "fancybox": "~2.1.5", - "jquery": "~2.1.4", - "format-io": "~0.9.6", - "rendy": "~1.1.0", - "github": "~0.10.6", - "vk-openapi": "~0.0.1", "domtokenlist-shim": "~1.1.0", - "promise-polyfill": "~2.1.0", "emitify": "~1.2.0", + "fancybox": "~2.1.5", + "format-io": "~0.9.6", + "github": "~0.10.6", + "jquery": "~2.1.4", + "menu": "~0.7.9", "philip": "~1.3.0", - "menu": "~0.7.9" + "promise-polyfill": "~2.1.0", + "rendy": "~1.1.0", + "vk-openapi": "~0.0.1", + "smalltalk": "~1.5.0" } } diff --git a/img/screen/config.png b/img/screen/config.png index 21db334f..8c4fd69c 100644 Binary files a/img/screen/config.png and b/img/screen/config.png differ diff --git a/json/bin.json b/json/bin.json index 070b3db3..046e50eb 100644 --- a/json/bin.json +++ b/json/bin.json @@ -13,9 +13,11 @@ "--port ": "set port number", "--minify ": "enable minification", "--progress ": "show progress of file operations", + "--html-dialogs ": "use html dialogs", "--no-server ": "do not start server", "--no-auth ": "disable authorization", "--no-online ": "load scripts from local server", "--no-minify ": "disable minification", - "--no-progress ": "do not show progress of file operations" + "--no-progress ": "do not show progress of file operations", + "--no-html-dialogs ": "do not use html dialogs" } diff --git a/json/config.json b/json/config.json index 19ee0e77..06178dab 100644 --- a/json/config.json +++ b/json/config.json @@ -18,5 +18,6 @@ "ip": null, "root": "/", "prefix": "", - "progress": true + "progress": true, + "htmlDialogs":false } diff --git a/lib/client.js b/lib/client.js index 81bbdde7..15e7ccdb 100644 --- a/lib/client.js +++ b/lib/client.js @@ -13,6 +13,7 @@ var Util, DOM, CloudFunc, join; console.log(str); }, + TITLE, Listeners, Files = DOM.Files, Images = DOM.Images, @@ -29,6 +30,10 @@ var Util, DOM, CloudFunc, join; this.HOST = location.origin || location.protocol + '//' + location.host; + this.TITLE = 'Cloud Commander'; + + TITLE = this.TITLE; + log.enable = function() { Debug = true; }; @@ -171,6 +176,17 @@ var Util, DOM, CloudFunc, join; CloudCmd.LIBDIR = prefix + '/lib/'; CloudCmd.LIBDIRCLIENT = prefix + '/lib/client/'; + DOM.Files.get('config', function(error, config) { + var options = { + htmlDialogs: !error && config.htmlDialogs + }; + + if (error) + CloudCmd.log(error); + + DOM.Dialog = new DOM.Dialog(prefix, options); + }); + Util.exec.if(document.body.scrollIntoViewIfNeeded, func, funcBefore); }; @@ -385,13 +401,14 @@ var Util, DOM, CloudFunc, join; CloudCmd.log('reading dir: "' + path + '";'); Files.get('config', function(error, config) { - var dirStorage; + var dirStorage, + Dialog = DOM.Dialog; if (error) - alert(error); + Dialog.alert(TITLE, error); else dirStorage = config.dirStorage; - + if (dirStorage) Storage.get(path, create); else @@ -421,7 +438,7 @@ var Util, DOM, CloudFunc, join; name = Info.name; if (error) { - Dialog.alert(error.responseText); + Dialog.alert(TITLE, error.responseText); } else { childNodes = panel.childNodes; i = childNodes.length; diff --git a/lib/client/buffer.js b/lib/client/buffer.js index 04ca481c..af31342b 100644 --- a/lib/client/buffer.js +++ b/lib/client/buffer.js @@ -21,6 +21,8 @@ COPY = 'copy', CUT = 'cut', + TITLE = 'Buffer', + Buffer = { cut : callIfEnabled.bind(null, cut), copy : callIfEnabled.bind(null, copy), @@ -56,7 +58,7 @@ function isEnabled(callback) { Files.get('config', function(error, config) { if (error) - Dialog.alert(error); + Dialog.alert(TITLE, error); else callback(config.buffer); }); @@ -67,7 +69,7 @@ if (is) callback(); else - Dialog.alert('Buffer disabled in config!'); + Dialog.alert(TITLE, 'Buffer disabled in config!'); }); } @@ -127,13 +129,13 @@ error = 'Buffer is empty!'; if (error) { - DOM.Dialog.alert(error); + Dialog.alert(TITLE, error); } else { data = json.parse(opData); data.to = path; if (data.from === path) { - Dialog.alert(msg); + Dialog.alert(TITLE, msg); } else { Operation.show(opStr, data); clear(); diff --git a/lib/client/cloudcmd.js b/lib/client/cloudcmd.js index 7fa8a855..dc2938bc 100644 --- a/lib/client/cloudcmd.js +++ b/lib/client/cloudcmd.js @@ -20,6 +20,7 @@ var CloudCmd; client + 'notify', client + 'storage', client + 'files', + client + 'dialog', 'client', client + 'buffer', client + 'listeners', @@ -30,11 +31,13 @@ var CloudCmd; }), moduleFiles = [ - 'promise-polyfill/Promise.min', + window.Promise ? '' : 'promise-polyfill/Promise.min', 'format-io/lib/format', 'rendy/lib/rendy', 'emitify/lib/emitify' - ].map(function(name) { + ].filter(function(name) { + return name; + }).map(function(name) { return modules + name; }); diff --git a/lib/client/config.js b/lib/client/config.js index aa980e7d..06b1a621 100644 --- a/lib/client/config.js +++ b/lib/client/config.js @@ -17,8 +17,12 @@ var CloudCmd, Util, DOM, io; showLoad = function() { Images.show.load('top'); }, + Element, Template, + + TITLE = 'Config', + Notify = DOM.Notify, Config = this; @@ -79,7 +83,7 @@ var CloudCmd, Util, DOM, io; }); socket.on('err', function(error) { - Dialog.alert(error); + Dialog.alert(TITLE, error); }); } } @@ -112,7 +116,7 @@ var CloudCmd, Util, DOM, io; focus, obj; if (error) - return Dialog.alert('Could not load config!'); + return Dialog.alert(TITLE, 'Could not load config!'); obj = input.convert(config); @@ -221,7 +225,7 @@ var CloudCmd, Util, DOM, io; }); if (isChecked && !el.localStorage.checked) { - Dialog.alert(msg); + Dialog.alert(TITLE, msg); elements.forEach(function(element) { if (element.checked) { @@ -241,7 +245,7 @@ var CloudCmd, Util, DOM, io; msg = name + ' depends on localStorage'; if (data && !elLocalStorage.checked) { - Dialog.alert(msg); + Dialog.alert(TITLE, msg); elLocalStorage.checked = true; } } diff --git a/lib/client/dialog.js b/lib/client/dialog.js new file mode 100644 index 00000000..77f7a7ad --- /dev/null +++ b/lib/client/dialog.js @@ -0,0 +1,54 @@ +/* global DOM */ +/* global smalltalk */ + +(function(DOM) { + 'use strict'; + + DOM.Dialog = Dialog; + + function Dialog(prefix, config) { + var self = this; + + if (!(this instanceof Dialog)) + return new Dialog(config); + + load(config.htmlDialogs); + + function load(htmlDialogs) { + var names, + name = 'smalltalk', + is = window.Promise, + js = '.min.js', + jsName = is ? js : '.poly' + js, + dir = '/modules/' + name + '/dist/'; + + if (!htmlDialogs) + jsName = '.native' + jsName; + + names = [jsName, '.min.css'].map(function(ext) { + return prefix + dir + name + ext; + }); + + DOM.load.parallel(names, function() {}); + } + + this.alert = function(title, message) { + return smalltalk.alert(title, message, { + cancel: false + }); + }; + + this.prompt = function(title, message, value, options) { + return smalltalk.prompt(title, message, value, options); + }; + + this.confirm = function(title, message, options) { + return smalltalk.confirm(title, message, options); + }; + + this.alert.noFiles = function(title) { + return self.alert(title, 'No files selected!'); + }; + } + +})(DOM); diff --git a/lib/client/dom.js b/lib/client/dom.js index 9f57c278..5e8e4c3b 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -1,4 +1,4 @@ -var CloudCmd, Util, DOM, CloudFunc, Dialog; +var CloudCmd, Util, DOM, CloudFunc; (function(Util, window) { 'use strict'; @@ -8,18 +8,6 @@ var CloudCmd, Util, DOM, CloudFunc, Dialog; var DOMFunc = function() {}, DOMProto, - DialogProto = function() { - var self = this; - - this.alert = alert.bind(window); - this.prompt = prompt.bind(window); - this.confirm = confirm.bind(window); - - this.alert.noFiles = function() { - self.alert('No files selected!'); - }; - }, - ImagesProto = function() { var Images, ImageElementProto = function() { @@ -167,7 +155,7 @@ var CloudCmd, Util, DOM, CloudFunc, Dialog; }, DOMTreeProto = function() { - var DOM = this; + var DOM = this; /** * check class of element @@ -280,7 +268,8 @@ var CloudCmd, Util, DOM, CloudFunc, Dialog; CURRENT_FILE = 'current-file', SELECTED_FILE = 'selected-file', SelectType = '*.*', - Title, + TITLE = 'Cloud Commander', + Title, TabPanel = { 'js-left' : null, 'js-right' : null @@ -468,6 +457,7 @@ var CloudCmd, Util, DOM, CloudFunc, Dialog; function promptNew(typeName, type) { var RESTful = DOM.RESTful, + Dialog = DOM.Dialog, path = '', name = Cmd.getCurrentName(), dir = Cmd.getCurrentDirPath(), @@ -476,20 +466,21 @@ var CloudCmd, Util, DOM, CloudFunc, Dialog; if (name === '..') name = ''; - name = Dialog.prompt(msg, name); - path = dir + name; - - if (type) - path += type; + Dialog.prompt(TITLE, msg, name, {cancel: false}).then(function(name) { + path = dir + name; - if (name) - RESTful.write(path, function(error) { - !error && CloudCmd.refresh(null, function() { - var current = DOM.getCurrentByName(name); - - DOM.setCurrentFile(current); + if (type) + path += type; + + if (name) + RESTful.write(path, function(error) { + !error && CloudCmd.refresh(null, function() { + var current = DOM.getCurrentByName(name); + + DOM.setCurrentFile(current); + }); }); - }); + }); } /** @@ -729,6 +720,7 @@ var CloudCmd, Util, DOM, CloudFunc, Dialog; this.getCurrentData = function(callback, currentFile) { var hash, RESTful = DOM.RESTful, + Dialog = DOM.Dialog, Info = DOM.CurrentInfo, current = currentFile || DOM.getCurrentFile(), path = DOM.getCurrentPath(current), @@ -752,7 +744,7 @@ var CloudCmd, Util, DOM, CloudFunc, Dialog; }; if (Info.name === '..') { - Dialog.alert.noFiles(); + Dialog.alert.noFiles(TITLE); callback(Error('No files selected!')); } else if (isDir) { RESTful.read(path, func); @@ -913,18 +905,16 @@ var CloudCmd, Util, DOM, CloudFunc, Dialog; function selectByPattern(msg, files) { var n, regExp, + Dialog = DOM.Dialog, allMsg = 'Specify file type for ' + msg + ' selection', i = 0, - type, matches = 0, name, shouldSel = msg === 'expand', isSelected, isMatch, current; - type = Dialog.prompt(allMsg, SelectType); - - if (type !== null) { + Dialog.prompt(TITLE, allMsg, SelectType, {cancel: false}).then(function(type) { SelectType = type; regExp = Util.getRegExp(type); @@ -952,8 +942,8 @@ var CloudCmd, Util, DOM, CloudFunc, Dialog; } if (!matches) - Dialog.alert('No matches found!'); - } + Dialog.alert('Select Files', 'No matches found!'); + }); } /** @@ -1334,7 +1324,7 @@ var CloudCmd, Util, DOM, CloudFunc, Dialog; wnd = window.open(url, 'Cloud Commander Auth', options); if (!wnd) - Dialog.alert('Please disable your popup blocker and try again.'); + Dialog.alert(TITLE, 'Please disable your popup blocker and try again.'); return wnd; }; @@ -1411,42 +1401,42 @@ var CloudCmd, Util, DOM, CloudFunc, Dialog; * @currentFile */ this.renameCurrent = function(current) { - var from, to, dirPath, files, isExist, - RESTful = DOM.RESTful; + var from, dirPath, files, isExist, + RESTful = DOM.RESTful, + Dialog = DOM.Dialog; if (!Cmd.isCurrentFile(current)) current = Cmd.getCurrentFile(); from = Cmd.getCurrentName(current); - if (from === '..') { - Dialog.alert.noFiles(); - } else { - to = Dialog.prompt('Rename', from) || from; - isExist = !!DOM.getCurrentByName(to); - - dirPath = Cmd.getCurrentDirPath(); - - if (from !== to) { - files = { - from : dirPath + from, - to : dirPath + to - }; + if (from === '..') + Dialog.alert.noFiles(TITLE); + else + Dialog.prompt(TITLE, 'Rename', from, {cancel: false}).then(function(to) { + isExist = !!DOM.getCurrentByName(to); + dirPath = Cmd.getCurrentDirPath(); - RESTful.mv(files, function(error) { - var Storage = DOM.Storage; + if (from !== to) { + files = { + from : dirPath + from, + to : dirPath + to + }; - if (!error) { - DOM.setCurrentName(to, current); - Cmd.updateCurrentInfo(); - Storage.remove(dirPath); + RESTful.mv(files, function(error) { + var Storage = DOM.Storage; - if (isExist) - CloudCmd.refresh(); - } - }); - } - } + if (!error) { + DOM.setCurrentName(to, current); + Cmd.updateCurrentInfo(); + Storage.remove(dirPath); + + if (isExist) + CloudCmd.refresh(); + } + }); + } + }); }; /** @@ -1523,12 +1513,13 @@ var CloudCmd, Util, DOM, CloudFunc, Dialog; this.goToDirectory = function() { var msg = 'Go to directory:', - path = CurrentInfo.dirPath; + path = CurrentInfo.dirPath, + Dialog = DOM.Dialog; - path = prompt(msg, path); - - path && CloudCmd.loadDir({ - path: path + Dialog.prompt(TITLE, msg, path, {cancel: false}).then(function(path) { + CloudCmd.loadDir({ + path: path + }); }); }, @@ -1627,12 +1618,9 @@ var CloudCmd, Util, DOM, CloudFunc, Dialog; DOMProto = DOMFunc.prototype = new CmdProto(); - Dialog = new DialogProto(); - Util.extend(DOMProto, [ DOMTree, { - Images : Images, - Dialog : Dialog + Images : Images } ]); diff --git a/lib/client/edit.js b/lib/client/edit.js index a8e219ba..c129739f 100644 --- a/lib/client/edit.js +++ b/lib/client/edit.js @@ -9,14 +9,17 @@ var CloudCmd, Util, DOM, CloudFunc, MenuIO, Format, edward; Loading = true, Info = DOM.CurrentInfo, + Dialog = DOM.Dialog, exec = Util.exec, Edit = this, Menu, - Dialog = DOM.Dialog, + Editor = 'edward', + TITLE = 'Edit', + Images = DOM.Images, MSG_CHANGED, Element, @@ -126,7 +129,7 @@ var CloudCmd, Util, DOM, CloudFunc, MenuIO, Format, edward; function getConfig(callback) { DOM.Files.get('config', function(error, config) { if (error) - Dialog.alert(error); + Dialog.alert(TITLE, error); else if (config.editor) Editor = config.editor; @@ -199,7 +202,7 @@ var CloudCmd, Util, DOM, CloudFunc, MenuIO, Format, edward; }; if (error) { - Dialog.alert(error); + Dialog.alert(TITLE, error); } else if (!Menu && MenuIO) { Menu = new MenuIO(Element, options, menuData); Menu.show(position.x, position.y); diff --git a/lib/client/key.js b/lib/client/key.js index 2ebcd672..8a67a108 100644 --- a/lib/client/key.js +++ b/lib/client/key.js @@ -262,10 +262,12 @@ var CloudCmd, Util, DOM; case Key.PLUS: DOM.expandSelection(); + event.preventDefault(); break; case Key.MINUS: DOM.shrinkSelection(); + event.preventDefault(); break; case Key.F1: diff --git a/lib/client/konsole.js b/lib/client/konsole.js index d0342bc2..25db8efd 100644 --- a/lib/client/konsole.js +++ b/lib/client/konsole.js @@ -10,6 +10,8 @@ function ConsoleProto() { var Name = 'Konsole', + TITLE = 'Console', + Element, Loaded, Images = DOM.Images, @@ -79,7 +81,7 @@ DOM.load.js(url, function(error) { if (error) { - Dialog.alert(error.message); + Dialog.alert(TITLE, error.message); } else { Loaded = true; Util.timeEnd(Name + ' load'); diff --git a/lib/client/menu.js b/lib/client/menu.js index 61cbfa24..830b0fca 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -6,13 +6,16 @@ var CloudCmd, Util, DOM, CloudFunc, MenuIO; CloudCmd.Menu = MenuProto; function MenuProto(position) { - var Buffer = DOM.Buffer, - Info = DOM.CurrentInfo, - Loading = true, - Key = CloudCmd.Key, - Events = DOM.Events, - Menu = this, - Images = DOM.Images, + var Buffer = DOM.Buffer, + Info = DOM.CurrentInfo, + Loading = true, + Key = CloudCmd.Key, + Events = DOM.Events, + Dialog = DOM.Dialog, + Images = DOM.Images, + Menu = this, + TITLE = 'Menu', + MenuShowedName, MenuContext, MenuContextFile; @@ -48,7 +51,7 @@ var CloudCmd, Util, DOM, CloudFunc, MenuIO; Util.exec.if(MenuIO, showFunc, function() { DOM.loadMenu(function(error) { if (error) - DOM.Dialog.alert(error); + Dialog.alert(TITLE, error); else showFunc(); }); @@ -107,7 +110,7 @@ var CloudCmd, Util, DOM, CloudFunc, MenuIO; var is = config.auth; if (error) - DOM.alert(error); + DOM.alert(TITLE, error); callback(is); }); @@ -187,10 +190,14 @@ var CloudCmd, Util, DOM, CloudFunc, MenuIO; 'Download' : download, 'Upload To Cloud': curry(uploadTo, 'Cloud'), 'Cut' : function() { - isCurrent(Buffer.cut, Dialog.alert.noFiles); + isCurrent(Buffer.cut, function() { + Dialog.alert.noFiles(TITLE); + }); }, 'Copy' : function() { - isCurrent(Buffer.copy, Dialog.alert.noFiles); + isCurrent(Buffer.copy, function() { + Dialog.alert.noFiles(TITLE); + }); }, }; diff --git a/lib/client/operation.js b/lib/client/operation.js index ebbfcbd0..b9cdcff9 100644 --- a/lib/client/operation.js +++ b/lib/client/operation.js @@ -13,6 +13,7 @@ function OperationProto(operation, data) { var Name = 'Operation', + TITLE = Name, Loaded, RESTful = DOM.RESTful, @@ -41,7 +42,7 @@ Files.get('config', function(error, config) { if (error) - alert(error); + Dialog.alert('Config', error); else if (config.progress) load(create); @@ -144,21 +145,27 @@ }, error: function(error) { - var is; + var msg, + messageBox; wasError = true; - if (done) - Dialog.alert(error); - else - is = Dialog.confirm(error + '\n Continue?'); + if (done) { + msg = error; + messageBox = Dialog.alert; + } else { + msg = error + '\n Continue?'; + messageBox = Dialog.confirm; + } - if (is) + messageBox(TITLE, msg).then(function() { emitter.continue(); - else + }, function() { emitter.abort(); + }); } }, + events = Object.keys(listeners); events.forEach(function(name) { @@ -263,13 +270,12 @@ msg = msgAsk + msgSel + type + name + '?'; } - if (name !== '..') - ret = Dialog.confirm(msg); + if (name === '..') + Dialog.alert.noFiles(TITLE); else - Dialog.alert.noFiles(); - - if (ret) - deleteSilent(files); + Dialog.confirm(TITLE, msg, {cancel: false}).then(function() { + deleteSilent(files); + }); return ret; } @@ -354,19 +360,28 @@ sameName = !!DOM.getCurrentByName(name, panel); if (name === '..') { - Dialog.alert.noFiles(); + Dialog.alert.noFiles(TITLE); } else { if (shouldAsk) - to = message(to, names); - + message(to, names).then(ask); + else + ask(to); + } + + function ask(to) { ok = from !== to && to; - if (ok && shouldAsk && sameName) - ok = Dialog.confirm(rendy(tmpl, { - name: name - })); + if (ok) + if (shouldAsk && sameName) + Dialog.confirm(TITLE, rendy(tmpl, { + name: name + }), {cancel: false}).then(function() { + go(); + }); + else + go(); - if (ok) { + function go() { showLoad(); files = { @@ -415,7 +430,7 @@ Util.check(arguments, ['operation']); if (!names.length) { - Dialog.alert.noFiles(); + Dialog.alert.noFiles(TITLE); } else { switch(operation) { case 'extract': @@ -460,7 +475,7 @@ function message(msg) { return function(to, names) { - var ret, + var promise, n = names.length, name = names[0]; @@ -473,9 +488,9 @@ msg += ' to'; - ret = Dialog.prompt(msg, to); + promise = Dialog.prompt(TITLE, msg, to, {cancel: false}); - return ret; + return promise; }; } @@ -491,7 +506,7 @@ DOM.load.parallel(files, function(error) { if (error) { - Dialog.alert(error.message); + Dialog.alert(TITLE, error.message); } else { Loaded = true; Util.timeEnd(Name + ' load'); diff --git a/lib/client/rest.js b/lib/client/rest.js index 69f9d9c1..69de2faa 100644 --- a/lib/client/rest.js +++ b/lib/client/rest.js @@ -195,7 +195,7 @@ var Util, DOM, CloudFunc, CloudCmd; Images.show.error(text); setTimeout(function() { - DOM.Dialog.alert(text); + DOM.Dialog.alert(CloudCmd.TITLE, text); }, 100); p.callback(Error(text)); diff --git a/man/cloudcmd.1 b/man/cloudcmd.1 index 86c39e88..f16ccdd2 100644 --- a/man/cloudcmd.1 +++ b/man/cloudcmd.1 @@ -34,13 +34,15 @@ programs in browser from any computer, mobile or tablet device. --root set root directory --prefix set url prefix --port set port number + --minify enable minification + --progress show progress of file operations + --html-dialogs use html dialogs --no-auth disable authorization --no-server do not start server --no-online load scripts from local server - --minify enable minification --no-minify disable minification - --progress show progress of file operations --no-progress do not show progress of file operations + --no-html-dialogs do not use html dialogs .SH RESOURCES AND DOCUMENTATION diff --git a/modules/smalltalk/.bower.json b/modules/smalltalk/.bower.json new file mode 100644 index 00000000..30be8129 --- /dev/null +++ b/modules/smalltalk/.bower.json @@ -0,0 +1,41 @@ +{ + "name": "smalltalk", + "version": "1.5.0", + "homepage": "https://github.com/coderaiser/smalltalk", + "authors": [ + "coderaiser " + ], + "description": "Promise-based Alert, Confirm and Prompt replacement", + "main": "dist/smalltalk.min.js", + "moduleType": [ + "globals", + "node" + ], + "keywords": [ + "modal", + "alert", + "prompt", + "confirm" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "modules", + "test", + "tests", + "screen" + ], + "dependencies": {}, + "_release": "1.5.0", + "_resolution": { + "type": "version", + "tag": "v1.5.0", + "commit": "04b5b3474dde87c4bdcb57b1040006050511f79b" + }, + "_source": "git://github.com/coderaiser/smalltalk.git", + "_target": "~1.5.0", + "_originalSource": "smalltalk", + "_direct": true +} \ No newline at end of file diff --git a/modules/smalltalk/ChangeLog b/modules/smalltalk/ChangeLog new file mode 100644 index 00000000..3633c315 --- /dev/null +++ b/modules/smalltalk/ChangeLog @@ -0,0 +1,67 @@ +2015.09.28, v1.5.0 + +feature: +- (smalltalk) add native + + +2015.09.26, v1.4.1 + +feature: +- (bower) rm promise-polyfill + + +2015.09.26, v1.4.0 + +feature: +- (smalltalk) Cancel, OK -> OK, Cancel + + +2015.09.26, v1.3.4 + +fix: +- (smalltalk) keyDown enter: prevent pass next + +feature: +- (package) add jscs +- (package) add jshint support + + +2015.09.24, v1.3.3 + +feature: +- (smalltalk) header: add font-weight + + +2015.09.24, v1.3.2 + +feature: +- (smalltalk) h1 -> header +- (smalltalk) .content-area: add max-width + + +2015.09.23, v1.3.1 + +fix: +- (smalltalk) changeButtonFocus: left, right, up, down do not work with confirm + + +2015.09.23, v1.3.0 + +feature: +- (smalltalk) add handling of keys: left, right, up, down + + +2015.09.23, v1.2.0 + +feature: +- (smalltalk) keyDown: add stopPropogation +- (smalltalk) .smalltalk: rm background-color + + +2015.09.22, v1.1.0 + +feature: +- (package) scripts build: add build-poly +- (smalltalk) add options {cancel} +- (screen) add + diff --git a/modules/smalltalk/LICENSE b/modules/smalltalk/LICENSE new file mode 100644 index 00000000..a103b669 --- /dev/null +++ b/modules/smalltalk/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/modules/smalltalk/README.md b/modules/smalltalk/README.md new file mode 100644 index 00000000..0b5800e7 --- /dev/null +++ b/modules/smalltalk/README.md @@ -0,0 +1,76 @@ +Smalltalk [![License][LicenseIMGURL]][LicenseURL] [![NPM version][NPMIMGURL]][NPMURL] [![Dependency Status][DependencyStatusIMGURL]][DependencyStatusURL] [![Build Status][BuildStatusIMGURL]][BuildStatusURL] +==== + +Simple [Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise)-based replacement of native Alert, Confirm and Prompt. + +# Install +With help of [bower](http://bower.io "Bower"). + +``` +bower install smalltalk +``` + +Or npm: + +``` +npm i smalltalk +``` + +# API + +In every method of `smalltalk` last parameter *options* is optional and could be used +for preventing of handling cancel event. + +```js +{ + cancel: true /* default */ +} +``` + +## smalltalk.alert(title, message [, options]) + +![Alert](https://raw.githubusercontent.com/coderaiser/smalltalk/master/screen/alert.png "Alert") + +```js +smalltalk.alert('Error', 'There was an error!').then(function() { + console.log('ok'); +}, function() { + console.log('cancel'); +}); +``` + +## smalltalk.confirm(title, message [, options]) + +![Confirm](https://raw.githubusercontent.com/coderaiser/smalltalk/master/screen/confirm.png "Confirm") + +```js +smalltalk.confirm('Question', 'Are you sure?').then(function() { + console.log('yes'); +}, function() { + console.log('no'); +}); +``` + +## smalltalk.prompt(title, message, value [, options]) + +![Prompt](https://raw.githubusercontent.com/coderaiser/smalltalk/master/screen/prompt.png "Prompt") + +```js +smalltalk.prompt('Question', 'How old are you?', '10').then(function(value) { + console.log(value); +}, function() { + console.log('cancel'); +}); +``` + +#License +MIT + +[NPMIMGURL]: https://img.shields.io/npm/v/smalltalk.svg?style=flat +[BuildStatusIMGURL]: https://img.shields.io/travis/coderaiser/smalltalk/master.svg?style=flat +[DependencyStatusIMGURL]: https://img.shields.io/gemnasium/coderaiser/smalltalk.svg?style=flat +[LicenseIMGURL]: https://img.shields.io/badge/license-MIT-317BF9.svg?style=flat +[NPMURL]: https://npmjs.org/package/smalltalk "npm" +[BuildStatusURL]: https://travis-ci.org/coderaiser/smalltalk "Build Status" +[DependencyStatusURL]: https://gemnasium.com/coderaiser/smalltalk "Dependency Status" +[LicenseURL]: https://tldrlegal.com/license/mit-license "MIT License" diff --git a/modules/smalltalk/bower.json b/modules/smalltalk/bower.json new file mode 100644 index 00000000..39f7cea7 --- /dev/null +++ b/modules/smalltalk/bower.json @@ -0,0 +1,31 @@ +{ + "name": "smalltalk", + "version": "1.5.0", + "homepage": "https://github.com/coderaiser/smalltalk", + "authors": [ + "coderaiser " + ], + "description": "Promise-based Alert, Confirm and Prompt replacement", + "main": "dist/smalltalk.min.js", + "moduleType": [ + "globals", + "node" + ], + "keywords": [ + "modal", + "alert", + "prompt", + "confirm" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "modules", + "test", + "tests", + "screen" + ], + "dependencies": {} +} diff --git a/modules/smalltalk/css/smalltalk.css b/modules/smalltalk/css/smalltalk.css new file mode 100644 index 00000000..ecc56128 --- /dev/null +++ b/modules/smalltalk/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; + + 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 header { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + color: #333; + font-size: 120%; + font-weight: bold; + margin: 0; + padding: 14px 17px 14px; + text-shadow: white 0 1px 2px; +} + +.smalltalk .page .content-area { + overflow: auto; + padding: 6px 17px 6px; + position: relative; + max-width: 500px; +} + +.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/modules/smalltalk/dist/smalltalk.min.css b/modules/smalltalk/dist/smalltalk.min.css new file mode 100644 index 00000000..b7e98e3c --- /dev/null +++ b/modules/smalltalk/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;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 header{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;color:#333;font-size:120%;font-weight:700;margin:0;padding:14px 17px;text-shadow:#fff 0 1px 2px}.smalltalk .page .content-area{overflow:auto;padding:6px 17px;position:relative;max-width:500px}.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/modules/smalltalk/dist/smalltalk.min.js b/modules/smalltalk/dist/smalltalk.min.js new file mode 100644 index 00000000..3574eb91 --- /dev/null +++ b/modules/smalltalk/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,o,u){var c=document.createElement("div"),s=["cancel","close","ok"],d=void 0,v=void 0,m=new Promise(function(n,t){var e=u&&!u.cancel,r=function(){};d=n,v=t,e&&(v=r)}),p=e(n,t,r,o);return c.innerHTML=p,c.className="smalltalk",document.body.appendChild(c),l(c,["ok"]).forEach(function(n){return n.focus()}),l(c,["input"]).forEach(function(n){return n.setSelectionRange(0,r.length)}),f("click",c,s,function(n){return i(n.target,c,d,v)}),["click","contextmenu"].forEach(function(n){return c.addEventListener(n,function(){return l(c,["ok","input"]).forEach(function(n){return n.focus()})})}),c.addEventListener("keydown",a(c,d,v)),m}function a(n,t,e){return function(r){var a={ENTER:13,ESC:27,TAB:9,LEFT:37,UP:38,RIGHT:39,DOWN:40},f=r.keyCode,s=r.target,d=["ok","cancel","input"],m=l(n,d).map(function(n){return o(n)});switch(f){case a.ENTER:i(s,n,t,e),r.preventDefault();break;case a.ESC:v(),e();break;case a.TAB:r.shiftKey&&c(n,m),c(n,m),r.preventDefault();break;default:var p=["left","right","up","down"].some(function(n){return f===a[n.toUpperCase()]});p&&u(n,m)}r.stopPropagation()}}function o(n){return n.getAttribute("data-name").replace("js-","")}function u(n,t){var e="",r=document.activeElement,a=o(r),u=/ok|cancel/.test(a),c=t.length-1;c&&u&&(e="cancel"===a?"ok":"cancel",l(n,[e]).forEach(function(n){return n.focus()}))}function c(n,t){var e=document.activeElement,r=o(e),a=t.length-1,u=t.indexOf(r);u===a?u=0:a>u&&++u;var c=t[u];l(n,[c]).forEach(function(n){return n.focus()})}function i(n,t,e,r){var a=void 0,o=n.getAttribute("data-name").replace("js-","");/close|cancel/.test(o)?r():(a=l(t,["input"]).reduce(function(n,t){return t.value},null),e(a)),v()}function l(n,t){var e=t.map(function(t){return n.querySelector('[data-name="js-'+t+'"]')}).filter(function(n){return n});return e}function f(n,t,e,r){l(t,e).forEach(function(t){return t.addEventListener(n,function(n){return r(n)})})}function s(n){var t=document.querySelector(n);t.parentElement.removeChild(t)}function d(n){var t=[].slice.call(arguments,1);return n.bind(null,t)}if(!(this instanceof t))return new t(n);var v=d(s,".smalltalk"),m=["OK"],p=["OK","Cancel"];this.alert=function(n,t,e){return r(n,t,"",m,e)},this.prompt=function(n,t,e,a){var o=e||"",u='';return r(n,t,u,p,a)},this.confirm=function(n,t,e){return r(n,t,"",p,e)}}"undefined"!=typeof module&&module.exports?module.exports=t():n.smalltalk=t()}("undefined"!=typeof window&&window); diff --git a/modules/smalltalk/dist/smalltalk.native.min.js b/modules/smalltalk/dist/smalltalk.native.min.js new file mode 100644 index 00000000..6bb18af6 --- /dev/null +++ b/modules/smalltalk/dist/smalltalk.native.min.js @@ -0,0 +1 @@ +!function(n){"use strict";function t(){return this instanceof t?(this.alert=function(n,t){var e=new Promise(function(n){alert(t),n()});return e},this.prompt=function(n,t,e,r){var o=r,i=new Promise(function(n,r){var i=o&&!o.cancel,u=prompt(t,e);null!==u?n(u):i||r()});return i},void(this.confirm=function(n,t,e){var r=e,o=r&&!r.noCancel,i=new Promise(function(n,e){var r=confirm(t);r?n():o||e()});return i})):new t}"undefined"!=typeof module&&module.exports?module.exports=t():n.smalltalk=t()}(this); diff --git a/modules/smalltalk/dist/smalltalk.poly.min.js b/modules/smalltalk/dist/smalltalk.poly.min.js new file mode 100644 index 00000000..d4929644 --- /dev/null +++ b/modules/smalltalk/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,o,u){var c=document.createElement("div"),s=["cancel","close","ok"],d=void 0,v=void 0,m=new Promise(function(n,t){var e=u&&!u.cancel,r=function(){};d=n,v=t,e&&(v=r)}),p=e(n,t,r,o);return c.innerHTML=p,c.className="smalltalk",document.body.appendChild(c),l(c,["ok"]).forEach(function(n){return n.focus()}),l(c,["input"]).forEach(function(n){return n.setSelectionRange(0,r.length)}),f("click",c,s,function(n){return i(n.target,c,d,v)}),["click","contextmenu"].forEach(function(n){return c.addEventListener(n,function(){return l(c,["ok","input"]).forEach(function(n){return n.focus()})})}),c.addEventListener("keydown",a(c,d,v)),m}function a(n,t,e){return function(r){var a={ENTER:13,ESC:27,TAB:9,LEFT:37,UP:38,RIGHT:39,DOWN:40},f=r.keyCode,s=r.target,d=["ok","cancel","input"],m=l(n,d).map(function(n){return o(n)});switch(f){case a.ENTER:i(s,n,t,e),r.preventDefault();break;case a.ESC:v(),e();break;case a.TAB:r.shiftKey&&c(n,m),c(n,m),r.preventDefault();break;default:var p=["left","right","up","down"].some(function(n){return f===a[n.toUpperCase()]});p&&u(n,m)}r.stopPropagation()}}function o(n){return n.getAttribute("data-name").replace("js-","")}function u(n,t){var e="",r=document.activeElement,a=o(r),u=/ok|cancel/.test(a),c=t.length-1;c&&u&&(e="cancel"===a?"ok":"cancel",l(n,[e]).forEach(function(n){return n.focus()}))}function c(n,t){var e=document.activeElement,r=o(e),a=t.length-1,u=t.indexOf(r);u===a?u=0:a>u&&++u;var c=t[u];l(n,[c]).forEach(function(n){return n.focus()})}function i(n,t,e,r){var a=void 0,o=n.getAttribute("data-name").replace("js-","");/close|cancel/.test(o)?r():(a=l(t,["input"]).reduce(function(n,t){return t.value},null),e(a)),v()}function l(n,t){var e=t.map(function(t){return n.querySelector('[data-name="js-'+t+'"]')}).filter(function(n){return n});return e}function f(n,t,e,r){l(t,e).forEach(function(t){return t.addEventListener(n,function(n){return r(n)})})}function s(n){var t=document.querySelector(n);t.parentElement.removeChild(t)}function d(n){var t=[].slice.call(arguments,1);return n.bind(null,t)}if(!(this instanceof t))return new t(n);var v=d(s,".smalltalk"),m=["OK"],p=["OK","Cancel"];this.alert=function(n,t,e){return r(n,t,"",m,e)},this.prompt=function(n,t,e,a){var o=e||"",u='';return r(n,t,u,p,a)},this.confirm=function(n,t,e){return r(n,t,"",p,e)}}"undefined"!=typeof module&&module.exports?module.exports=t():n.smalltalk=t()}("undefined"!=typeof window&&window); diff --git a/modules/smalltalk/example/index.html b/modules/smalltalk/example/index.html new file mode 100644 index 00000000..1bd3e1a4 --- /dev/null +++ b/modules/smalltalk/example/index.html @@ -0,0 +1,15 @@ + + + + + + diff --git a/modules/smalltalk/img/IDR_CLOSE_DIALOG.png b/modules/smalltalk/img/IDR_CLOSE_DIALOG.png new file mode 100644 index 00000000..9e2956d9 Binary files /dev/null and b/modules/smalltalk/img/IDR_CLOSE_DIALOG.png differ diff --git a/modules/smalltalk/img/IDR_CLOSE_DIALOG_H.png b/modules/smalltalk/img/IDR_CLOSE_DIALOG_H.png new file mode 100644 index 00000000..aeafd962 Binary files /dev/null and b/modules/smalltalk/img/IDR_CLOSE_DIALOG_H.png differ diff --git a/modules/smalltalk/lib/smalltalk.js b/modules/smalltalk/lib/smalltalk.js new file mode 100644 index 00000000..9e9482af --- /dev/null +++ b/modules/smalltalk/lib/smalltalk.js @@ -0,0 +1,219 @@ +'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 = ['OK', 'Cancel']; + + this.alert = function (title, msg, options) { + return showDialog(title, msg, '', BUTTON_OK, options); + }; + + this.prompt = function (title, msg, value, options) { + var val = value || ''; + var valueStr = ''; + + return showDialog(title, msg, valueStr, BUTTON_OK_CANCEL, options); + }; + + this.confirm = function (title, msg, options) { + return showDialog(title, msg, '', BUTTON_OK_CANCEL, options); + }; + + 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, options) { + var dialog = document.createElement('div'), + closeButtons = ['cancel', 'close', 'ok'], + ok = undefined, + cancel = undefined, + promise = new Promise(function (resolve, reject) { + var noCancel = options && !options.cancel; + var empty = function empty() {}; + + ok = resolve; + cancel = reject; + + if (noCancel) cancel = empty; + }), + 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', keyDown(dialog, ok, cancel)); + + return promise; + } + + function keyDown(dialog, ok, cancel) { + return function (event) { + var KEY = { + ENTER: 13, + ESC: 27, + TAB: 9, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40 + }; + + var keyCode = event.keyCode, + el = event.target; + + var namesAll = ['ok', 'cancel', 'input'], + names = find(dialog, namesAll).map(function (el) { + return getDataName(el); + }); + + switch (keyCode) { + case KEY.ENTER: + closeDialog(el, dialog, ok, cancel); + event.preventDefault(); + break; + + case KEY.ESC: + remove(); + cancel(); + break; + + case KEY.TAB: + if (event.shiftKey) tab(dialog, names); + + tab(dialog, names); + event.preventDefault(); + break; + + default: + var is = ['left', 'right', 'up', 'down'].some(function (name) { + return keyCode === KEY[name.toUpperCase()]; + }); + + if (is) changeButtonFocus(dialog, names); + + break; + } + + event.stopPropagation(); + }; + } + + function getDataName(el) { + return el.getAttribute('data-name').replace('js-', ''); + } + + function changeButtonFocus(dialog, names) { + var name = '', + active = document.activeElement, + activeName = getDataName(active), + isButton = /ok|cancel/.test(activeName), + count = names.length - 1; + + if (count && isButton) { + if (activeName === 'cancel') name = 'ok';else name = 'cancel'; + + find(dialog, [name]).forEach(function (el) { + return el.focus(); + }); + } + } + + function tab(dialog, names) { + var active = document.activeElement, + activeName = getDataName(active), + 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/modules/smalltalk/lib/smalltalk.native.js b/modules/smalltalk/lib/smalltalk.native.js new file mode 100644 index 00000000..100264b7 --- /dev/null +++ b/modules/smalltalk/lib/smalltalk.native.js @@ -0,0 +1,54 @@ +(function(global) { + 'use strict'; + + if (typeof module !== 'undefined' && module.exports) + module.exports = Smalltalk(); + else + global.smalltalk = Smalltalk(); + + + function Smalltalk() { + if (!(this instanceof Smalltalk)) + return new Smalltalk(); + + this.alert = function(title, message) { + var promise = new Promise(function(resolve) { + alert(message); + resolve(); + }); + + return promise; + }; + + this.prompt = function(title, message, value, options) { + var o = options, + promise = new Promise(function(resolve, reject) { + var noCancel = o && !o.cancel, + result = prompt(message, value); + + if (result !== null) + resolve(result); + else if (!noCancel) + reject(); + }); + + return promise; + }; + + this.confirm = function(title, message, options) { + var o = options, + noCancel = o && !o.noCancel, + promise = new Promise(function(resolve, reject) { + var is = confirm(message); + + if (is) + resolve(); + else if (!noCancel) + reject(); + }); + + return promise; + }; + } + +})(this); diff --git a/modules/smalltalk/package.json b/modules/smalltalk/package.json new file mode 100644 index 00000000..5a34bb97 --- /dev/null +++ b/modules/smalltalk/package.json @@ -0,0 +1,47 @@ +{ + "name": "smalltalk", + "version": "1.5.0", + "description": "Promise-based Alert, Confirm and Prompt replacement", + "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 lib src", + "build-css": "minify css/smalltalk.css > dist/smalltalk.min.css", + "build-js": "npm run 6to5 && minify lib/smalltalk.js > dist/smalltalk.min.js", + "build-js-native": "minify lib/smalltalk.native.js > dist/smalltalk.native.min.js", + "build-poly": "cat node_modules/promise-polyfill/Promise.min.js dist/smalltalk.min.js > dist/smalltalk.poly.min.js", + "build-poly-native": "cat node_modules/promise-polyfill/Promise.min.js dist/smalltalk.native.min.js > dist/smalltalk.native.poly.min.js", + "build-js-full": "npm run build-js && npm run build-poly", + "build-native-full": "npm run build-js-native && npm run build-js-native", + "build": "npm run build-css && npm run build-js-full && npm run build-native-full", + "bower": "bower", + "babel": "babel", + "minify": "minify", + "wisdom": "npm run build", + "test": "jshint src && jscs --esnext src" + }, + "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", + "jscs": "~2.1.1", + "jshint": "~2.8.0", + "minify": "~1.4.20", + "promise-polyfill": "^2.1.0", + "watch": "~0.16.0" + } +} diff --git a/modules/smalltalk/src/smalltalk.js b/modules/smalltalk/src/smalltalk.js new file mode 100644 index 00000000..a46b8702 --- /dev/null +++ b/modules/smalltalk/src/smalltalk.js @@ -0,0 +1,257 @@ +(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 = ['OK', 'Cancel']; + + this.alert = (title, msg, options) => { + return showDialog(title, msg, '', BUTTON_OK, options); + }; + + this.prompt = (title, msg, value, options) => { + let val = value || ''; + let valueStr = ``; + + return showDialog(title, msg, valueStr, BUTTON_OK_CANCEL, options); + }; + + this.confirm = (title, msg, options) => { + return showDialog(title, msg, '', BUTTON_OK_CANCEL, options); + }; + + 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, options) { + let dialog = document.createElement('div'), + + closeButtons = [ + 'cancel', + 'close', + 'ok' + ], + + ok, cancel, + + promise = new Promise((resolve, reject) => { + let noCancel = options && !options.cancel; + let empty = () => {}; + + ok = resolve; + cancel = reject; + + if (noCancel) + cancel = empty; + }), + + 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', keyDown(dialog, ok, cancel)); + + return promise; + } + + function keyDown(dialog, ok, cancel) { + return event => { + const KEY = { + ENTER : 13, + ESC : 27, + TAB : 9, + LEFT : 37, + UP : 38, + RIGHT : 39, + DOWN : 40 + }; + + let keyCode = event.keyCode, + el = event.target; + + let namesAll = ['ok', 'cancel', 'input'], + names = find(dialog, namesAll).map(el => + getDataName(el) + ); + + switch(keyCode) { + case KEY.ENTER: + closeDialog(el, dialog, ok, cancel); + event.preventDefault(); + break; + + case KEY.ESC: + remove(); + cancel(); + break; + + case KEY.TAB: + if (event.shiftKey) + tab(dialog, names); + + tab(dialog, names); + event.preventDefault(); + break; + + default: + let is = ['left', 'right', 'up', 'down'].some(name => + keyCode === KEY[name.toUpperCase()] + ); + + if (is) + changeButtonFocus(dialog, names); + + break; + } + + event.stopPropagation(); + }; + } + + function getDataName(el) { + return el + .getAttribute('data-name') + .replace('js-', ''); + } + + function changeButtonFocus(dialog, names) { + let name = '', + active = document.activeElement, + activeName = getDataName(active), + isButton = /ok|cancel/.test(activeName), + count = names.length - 1; + + if (count && isButton) { + if (activeName === 'cancel') + name = 'ok'; + else + name = 'cancel'; + + find(dialog, [name]).forEach(el => + el.focus() + ); + } + } + + function tab(dialog, names) { + let active = document.activeElement, + activeName = getDataName(active), + + 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); diff --git a/tmpl/config.hbs b/tmpl/config.hbs index 281279b3..ae37f02c 100644 --- a/tmpl/config.hbs +++ b/tmpl/config.hbs @@ -127,4 +127,10 @@ Progress +
  • + +