Browse Source

+ modularization

dev
RubaXa 11 years ago
parent
commit
d6291e21d8
  1. 1
      .gitignore
  2. 49
      Gruntfile.js
  3. 464
      Ply.min.js
  4. 74
      src/Context.es6
  5. 1054
      src/Ply.es6
  6. 48
      src/css.es6
  7. 28
      src/defaults.es6
  8. 312
      src/dom.es6
  9. 191
      src/effects.es6
  10. 137
      src/effects.preset.es6
  11. 5
      src/jquery.es6
  12. 7
      src/keys.es6
  13. 5
      src/lang.es6
  14. 73
      src/stack.es6
  15. 25
      src/support.es6
  16. 44
      src/ui.es6
  17. 133
      src/utils.es6
  18. 2
      tests/Ply.ui.tests.js
  19. 1
      tests/index.html

1
.gitignore vendored

@ -3,4 +3,5 @@ report
.DS_Store .DS_Store
node_modules node_modules
Ply.js Ply.js
Ply.es6
Ply.ui.js Ply.ui.js

49
Gruntfile.js

@ -6,19 +6,20 @@ module.exports = function (grunt){
es6transpiler: { es6transpiler: {
core: { core: {
src: 'src/Ply.es6', src: 'Ply.es6',
dest: 'Ply.js' dest: 'Ply.js'
},
ui: {
src: 'src/Ply.ui.es6',
dest: 'Ply.ui.js'
} }
}, },
export: {
src: "src/Ply.es6",
dst: "Ply.es6"
},
watch: { watch: {
scripts: { scripts: {
files: 'src/*.es6', files: 'src/*.es6',
tasks: ['es6transpiler'], tasks: ['es'],
options: { interrupt: true } options: { interrupt: true }
} }
}, },
@ -46,20 +47,52 @@ module.exports = function (grunt){
}, },
dist: { dist: {
files: { files: {
'<%= pkg.exportName %>.min.js': ['Ply.js', 'Ply.ui.js'] '<%= pkg.exportName %>.min.js': ['Ply.js']
} }
} }
} }
}); });
grunt.registerTask('export', 'Export es6 to js', function () {
function file(rel, name) {
return rel.split('/').slice(0, -1).concat(name).join('/') + '.es6';
}
function parse(src, pad) {
grunt.log.writeln((pad || '') + 'Parse file:', src);
return grunt.file.read(src)
.replace(/module\.exports\s*=\s*([\s\S]+);/, '$1')
.replace(/require\('(.*?)'\);?/g, function (_, name) {
return parse(file(src, name), ' ');
})
.replace(/\/+\s+&import\s+"(.*?)".*?\n/g, function (_, name) {
return parse(file(src, name), ' ');
})
.trim()
;
}
var config = grunt.config(this.name);
var content = parse(config.src).replace(/;;;[^\n]+/g, '');
grunt.log.writeln(new Array(50).join('-'));
grunt.log.oklns('Write file:', config.dst);
grunt.file.write(config.dst, content);
});
grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-qunit-istanbul'); grunt.loadNpmTasks('grunt-qunit-istanbul');
grunt.loadNpmTasks('grunt-es6-transpiler'); grunt.loadNpmTasks('grunt-es6-transpiler');
grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('es', ['es6transpiler']); grunt.registerTask('es', ['export', 'es6transpiler']);
grunt.registerTask('build', ['es', 'qunit']); grunt.registerTask('build', ['es', 'qunit']);
grunt.registerTask('min', ['uglify']); grunt.registerTask('min', ['uglify']);
grunt.registerTask('default', ['build', 'min']); grunt.registerTask('default', ['build', 'min']);

464
Ply.min.js vendored

File diff suppressed because one or more lines are too long

74
src/Context.es6

