mirror of https://github.com/RubaXa/Ply.git
RubaXa
11 years ago
19 changed files with 1588 additions and 1065 deletions
File diff suppressed because one or more lines are too long
@ -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; |
||||||
|
} |
||||||
|
}; |
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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 |
||||||
|
}; |
@ -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(); |
||||||
|
}); |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
}; |
@ -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%)'); } |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,5 @@ |
|||||||
|
module.exports = window.jQuery |
||||||
|
|| /* istanbul ignore next */ window.Zepto |
||||||
|
|| /* istanbul ignore next */ window.ender |
||||||
|
|| /* istanbul ignore next */ window.$ |
||||||
|
; |
@ -0,0 +1,7 @@ |
|||||||
|
/** |
||||||
|
* Коды кнопок |
||||||
|
* @type {Object} |
||||||
|
*/ |
||||||
|
module.exports = { |
||||||
|
esc: 27 |
||||||
|
}; |
@ -0,0 +1,5 @@ |
|||||||
|
module.exports = { |
||||||
|
ok: 'OK', |
||||||
|
cancel: 'Cancel', |
||||||
|
cross: '✖' |
||||||
|
}; |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
@ -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; |
||||||
|
})(); |
@ -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; |
||||||
|
} |
Loading…
Reference in new issue