feature(cloudcmd) add --html-dialogs

This commit is contained in:
coderaiser 2015-09-28 11:43:03 -04:00
parent d9c67255a3
commit a7933b755c
37 changed files with 1271 additions and 141 deletions

View file

@ -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 */
}
```

View file

@ -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');

View file

@ -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"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Before After
Before After

View file

@ -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"
}

View file

@ -18,5 +18,6 @@
"ip": null,
"root": "/",
"prefix": "",
"progress": true
"progress": true,
"htmlDialogs":false
}

View file

@ -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;

View file

@ -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();

View file

@ -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;
});

View file

@ -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;
}
}

54
lib/client/dialog.js Normal file
View file

@ -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);

View file

@ -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
}
]);

View file

@ -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);

View file

@ -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:

View file

@ -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');

View file

@ -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);
});
},
};

View file

@ -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');

View file

@ -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));

View file

@ -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

View file

@ -0,0 +1,41 @@
{
"name": "smalltalk",
"version": "1.5.0",
"homepage": "https://github.com/coderaiser/smalltalk",
"authors": [
"coderaiser <mnemonic.enemy@gmail.com>"
],
"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
}

View file

@ -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

21
modules/smalltalk/LICENSE Normal file
View file

@ -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.

View file

@ -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"

View file

@ -0,0 +1,31 @@
{
"name": "smalltalk",
"version": "1.5.0",
"homepage": "https://github.com/coderaiser/smalltalk",
"authors": [
"coderaiser <mnemonic.enemy@gmail.com>"
],
"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": {}
}

View file

@ -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;
}

View file

@ -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}

View file

@ -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'<div class="page">\n <div data-name="js-close" class="close-button"></div>\n <header>'+n+'</header>\n <div class="content-area">\n '+t+"\n "+e+'\n </div>\n <div class="action-area">\n <div class="button-strip"> '+r.map(function(n,t){return"<button tabindex="+t+' data-name="js-'+n.toLowerCase()+'">'+n+"</button>"}).join("")+"\n </div>\n </div>\n </div>"}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='<input type="text" value="'+o+'" data-name="js-input">';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);

View file

@ -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);

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,15 @@
<!doctype html>
<link rel="stylesheet" href="../css/smalltalk.css">
<meta content="width=device-width,initial-scale=1" name="viewport">
<script src="../src/smalltalk.js"></script>
<script>
window.addEventListener('DOMContentLoaded', function() {
smalltalk.prompt('hello', 'world', '2+2').then(function(value) {
console.log(value);
}, function() {
console.log('close');
})
});
</script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

View file

@ -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 = '<input type="text" value="' + val + '" data-name="js-input">';
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 '<div class="page">\n <div data-name="js-close" class="close-button"></div>\n <header>' + title + '</header>\n <div class="content-area">\n ' + msg + '\n ' + value + '\n </div>\n <div class="action-area">\n <div class="button-strip"> ' + buttons.map(function (name, i) {
return '<button tabindex=' + i + ' data-name="js-' + name.toLowerCase() + '">' + name + '</button>';
}).join('') + '\n </div>\n </div>\n </div>';
}
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);

View file

@ -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);

View file

@ -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 <mnemonic.enemy@gmail.com> (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"
}
}

View file

@ -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 = `<input type="text" value="${ val }" data-name="js-input">`;
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 `<div class="page">
<div data-name="js-close" class="close-button"></div>
<header>${ title }</header>
<div class="content-area">
${ msg }
${ value }
</div>
<div class="action-area">
<div class="button-strip"> ${
buttons.map((name, i) =>
`<button tabindex=${ i } data-name="js-${ name.toLowerCase() }">${ name }</button>`
).join('')
}
</div>
</div>
</div>`;
}
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);

View file

@ -127,4 +127,10 @@
Progress
</label>
</li>
<li>
<label>
<input data-name="js-htmlDialogs" type="checkbox" {{ htmlDialogs }}>
HTML Dialogs
</label>
</li>
</ul>