@ -0,0 +1,74 @@
//
// Ply-контекст
//
/**
* @class Ply.Context
* @param {HTMLElement} el
*/
function Context(el) {
/**
* Корневой элемент
* @type {HTMLElement}
*/
this.el = el;
}
Context.fn = Context.prototype = /** @lends Ply.Context */{
constructor: Context,
/**
* Получить элемент по имени
* @param {String} name
* @returns {HTMLElement|undefined}
*/
getEl: function (name) {
if (this.el) {
return _querySelector('[' + _plyAttr + '-name="' + name + '"]', this.el);
}
},
/**
* Получить или установить значение по имени
* @param {String} name
* @param {String} [value]
* @returns {String}
*/
val: function (name, value) {
var el = typeof name === 'string' ? this.getEl(name) : name;
if (el && (el.value == null)) {
el = _getElementsByTagName(el, 'input')[0]
|| _getElementsByTagName(el, 'textarea')[0]
|| _getElementsByTagName(el, 'select')[0]
;
}
if (el && value != null) {
el.value = value;
}
return el && el.value || "";
},
/**
* Получить JSON
* @returns {Object}
*/
toJSON: function () {
var items = this.el.querySelectorAll('[' + _plyAttr + '-name]'),
json = {},
el,
i = items.length
;
while (i--) {
el = items[i];
json[el.getAttribute(_plyAttr + '-name')] = this.val(el);
}
return json;
}
};

1054
src/Ply.es6

File diff suppressed because it is too large Load Diff

48
src/css.es6

@ -0,0 +1,48 @@
/*global support, document*/
/**
* Хуки для css
* @type {Object}
*/
var _cssHooks = {
opacity: !support.opacity && function (style, value) {
style.zoom = 1;
style.filter = 'alpha(opacity=' + (value * 100) + ')';
}
};
/**
* Установка или получение css свойства
* @param {HTMLElement} el
* @param {Object|String} prop
* @param {String} [val]
* @returns {*}
* @private
*/
function _css(el, prop, val) {
if (el && el.style && prop) {
if (prop instanceof Object) {
for (var name in prop) {
_css(el, name, prop[name]);
}
}
else if (val === void 0) {
/* istanbul ignore else */
if (document.defaultView && document.defaultView.getComputedStyle) {
val = document.defaultView.getComputedStyle(el, '');
}
else if (el.currentStyle) {
val = el.currentStyle;
}
return prop === void 0 ? val : val[prop];
} else if (_cssHooks[prop]) {
_cssHooks[prop](el.style, val);
} else {
el.style[support[prop] || prop] = val;
}
}
}

28
src/defaults.es6

@ -0,0 +1,28 @@
module.exports = {
zIndex: 10000,
layer: {}, // css
overlay: {
opacity: .6,
backgroundColor: 'rgb(0, 0, 0)'
},
flags: {
closeBtn: true,
bodyScroll: false,
closeByEsc: true,
closeByOverlay: true,
hideLayerInStack: true,
visibleOverlayInStack: false
},
baseHtml: true,
// Callback's
init: noop,
open: noop,
close: noop,
destroy: noop,
callback: noop
};

312
src/dom.es6

@ -0,0 +1,312 @@
//
// Работа с DOM
//
var $ = require('jquery');
/**
* Разбор строки "tag#id.foo.bar"
* @const {RegExp}
*/
var R_SELECTOR = /^(\w+)?(#\w+)?((?:\.[\w_-]+)*)/i;
/**
* Выбрать элементы по заданному селектору
* @param {String} selector
* @param {HTMLElement} [ctx]
* @returns {HTMLElement}
*/
function _querySelector(selector, ctx) {
try {
return (ctx || document).querySelector(selector);
} catch (err) {
/* istanbul ignore next */
return $(selector, ctx)[0];
}
}
/**
* Найти элементы по имени
* @param {HTMLElement} el
* @param {String} name
* @returns {NodeList}
*/
function _getElementsByTagName(el, name) {
return el.getElementsByTagName(name);
}
/**
* Присоединить элемент
* @param {HTMLElement} parent
* @param {HTMLElement} el
* @private
*/
function _appendChild(parent, el) {
try {
parent && el && parent.appendChild(el);
} catch (e) {}
}
/**
* Удалить элемент
* @param {HTMLElement} el
* @private
*/
function _removeElement(el) {
/* istanbul ignore else */
if (el && el.parentNode) {
el.parentNode.removeChild(el);
}
}
/**
* Добавить слуашетеля
* @param {HTMLElement} el
* @param {String} name
* @param {Function} fn
* @private
*/
function _addEvent(el, name, fn) {
var handle = fn.handle = fn.handle || ((evt) => {
/* istanbul ignore if */
if (!evt.target) {
evt.target = evt.srcElement || document;
}
/* istanbul ignore if */
if (evt.target.nodeType === 3) {
evt.target = evt.target.parentNode;
}
/* istanbul ignore if */
if (!evt.preventDefault) {
evt.preventDefault = () => {
evt.returnValue = false;
};
}
/* istanbul ignore if */
if (!evt.stopPropagation) {
evt.stopPropagation = () => {
evt.cancelBubble = true;
};
}
fn.call(el, evt);
});
/* istanbul ignore else */
if (el.addEventListener) {
el.addEventListener(name, handle, false);
} else {
el.attachEvent('on' + name, handle);
}
}
/**
* Удалить слуашетеля
* @param {HTMLElement} el
* @param {String} name
* @param {Function} fn
* @private
*/
function _removeEvent(el, name, fn) {
var handle = fn.handle;
if (handle) {
/* istanbul ignore else */
if (el.removeEventListener) {
el.removeEventListener(name, handle, false);
} else {
el.detachEvent('on' + name, handle);
}
}
}
/**
* Создание DOM структуры по спецификации
* @param {String|Object|HTMLElement} [spec]
* @returns {HTMLElement}
* @private
*/
function _buildDOM(spec) {
if (spec == null) {
spec = 'div';
}
if (spec.appendChild) {
return spec;
}
else if (spec.skip) {
return document.createDocumentFragment();
}
if (typeof spec === 'string') { // selector
spec = { tag: spec };
}
var el,
children = spec.children,
selector = R_SELECTOR.exec(spec.tag || '')
;
// Это нам больше не нужно
delete spec.children;
// Разбираем селектор
spec.tag = selector[1] || 'div';
spec.id = spec.id || (selector[2] || '').substr(1);
// Собираем className
selector = (selector[3] || '').split('.');
selector[0] = (spec.className || '');
spec.className = selector.join(' ');
// Создаем элемент, теперь можно
el = document.createElement(spec.tag);
delete spec.tag;
// Определяем свойсва
_each(spec, (value, name) => {
if (value) {
if (name === 'css') {
// Определяем CSS свойства
_css(el, spec.css);
}
else if (name === 'text') {
(value != null) && _appendChild(el, document.createTextNode(value));
}
else if (name === 'html') {
(value != null) && (el.innerHTML = value);
}
else if (name === 'ply') {
// Ply-аттрибут
el.setAttribute(_plyAttr, value);
}
else if (name in el) {
try {
el[name] = value;
} catch (e) {
el.setAttribute(name, value);
}
}
else if (/^data-/.test(name)) {
el.setAttribute(name, value);
}
}
});
// Детишки
if (children && children.appendChild) {
_appendChild(el, children);
}
else {
_each(children, (spec, selector) => {
if (spec) {
if (typeof spec === 'string') {
spec = { text: spec };
}
else if (typeof spec !== 'object') {
spec = {};
}
/* istanbul ignore else */
if (typeof selector === 'string') {
spec.tag = spec.tag || selector;
}
_appendChild(el, _buildDOM(spec));
}
});
}
return el;
}
/**
* Выбрать первый не заполненый элемент
* @param {HTMLElement} parentNode
* @private
*/
function _autoFocus(parentNode) {
var items = _getElementsByTagName(parentNode, 'input'),
i = 0,
n = items.length,
el,
element
;
for (; i < n; i++) {
el = items[i];
/* istanbul ignore else */
if (el.type === 'submit') {
!element && (element = el);
}
else if (!/hidden|check|radio/.test(el.type) && el.value == '') {
element = el;
break;
}
}
if (!element) {
element = _getElementsByTagName(parentNode, 'button')[0];
}
try { element.focus(); } catch (err) { }
}
/**
* Предзагрузить все изображения
* @param {HTMLElement} parentNode
* @returns {Promise}
* @private
*/
function _preloadImage(parentNode) {
_loading(true);
return _promise((resolve) => {
var items = _getElementsByTagName(parentNode, 'img'),
i = items.length,
queue = i,
img,
complete = () => {
/* istanbul ignore else */
if (--queue <= 0) {
i = items.length;
while (i--) {
img = items[i];
_removeEvent(img, 'load', complete);
_removeEvent(img, 'error', complete);
}
_loading(false);
resolve();
}
}
;
while (i--) {
img = items[i];
if (img.complete) {
queue--;
} else {
_addEvent(img, 'load', complete);
_addEvent(img, 'error', complete);
}
}
!queue && complete();
});
}

191
src/effects.es6

@ -0,0 +1,191 @@
module.exports = {
// Установки по умолчанию
defaults: {
duration: 300,
open: {
layer: null,
overlay: null
},
close: {
layer: null,
overlay: null
}
},
/**
* Настройти эффекты по умолчанию
* @static
* @param {Object} options
*/
setup: function (options) {
this.defaults = this.get(options);
},
set: function (desc) {
_extend(this, desc);
},
/**
* Получить опции на основе переданных и по умолчанию
* @static
* @param {Object} options опции
* @returns {Object}
*/
get: function (options) {
var defaults = _deepClone(this.defaults);
// Функция разбора выражения `name:duration[args]`
function parseKey(key) {
var match = /^([\w_-]+)(?::(\d+%?))?(\[[^\]]+\])?/.exec(key) || [];
return {
name: match[1] || key,
duration: match[2] || null,
args: JSON.parse(match[3] || 'null') || {}
};
}
function toObj(obj, key, def) {
var fx = parseKey(key),
val = (obj[fx.name] || def || {}),
duration = (fx.duration || val.duration || obj.duration || options.duration)
;
if (typeof val === 'string') {
val = parseKey(val);
delete val.args;
}
if (/%/.test(val.duration)) {
val.duration = parseInt(val.duration, 10) / 100 * duration;
}
val.duration = (val.duration || duration) | 0;
return val;
}
if (typeof options === 'string') {
var fx = parseKey(options);
options = _deepClone(this[fx.name] || { open: {}, close: {} });
options.duration = fx.duration || options.duration;
options.open.args = fx.args[0];
options.close.args = fx.args[1];
}
else if (options instanceof Array) {
var openFx = parseKey(options[0]),
closeFx = parseKey(options[1]),
open = this[openFx.name],
close = this[closeFx.name]
;
options = {
open: _deepClone(open && open.open || { layer: options[0], overlay: options[0] }),
close: _deepClone(close && close.close || { layer: options[1], overlay: options[1] })
};
options.open.args = openFx.args[0];
options.close.args = closeFx.args[0];
}
else if (!(options instanceof Object)) {
options = {};
}
options.duration = (options.duration || defaults.duration) | 0;
for (var key in {open: 0, close: 0}) {
var val = options[key] || defaults[key] || {};
if (typeof val === 'string') {
// если это строка, то только layer
val = { layer: val };
}
val.layer = toObj(val, 'layer', defaults[key].layer);
val.overlay = toObj(val, 'overlay', defaults[key].overlay);
if(val.args === void 0){
// clean
delete val.args;
}
options[key] = val;
}
return options;
},
/**
* Применить эффекты
* @static
* @param {HTMLElement} el элемент, к которому нужно применить эффект
* @param {String} name название эффекта
* @returns {Promise|undefined}
*/
apply: function (el, name) {
name = name.split('.');
var effects = this[name[0]], // эффекты open/close
firstEl = el.firstChild,
oldStyle = [el.getAttribute('style'), firstEl && firstEl.getAttribute('style')],
fx,
effect
;
if (support.transition && effects && (effect = effects[name[1]]) && (fx = Ply.effects[effect.name])) {
if (fx['to'] || fx['from']) {
// Клонируем
fx = _deepClone(fx);
// Выключаем анимацию
_css(el, 'transition', 'none');
_css(firstEl, 'transition', 'none');
// Определяем текущее css-значения
_each(fx['to'], (val, key, target) => {
if (val === '&') {
target[key] = _css(el, key);
}
});
// Выставляем initied значения
if (isFn(fx['from'])) {
fx['from'](el, effects.args);
} else if (fx['from']) {
_css(el, fx['from']);
}
return _promise((resolve) => {
// Принудительный repaint/reflow
fx.width = el.offsetWidth;
// Включаем анимацию
_css(el, 'transition', 'all ' + effect.duration + 'ms');
_css(firstEl, 'transition', 'all ' + effect.duration + 'ms');
// Изменяем css
if (isFn(fx['to'])) {
fx['to'](el, effects.args);
}
else {
_css(el, fx['to']);
}
// Ждем завершения анимации
setTimeout(resolve, effect.duration);
}).then(() => {
el.setAttribute('style', oldStyle[0]);
firstEl && firstEl.setAttribute('style', oldStyle[1]);
});
}
}
return _resolvePromise();
}
};

137
src/effects.preset.es6

@ -0,0 +1,137 @@
module.exports = {
//
// Комбинированный эффекты
//
'fade': {
open: { layer: 'fade-in:80%', overlay: 'fade-in:100%' },
close: { layer: 'fade-out:60%', overlay: 'fade-out:60%' }
},
'scale': {
open: { layer: 'scale-in', overlay: 'fade-in' },
close: { layer: 'scale-out', overlay: 'fade-out' }
},
'fall': {
open: { layer: 'fall-in', overlay: 'fade-in' },
close: { layer: 'fall-out', overlay: 'fade-out' }
},
'slide': {
open: { layer: 'slide-in', overlay: 'fade-in' },
close: { layer: 'slide-out', overlay: 'fade-out' }
},
'3d-flip': {
open: { layer: '3d-flip-in', overlay: 'fade-in' },
close: { layer: '3d-flip-out', overlay: 'fade-out' }
},
'3d-sign': {
open: { layer: '3d-sign-in', overlay: 'fade-in' },
close: { layer: '3d-sign-out', overlay: 'fade-out' }
},
'inner': {
open: { layer: 'inner-in' },
close: { layer: 'inner-out' }
},
//
// Описание эффекта
//
'fade-in': {
'from': { opacity: 0 },
'to': { opacity: '&' }
},
'fade-out': {
'to': { opacity: 0 }
},
'slide-in': {
'from': { opacity: 0, transform: 'translateY(20%)' },
'to': { opacity: '&', transform: 'translateY(0)' }
},
'slide-out': {
'to': { opacity: 0, transform: 'translateY(20%)' }
},
'fall-in': {
'from': { opacity: 0, transform: 'scale(1.3)' },
'to': { opacity: '&', transform: 'scale(1)' }
},
'fall-out': {
'to': { opacity: 0, transform: 'scale(1.3)' }
},
'scale-in': {
'from': { opacity: 0, transform: 'scale(0.7)' },
'to': { opacity: '&', transform: 'scale(1)' }
},
'scale-out': {
'to': { opacity: 0, 'transform': 'scale(0.7)' }
},
'rotate3d': (el, opacity, axis, deg, origin) => {
_css(el, { perspective: '1300px' });
_css(el.firstChild, {
opacity: opacity,
transform: 'rotate' + axis + '(' + deg + 'deg)',
transformStyle: 'preserve-3d',
transformOrigin: origin ? '50% 0' : '50%'
});
},
'3d-sign-in': {
'from': (el) => {
Ply.effects.rotate3d(el, 0, 'X', -60, '50% 0');
},
'to': (el) => {
_css(el.firstChild, { opacity: 1, transform: 'rotateX(0)' });
}
},
'3d-sign-out': {
'from': (el) => {
Ply.effects.rotate3d(el, 1, 'X', 0, '50% 0');
},
'to': (el) => {
_css(el.firstChild, { opacity: 0, transform: 'rotateX(-60deg)' });
}
},
'3d-flip-in': {
'from': (el, args) => {
Ply.effects.rotate3d(el, 0, 'Y', args || -70);
},
'to': (el) => {
_css(el.firstChild, { opacity: 1, transform: 'rotateY(0)' });
}
},
'3d-flip-out': {
'from': (el) => {
Ply.effects.rotate3d(el, 1, 'Y', 0);
},
'to': (el, args) => {
_css(el.firstChild, { opacity: 0, transform: 'rotateY(' + (args || 70) + 'deg)' });
}
},
'inner-in': {
'from': (el) => { _css(el, 'transform', 'translateX(100%)'); },
'to': (el) => { _css(el, 'transform', 'translateX(0%)'); }
},
'inner-out': {
'from': (el) => { _css(el, 'transform', 'translateX(0%)'); },
'to': (el) => { _css(el, 'transform', 'translateX(-100%)'); }
}
};

5
src/jquery.es6

@ -0,0 +1,5 @@
module.exports = window.jQuery
|| /* istanbul ignore next */ window.Zepto
|| /* istanbul ignore next */ window.ender
|| /* istanbul ignore next */ window.$
;

7
src/keys.es6

@ -0,0 +1,7 @@
/**
* Коды кнопок
* @type {Object}
*/
module.exports = {
esc: 27
};

5
src/lang.es6

@ -0,0 +1,5 @@
module.exports = {
ok: 'OK',
cancel: 'Cancel',
cross: '✖'
};

73
src/stack.es6

@ -0,0 +1,73 @@
var array_core = [],
array_push = array_core.push,
array_splice = array_core.splice
;
Ply.stack = {
_idx: {},
/**
* Последний Ply в стеке
* @type {Ply}
*/
last: null,
/**
* Длинна стека
* @type {Number}
*/
length: 0,
/**
* Удаить последний ply-слой из стека
* @param {Event} evt
* @private
*/
_pop: function (evt) {
var layer = Ply.stack.last;
if (evt.keyCode === keys.esc && layer.hasFlag('closeByEsc')) {
layer.closeBy('esc');
}
},
/**
* Добавить ply в стек
* @param {Ply} layer
*/
add: function (layer) {
var idx = array_push.call(this, layer);
this.last = layer;
this._idx[layer.cid] = idx - 1;
if (idx === 1) {
_addEvent(document, 'keyup', this._pop);
}
},
/**
* Удалить ply из стека
* @param {Ply} layer
*/
remove: function (layer) {
var idx = this._idx[layer.cid];
if (idx >= 0) {
array_splice.call(this, idx, 1);
delete this._idx[layer.cid];
this.last = this[this.length-1];
if (!this.last) {
_removeEvent(document, 'keyup', this._pop);
}
}
}
};

25
src/support.es6

@ -0,0 +1,25 @@
/*global _buildDOM*/
module.exports = (() => {
var props = {},
style = document.createElement('div').style,
names = 'opacity transition transform perspective transformStyle transformOrigin backfaceVisibility'.split(' '),
prefix = ['Webkit', 'Moz', 'O', 'MS']
;
_each(names, (name, i) => {
props[name] = (name in style) && /* istanbul ignore next */ name;
/* istanbul ignore else */
if (!props[name]) {
for (i = 0; i < 4; i++) {
var pname = prefix[i] + name.charAt(0).toUpperCase() + name.substr(1);
/* istanbul ignore else */
if (props[name] = (pname in style) && pname) {
break;
}
}
}
});
return props;
})();

44
src/Ply.ui.es6 → src/ui.es6

@ -1,35 +1,23 @@
/*global define, Ply */ /*global define, Ply */
((factory) => { ;;;(Ply => {
factory(Ply);
})((Ply) => {
'use strict'; 'use strict';
var _plyAttr = Ply.attrName, function _toBlock(block, name) {
noop = Ply.noop, if (block == null) {
_each = Ply.each, return { skip: true };
_extend = Ply.extend, }
_promise = Ply.promise,
_buildDOM = Ply.dom.build,
_appendChild = Ply.dom.append,
_lang = Ply.lang,
_toBlock = (block, name) => {
if (block == null) {
return { skip: true };
}
if (typeof block === 'string') {
block = { text: block };
}
if (typeof block === 'object') { if (typeof block === 'string') {
block.name = block.name || name; block = { text: block };
} }
return block; if (typeof block === 'object') {
block.name = block.name || name;
} }
;
return block;
}
@ -143,7 +131,7 @@
return { return {
ply: ':ok', ply: ':ok',
tag: 'button.ply-ctrl.ply-ok', tag: 'button.ply-ctrl.ply-ok',
text: data === true ? _lang.ok : data text: data === true ? lang.ok : data
}; };
}); });
@ -154,7 +142,7 @@
ply: ':close', ply: ':close',
tag: 'button.ply-ctrl.ply-cancel', tag: 'button.ply-ctrl.ply-cancel',
type: 'reset', type: 'reset',
text: data === true ? _lang.cancel : data text: data === true ? lang.cancel : data
}; };
}); });
@ -417,4 +405,4 @@
// Export // Export
Ply.ui = ui; Ply.ui = ui;
Ply.factory = factory; Ply.factory = factory;
}); ;;;})();

133
src/utils.es6

@ -0,0 +1,133 @@
//
// Вспомогательные методы
//
var Promise = Deferred || window.Promise;
/**
* Функция?
* @param {*} fn
* @returns {Boolean}
*/
function isFn(fn) {
return typeof fn === 'function';
}
/**
* Создать «Обещание»
* @param {Function} executor
* @returns {Promise}
* @private
*/
function _promise(executor) {
/* istanbul ignore if */
if (Promise) {
return new Promise(executor);
} else {
var dfd = $.Deferred();
executor(dfd.resolve, dfd.reject);
return dfd;
}
}
/**
* Дождаться разрешения всех «Обещаний»
* @param {Promise[]} iterable
* @returns {Promise}
* @private
*/
function _promiseAll(iterable) {
return Promise
? /* istanbul ignore next */ Promise.all(iterable)
: $.when.apply($, iterable);
}
/**
* Вернуть разрешенное «Обещание»
* @param {*} [value]
* @returns {Promise}
* @private
*/
function _resolvePromise(value) {
return _promise((resolve) => resolve(value));
}
/**
* Привести значение к «Обещанию»
* @param {*} value
* @returns {Promise}
* @private
*/
function _cast(value) {
return value && value.then ? value : _resolvePromise(value);
}
/**
* Object iterator
* @param {Object|Array} obj
* @param {Function} iterator
* @private
*/
function _each(obj, iterator) {
if (obj) {
for (var key in obj) {
/* istanbul ignore else */
if (obj.hasOwnProperty(key)) {
iterator(obj[key], key, obj);
}
}
}
}
/**
* Глубокое клонирование
* @param {*} obj
* @returns {*}
* @private
*/
function _deepClone(obj) {
var result = {};
_each(obj, (val, key) => {
if (isFn(val)) {
result[key] = val;
}
else if (val instanceof Object) {
result[key] = _deepClone(val);
}
else {
result[key] = val;
}
});
return result;
}
/**
* Перенос свойств одного объект к другому
* @param {Object} dst
* @param {...Object} src
* @returns {Object}
* @private
*/
function _extend(dst, ...src) {
var i = 0, n = src.length;
for (; i < n; i++) {
_each(src[i], (val, key) => {
dst[key] = val;
});
}
return dst;
}

2
tests/Ply.ui.tests.js

@ -101,7 +101,7 @@
setTimeout(function () { setTimeout(function () {
var el = ui.layer.layerEl; var el = ui.layer.layerEl;
(el.getElementsByTagName('button')[1] || el.getElementsByTagName('button')[0]).click(); (el.getElementsByTagName('button')[1] || el.getElementsByTagName('button')[0]).click();
}, 50); }, 100);
} }
}).then(function () { }).then(function () {
equal(log.join('\n'), [ equal(log.join('\n'), [

1
tests/index.html

@ -126,7 +126,6 @@
<!-- lib:src --> <!-- lib:src -->
<script src="../Ply.js"></script> <script src="../Ply.js"></script>
<script src="../Ply.ui.js"></script>
<script>!window.Ply && document.write('<script src="../Ply.min.js"><' + '/script>');</script> <script>!window.Ply && document.write('<script src="../Ply.min.js"><' + '/script>');</script>
<!-- lib:tests --> <!-- lib:tests -->

Loading…
Cancel
Save