sbis3-node-ws
Version:
Модуль позволяет использовать ядро интерфейсного фреймворка sbis3-ws(`core.js`) и модуль работы с данными (`Source.js`) в приложении на nodejs.
1,336 lines (1,263 loc) • 355 kB
JavaScript
/**
* @fileoverview
* @author elifantievon
*/
var global = (function(){ return this || (0,eval)('this'); }());
(function() {
if ($ws.single.Storage.isReady('Source')) {
return;
}
var EVENTS = ['onNewRecord', 'onParseData', 'onSettingsParsed', 'onComplete', 'onWayExists', 'onResults'];
/**
* Асинхронная загрузка XML-документа с шаблоном.
* Для каждого файла непосредственно загрузка будет выполняться только один раз
*
* @param {String} templateName имя шаблона или путь (если начинается с . или /)
* @param {Boolean} [fast]
* @returns {$ws.proto.Deferred}
*/
$ws.core.loadTemplateFile = function(templateName, fast){
/**
* Функция, запрашивающая шаблон.
* @param {String} file конкретное абсолютное имя файла шаблона
* @return {$ws.proto.Deferred}
*/
function requestTemplateFile(file){
//проверяем загружался ли хоть 1 из пакетов содержащий нужный шаблон, если нет - берем последний
if (!fast && file instanceof Array){
for (var i = 0, l = file.length; i < l; i++){
if ($ws.single.Storage.isStored('resfile://' + file[i]) || i == l - 1){
file = file[i];
break;
}
}
}
var rid = $ && $.cookie && $.cookie('rightshash');
return $ws.single.Storage.store('resfile://' + file, function(dResult) {
dResult.dependOn($ws.single.ioc.resolve('ITransport', {
dataType: fast ? 'text' : 'xml',
url: (function() {
// При запросе XML допишем к версии идентификатор прав (передаваемый в cookie)
var res = $ws.core.urlWithHost(file + (fast ? '.fast.xml' : '.xml'));
if (rid) {
res = res.replace(/(\.fast)?(\.v[0-9a-f]+)?(\.xml)$/, '$1.r' + rid + '$2$3');
}
return res;
})()
}).execute(null));
});
}
// Если ресурс пакетирован - грузим пакет и ищем там
if($ws._const.xmlPackages[templateName] && !fast){
return new $ws.proto.Deferred().dependOn(requestTemplateFile($ws._const.xmlPackages[templateName]))
.addCallback(function(res){
var root = res.documentElement;
for(var j = root.childNodes.length - 1; j >= 0; --j){
if(root.childNodes[j].nodeType != $ws._const.nodeType.ELEMENT_NODE) {
continue;
}
if(root.childNodes[j].getAttribute('id') == templateName) {
return root.childNodes[j];
}
}
return new Error(templateName + ' is not found in the package ' + $ws._const.xmlPackages[templateName]);
});
}
// Иначе работаем по старой схеме
var
firstChar = templateName.charAt(0),
base = '';
// Если первый символ . или / - значит это путь. Грузим его непосредственно. В противном случае
// это или ресурс движка или ресурс по оглавлению
if(firstChar !== '/' && firstChar !== '.') {
// Проверим есть ли в оглавлении
if(templateName in $ws._const.xmlContents) {
templateName = $ws._const.xmlContents[templateName];
firstChar = templateName.charAt(0);
if (firstChar !== '/'){
// Если у нас не абсолютный путь
base = $ws._const.resourceRoot;
}
} else {
// если нет - грузим из ресурсов движка
base = $ws._const.wsRoot + 'res/xml/';
}
}
return requestTemplateFile(base + templateName);
};
/**
* Асинхронная загрузка инстанса класса $ws.proto.Template
* Для каждого не первого вызова будет отдаваться ранее созданный инстанс
*
* @param {String} templateName имя шаблона
* @param {Object} [options]
* @param {Boolean} [options.fast=false]
* @param {String} [options.html='']
* @returns {$ws.proto.Deferred}
*/
$ws.core.attachTemplate = function(templateName, options){
var
res,
block = BOOMR.plugins.WS.startBlock('$ws.core.attachTemplate:' + templateName),
fast = false,
html = '';
if (options && typeof options == 'object') {
fast = options.fast || false;
html = options.html || '';
} else {
fast = arguments[1];
html = arguments[2];
}
block.openSyncBlock();
res = $ws.single.Storage.store('res://' + templateName, function(dResult){
function createTpl (type, cont, tplName){
if (typeof $ws.proto[type] == 'function') {
var templateInstance = new $ws.proto[type]({
templateXML : cont,
template: cont,
templateName: tplName
});
templateInstance.getRenderResult().addCallback(function() {
dResult.callback(templateInstance);
}).addErrback(function(e) {
dResult.errback(new Error('$ws.core.attachTemplate : ' + e.message));
});
}
else {
dResult.errback(new Error('$ws.core.attachTemplate : $ws.proto.' + type + ' is not implemented'));
}
}
if (/js!/.test(templateName)){
require([templateName], function(constructor){
createTpl('CompoundControlTemplate', constructor, templateName);
}, function(e){
dResult.errback(e);
});
}
else if (fast && html) {
createTpl('FastTemplate', html, templateName);
} else if (templateName) {
$ws.core.loadTemplateFile(templateName, fast).addCallbacks(function(content){
createTpl(fast ? 'FastTemplate' : 'XMLTemplate', content, templateName);
$ws.helpers.addXmlLog(templateName);
return content;
}, function(error){
dResult.errback(error);
return error;
});
} else {
createTpl('EmptyTemplate', '', '');
}
}).addBoth(function(res){
block.close();
return res;
});
block.closeSyncBlock();
return res;
};
$ws._const.templateParseConfig = {
'#text': true,
'control': 'div',
'option' : false,
'style' : false,
'configuration' : false
};
$ws._const.allowAttribute = {
'config': false
};
$ws._const.nodeType = {
'ELEMENT_NODE': 1,
'ATTRIBUTE_NODE': 2,
'TEXT_NODE': 3,
'CDATA_SECTION_NODE': 4,
'ENTITY_REFERENCE_NODE': 5,
'ENTITY_NODE': 6,
'PROCESSING_INSTRUCTION_NODE': 7,
'COMMENT_NODE': 8,
'DOCUMENT_NODE': 9,
'DOCUMENT_TYPE_NODE': 10,
'DOCUMENT_FRAGMENT_NODE': 11,
'NOTATION_NODE': 12
};
/**
* @class $ws.proto.Template
* @public
*/
$ws.proto.Template = $ws.core.extend({}, /** @lends $ws.proto.Template.prototype */{
/**
* @cfg {String} Название шаблона, оно же путь к нему
* @name $ws.proto.Template#templateName
*/
$protected: {
_templateName: '',
_dReady: null,
_loadedHandlers: {},
_configuration:{
autoWidth:false,
autoHeight:false,
isRelativeTemplate:false,
minWidth:0
}
},
$constructor: function(cfg){
this._templateName = cfg.templateName;
this._dReady = new $ws.proto.ParallelDeferred();
this._dReady.getResult().
addCallback(this._attachResources.bind(this)).
addCallback(this._loadDependencies.bind(this));
},
isPage: function() {
throw new Error("Template::isPage - method is not implemented!");
},
_collectAllControlsToPreload: function(source) {
throw new Error("Template::_collectAllControlsToPreload - method is not implemented!");
},
_loadDependencies: function() {
var
controlConfigs = this._collectAllControlsToPreload(this.getControls(undefined)),
configsPaths = $ws.helpers.map(controlConfigs, function(cfg) {
return [cfg.type, cfg];
}, []);
return $ws.core.loadControlsDependencies(configsPaths, true);
},
/**
* Возвращает результат загрузки всех функций используемых в шаблоне
* @return {$ws.proto.Deferred} результат загрузки
*/
getRenderResult : function(){
return this._dReady.getResult();
},
/**
* Возвращает стиль окна, заданный при проектировании
*/
getStyle: function() {
throw new Error("Template::getStyle - method is not implemented!");
},
/**
* @returns {Object} Объект с параметрами width и height
*/
getDimensions: function(){
throw new Error("Template::getDimensions - method is not implemented!");
},
/**
* @returns {Object} объект { h: String, w: String } с параметрами выравнивания ws-area
*/
getAlignment: function(){
throw new Error("Template::getAlignment - method is not implemented!");
},
/**
* @returns {String} заголовок окна
*/
getTitle : function(){
throw new Error("Template::getTitle - method is not implemented!");
},
/**
* @return {object} конфиг окна прописанный в шаблоне
*/
getConfig : function(node){
throw new Error("Template::getConfig - method is not implemented!");
},
/**
* @returns {Object} Хэш-мэп событий, и подписантов на них. Подписанты передаются в виде массива
*/
getDeclaredHandlers: function() {
return this._loadedHandlers;
},
createMarkup: function(container) {
throw new Error("Template::createMarkup - method is not implemented!");
},
/**
* @param {String} parentId
* @param {jQuery} templateRoot корневой элемент, в который встроен текущий шаблон
* @returns {Object} параметры и типы контролов, присутствующих в шаблоне
*/
getControls: function(parentId, templateRoot){
throw new Error("Template::getControls - method is not implemented!");
},
getName : function(){
return this._templateName;
},
_getIncludeDescriptorNodes: function() {
throw new Error("Template::_getIncludeDescriptorNodes - method is not implemented!");
},
_attachResources: function() {
var include = this._getIncludeDescriptorNodes(),
pdResult = new $ws.proto.ParallelDeferred();
if (include.length > 0) {
// Не может быть несколько инклюдов... Всегда берем первый
var spec = include[0].getAttribute('spec');
if(spec) {
try {
spec = JSON.parse(spec);
} catch(e) {
spec = false;
}
if(spec) {
if (spec.js) {
pdResult.push($ws.core.attachSequentally(spec.js));
}
if (spec.css) {
pdResult.push($ws.core.attachSequentally(spec.css));
}
}
}
}
return pdResult.done().getResult();
},
_mergeAttrToConfig: function(config, attrName, attrValue) {
if (attrName !== 'title' && attrName !== 'width' && attrName !== 'height'){
config[attrName] = attrValue === 'false' ? false : (attrValue === 'true' ? true : attrValue);
}
},
needSetZindexByOrder: function() {
return false;
}
});
/**
* @class $ws.proto.EmptyTemplate Класс - заглушка, представляющий собой пустой шаблон.
* Его может быть удобно подставить вместо настоящего шаблона.
* @extends $ws.proto.Template
* @public
*/
$ws.proto.EmptyTemplate = $ws.proto.Template.extend({
$constructor: function() { this._dReady.done(); },
_collectAllControlsToPreload: function() { return []; },
isPage: function() { return true; },
getConfig: function() { return {}; },
getStyle: function() { return ''; },
getAlignment: function() { return { horizontalAlignment: 'Stretch', verticalAlignment: 'Stretch' }; },
getTitle: function() { return ''; },
createMarkup: function(container) { container.get(0).innerHTML = ''; },
getDimensions: function() { return { width: '', height: ''}; },
getControls: function() { return []; },
_getIncludeDescriptorNodes: function() { return []; }
});
/**
* @class $ws.proto.XMLTemplate
* @extends $ws.proto.Template
* @public
*/
$ws.proto.XMLTemplate = $ws.proto.Template.extend(/** @lends $ws.proto.Template.prototype */{
/**
* @cfg {Document} XML-документ шаблона
* @name $ws.proto.XMLTemplate#templateXML
* @see templateName
*/
/**
* @cfg {String} Название шаблона, оно же путь к нему
* @name $ws.proto.XMLTemplate#templateName
* @see templateXML
*/
$protected: {
_xml : null,
_toText : [],
_html: '',
_dReady: null,
_innerTemplates: [],
_templateName: '',
_document : undefined,
_controlTagCache: ''
},
$constructor: function(cfg){
var self = this;
this._xml = cfg.templateXML;
this._document = this._xml.documentElement ? this._xml.documentElement : this._xml;
this._innerTemplates[this._templateName = cfg.templateName] = true;
this._includeInnerTemplate(new $ws.proto.Deferred()).addCallbacks(function(){
self._assignId();
self._loadEventHandlers();
}, function(e){
$ws.single.ioc.resolve('ILogger').log("Template", "Building failed. Reason: " + e.message);
return e;
});
var tag = this._xml.getElementsByTagName('style')[0];
if(tag){
var style = this._findCDATA(tag, true);
if(style){
var cssReady = $ws.helpers.insertCss(style, true, this.getName());
if (cssReady) {
this._dReady.push(cssReady);
}
}
}
},
_getIncludeDescriptorNodes: function() {
var collection;
if (this._xml.nodeType == $ws._const.nodeType.DOCUMENT_NODE) {
collection = this._xml.firstChild.childNodes;
} else {
collection = this._xml.childNodes;
}
return $ws.helpers.filter(collection, function(child){
return child.nodeName.toUpperCase() == 'I';
});
},
isPage: function() {
return this._document.getAttribute('isApplication') === 'false';
},
_collectAllControlsToPreload: function(source) {
return this._getControlsByFilter(function() { return true; });
},
_extractControlConfiguration: function(controlNode) {
var configObject, cfg = controlNode.getAttribute('config');
if(cfg) {
try {
configObject = eval("("+cfg+")");
} catch(e) {
configObject = {};
}
} else
configObject = this._parseConfiguration(controlNode.getElementsByTagName('configuration')[0]);
return configObject;
},
/**
* @param node где нужно искать
* @param needValue что нужно вернуть - значение или узел
* @return {String} styleData значение тега style
*/
_findCDATA: function(node, needValue){
var childNode,
styleData;
for(var i = 0, l = node.childNodes.length; i < l; i++){
childNode = node.childNodes[i];
if(childNode.nodeType === $ws._const.nodeType['CDATA_SECTION_NODE']){
styleData = needValue ? childNode.nodeValue : childNode;
break;
}
}
return styleData;
},
/**
* Ищет все теги include и инстанцирует их в нужные места
* @param {$ws.proto.Deferred} dResult
* @returns {$ws.proto.Deferred}
*/
_includeInnerTemplate : function(dResult){
var self = this,
includeNodes = this._xml.getElementsByTagName('include'),
dP = new $ws.proto.ParallelDeferred(),
includeNode,
parent, includeName,
innerTemplates = [],
styles = [];
if(includeNodes.length === 0)
dResult.callback();
else {
for (var i = 0, l = includeNodes.length; i < l; i++){
includeNode = includeNodes[i];
parent = includeNode.parentNode;
if((includeName = includeNodes[i].getAttribute('name')) !== null){
if(this._innerTemplates[includeName])
return dResult.errback("Cyclic dependency detected");
else{
innerTemplates.push(includeName);
styles[i] = "";
(function(parent, includeNode, i) {
dP.push($ws.core.loadTemplateFile(includeName).addCallback(function(xmlDoc){
var templateChildren = (xmlDoc.documentElement ? xmlDoc.documentElement.childNodes : xmlDoc.childNodes),
tag = xmlDoc.getElementsByTagName('style')[0],
newNode;
if(tag){
styles[i] = self._findCDATA(tag, true);
(xmlDoc.documentElement ? xmlDoc.documentElement : xmlDoc).removeChild(tag);
}
for(var j = 0, cnt = templateChildren.length; j < cnt; j++){
newNode = templateChildren[0];
if(self._xml.adoptNode)
newNode = self._xml.adoptNode(newNode);
parent.insertBefore(newNode, includeNode);
}
parent.removeChild(includeNode);
return xmlDoc;
}));
})(parent, includeNode, i);
}
}
else{
$ws.helpers.forEach(includeNode.childNodes, function(val){
if(val.getAttribute){
var source = val.getAttribute('source');
if(source)
dP.push($ws.core.attach(source));
}
});
}
}
dP.done().getResult().addCallback(function(){
var styleNode = self._xml.getElementsByTagName('style')[0];
for(var i = 0, l = innerTemplates.length; i< l; i++)
self._innerTemplates[innerTemplates[i]] = true;
if(styleNode)
self._findCDATA(styleNode, false).nodeValue += styles.join('');
else{
styleNode = self._xml.createElement('style');
styleNode.appendChild(self._xml.createCDATASection(styles.join('')));
self._document.appendChild(styleNode);
}
self._includeInnerTemplate(dResult);
});
}
return dResult;
},
/**
* Проставляет id контролам у которых его нету.
*/
_assignId : function(){
var controls = this._getControlTags(this._xml);
for (var i = 0, l = controls.length; i < l; i++){
var control = controls[i];
if (!control.getAttribute('id')){
control.setAttribute('id', $ws.helpers.randomId());
}
}
},
_getControlTags: function(where) {
var result;
if(this._controlTagCache)
return this._controlTagCache;
if(where.querySelectorAll) {
var qsa = where.querySelectorAll('div[wsControl="true"]');
if(qsa)
result = qsa;
}
if(!result) {
result = [];
var collection = where.getElementsByTagName('div');
for(var i = 0, l = collection.length; i < l; i++) {
var item = collection[i];
if(item.getAttribute('wsControl'))
result.push(item);
}
}
return (this._controlTagCache = result);
},
/**
* Это оптимизация.
* Используем более быстрый querySelectorAll если возможно
* @returns {Array}
*/
_getFunctionOptions: function(){
var res;
if(this._xml.querySelectorAll) {
res = this._xml.querySelectorAll('option[type="function"]');
if(res)
return res;
}
var functions = this._xml.getElementsByTagName('option');
res = [];
for (var i = 0; i < functions.length; i++){
if (functions[i].getAttribute('type') === 'function')
res.push(functions[i]);
}
return res;
},
/**
* Инициирует загрузку всех функций используемых в шаблоне
*/
_loadEventHandlers : function(){
var functions = this._getFunctionOptions();
for (var i = 0; i < functions.length; i++){
var name = functions[i].getAttribute('name'),
value = functions[i].getAttribute('value');
if (value !== null && value.length > 0)
this._dReady.push($ws.core.getHandler(value));
}
this._dReady.push(this._processTemplateHandlers()).done(this);
},
/**
* Ставит в очередь на загрузку все хэндлеры диалогов, найденные в содержании
* @returns {$ws.proto.Deferred}
*/
_processTemplateHandlers: function() {
var
dMainHandlers = new $ws.proto.ParallelDeferred(),
self = this;
var attrs = this._document.attributes, events = {};
for(var i = 0, l = attrs.length; i < l; i++) {
var
anAttr = attrs[i],
attrName = anAttr.nodeName;
if(attrName.substring(0, 2) == 'on' && anAttr.nodeValue !== '') { // it is event handler
events[attrName] = anAttr.nodeValue.split('|');
}
}
if(!Object.isEmpty(events)) {
/**
* Здесь мы начинаем загрузку всех хандлеров, определенных на самом окне.
* Здесь, а не ранее, потому что они будут иметь более высокий приоритет,
* но не должны нарушить ранее заданный порядок.
*
* В колбэке основной цепочки стартуем ParallelDeferred,
* зависящий от загрузки всех хандлеров на окне
*/
for(var eventName in events) {
if(events.hasOwnProperty(eventName)) {
(function(eN, hSpec) {
self._loadedHandlers[eN] = [];
for(var i = 0; i < hSpec.length; ++i) {
(function(i) {
dMainHandlers.push($ws.core.getHandler(hSpec[i]).addCallbacks(function(f){
self._loadedHandlers[eN][i] = f;
return f;
}, function(e){
$ws.single.ioc.resolve('ILogger').error(
"Template",
"Error while loading handler " + eN + ": " + e.message,
e);
return e;
}));
}(i));
}
})(eventName, events[eventName]);
}
}
}
return dMainHandlers.done().getResult();
},
/**
* Возвращает стиль окна, заданный при проектировании
*/
getStyle: function() {
return this._document.getAttribute('windowStyle') || "";
},
/**
* @returns {Object} Объект с параметрами width и height
*/
getDimensions: function(){
var
width = this._document.getAttribute('width'),
height = this._document.getAttribute('height');
return {
width: !width? '': width.toLowerCase() === 'auto' ? 'auto' : width.indexOf('%') >= 0 ? width : parseInt(width, 10) + 'px',
height: !height? '': height.toLowerCase() === 'auto' ? 'auto' : height.indexOf('%') >= 0 ? height : parseInt(height, 10) + 'px'
};
},
/**
* @returns {Object} объект { h: String, w: String } с параметрами выравнивания ws-area
*/
getAlignment: function(){
var
h = this._document.getAttribute('HorizontalAlignment'),
v = this._document.getAttribute('VerticalAlignment');
return {
horizontalAlignment: !h ? 'Stretch' : h,
verticalAlignment: !v ? 'Stretch' : v
};
},
/**
* @returns {String} заголовок окна
*/
getTitle : function(){
return this._document.getAttribute('title');
},
/**
* @return {object} конфиг окна прописанный в шаблоне
*/
getConfig : function(node){
var cfg = this._configuration,
tag = node || this._document;
if (tag.attributes !== null){
for (var i = 0, l = tag.attributes.length; i < l; i++){
var att = tag.attributes[i];
this._mergeAttrToConfig(cfg, att.nodeName, att.nodeValue);
}
}
return cfg;
},
/**
* @returns {Object} Хэш-мэп событий, и подписантов на них. Подписанты передаются в виде массива
*/
getDeclaredHandlers: function() {
return this._loadedHandlers;
},
createMarkup: function(container) {
var block = BOOMR.plugins.WS.startBlock("mkup:" + this.getName());
var markup;
if(this._html === '') {
// TODO зачем здесь так?
var markupNode = (this._xml.nodeName == 'ws-template' ? this._xml : this._xml.getElementsByTagName('ws-template')[0]);
var ownerDoc = container.get(0).ownerDocument;
var fragment = ownerDoc.createElement('div');
var col = markupNode ? markupNode.childNodes : [];
var item, i = 0, l;
while(col.length && i < col.length) {
item = col.item(i);
if(item.nodeType == $ws._const.nodeType.ELEMENT_NODE && item.nodeName != 'style') {
item = $ws.proto.Template._importNode(ownerDoc, item, true);
i++;
fragment.appendChild(item);
} else
i++;
}
var configs = fragment.getElementsByTagName('configuration');
for(i = 0, l = configs.length; i < l; i++) {
configs[0].parentNode.removeChild(configs[0]);
}
this._html = markup = fragment.innerHTML.replace(/<\/ ?br>/ig,"").replace(/&/ig, "&");
} else
markup = this._html;
container.html(markup);
block.close();
},
_getControlsByFilter: function(filter) {
var controls = this._getControlTags(this._xml);
var result = [];
for(var i = 0, l = controls.length; i < l; i++) {
var control = controls[i];
if(filter(control)) {
var cfg = this._extractControlConfiguration(control);
cfg.type = control.getAttribute('type');
cfg.id = control.getAttribute('id');
result.push(cfg);
}
}
return result;
},
/**
* @param {String} parentId
* @returns {Array} параметры и типы контролов, присутствующих в шаблоне
*/
getControls: function(parentId){
var block = BOOMR.plugins.WS.startBlock('ctrls:' + this.getName()),
result = this._getControlsByFilter(function(control) {
return parentId == control.getAttribute('parentId');
});
block.close();
return result;
},
/**
* Обрабатывает конфигурацию элементов управления
*
* @param {Node} configRoot Текщий узел разобра
* @param {Boolean} [makeArray] обрабатывается ли в настоящий момент массив?
* @returns {Object} Конфигурация контрола
*/
_parseConfiguration: function(configRoot, makeArray){
var
name, value, type,
// Это место переписано так не случайно. От старого вариант почему-то ВНЕЗАПНО ломался каверидж
retvalFnc = function(){
var self = this;
self.mass = makeArray ? [] : {};
self.push = function(name, value){
if (makeArray)
self.mass.push(value);
else if (name !== null)
self.mass[name] = value;
}
},
retval = new retvalFnc();
if (configRoot && configRoot.childNodes){
var children = configRoot.childNodes;
for (var i = 0, l = children.length; i < l; i++){
var child = children[i];
if (child.nodeName && child.nodeName == 'option'){
name = child.getAttribute('name');
type = child.getAttribute('type');
value = child.getAttribute('value');
//if (type === 'array' || name === null || value === null){
if (type === 'array' || (value === null && type != 'cdata')){
//Если не в листе дерева, то разбираем дальше рекурсивно
if (value === null)
value = this._parseConfiguration(child, type === 'array');
retval.push(name, value);
}
//добрались до листа дерева
else //if (!makeArray){
switch (type){
case 'cdata':
retval.push(name, this._findCDATA(child, true));
break;
case 'boolean':
retval.push(name, value === "true");
break;
case 'function':
if (typeof(value) === 'string' && value.length > 0){
var hdl = $ws.core.getHandler(value);
if(hdl.isSuccessful()) {
(function(name){
hdl.addCallback(function(handler){
if(typeof(handler) == 'function') {
retval.push(name, handler);
return handler;
}
else
throw new Error("Integrity error! Some serious problems in $ws.core.getHandler()!");
// XXX: Potentially uncaught error condition
});
})(name);
}
else
throw new Error(value + " function is not ready or don't exist");
}
break;
case 'dialog':
if (typeof(value) === 'string' && value.length > 0) {
(function(value){
retval.push(name, function() {
$ws.core.attachInstance("Control/Area:Dialog", {
template: value,
opener: this,
context: (new $ws.proto.Context().setPrevious(this.getLinkedContext()))
})
})
})(value);
}
break;
case 'floatArea':
if (typeof(value) === 'string' && value.length > 0) {
(function(value){
retval.push(name, function() {
var topParentContainer = this.getTopParent().getContainer();
this.setEnabled(false);//Отключаем кнопку, чтобы юзер 100500 раз панельку не вызывал
$ws.core.attachInstance("Control/Area:FloatArea", {
id:this.getId(),
opener: this,
name: this.getName()+'-floatArea',
template: value,
target: topParentContainer,//$('body'),
side : 'right',
autHide : true,
showDelay : 300,
animationLength : 300,
offset: {
x: 0,
y: (window.scrollY > topParentContainer.offset().top) ? window.scrollY : 0
},
handlers: {
'onAfterClose' : function(){
this.getOpener().setEnabled(true);//включаем обратно
}
}
});
});
})(value);
}
break;
case 'command':
if (typeof(value) === 'string' && value.length > 0) {
(function(value){
retval.push(name, function(event) {
event.setResult($ws.single.CommandDispatcher.sendCommand.apply($ws.single.CommandDispatcher, [this, value].concat(Array.prototype.slice.call(arguments, 1))));
});
})(value);
}
break;
case 'page':
case 'newpage':
if(typeof(value) === 'string' && value.length > 0) {
(function(value, type){
retval.push(name, function() {
var link = typeof this.getOpenLink === 'function' ? this.getOpenLink() || value : value;
if(type == 'page')
window.location = link;
else
window.open(link);
});
})(value, type);
}
break;
case 'menu':
if(typeof(value) === 'string' && value.length > 0) {
(function(value){
retval.push(name, function() {
if($ws.helpers.instanceOfModule(this, 'SBIS3.CORE.Button')){
this._options.menu = value;
this.unsubscribe('onActivated', arguments.callee);
this._initMenu();
this._notify('onActivated');
}
});
})(value);
}
break;
case null:
default :
if(value === "null"){
value = null;
}
if(value === "Infinity"){
value = Infinity;
}
retval.push(name, value);
break;
}
//}
}
}
}
return retval.mass;
},
needSetZindexByOrder: function() {
return true;
}
});
$ws.proto.FastTemplate = $ws.proto.Template.extend({
$protected: {
_options: {
template: ''
},
_dom: null,
_rootConfig: {},
_controlsConfig: []
},
$constructor: function() {
var self = this, preloadIndex = {};
var block = BOOMR.plugins.WS.startBlock("FastTemplate:ctor");
var block2 = BOOMR.plugins.WS.startBlock("FastTemplate:ctor:configuration:"+this.getName());
this._dom = document.createElement('div');
this._dom.innerHTML = this._options.template;
if(!this._dom.firstChild)
throw new Error("Wrong template (" + this.getName() + ") came from server");
var firstChild = this._dom.firstChild;
this._rootConfig = JSON.parse(firstChild.getAttribute('templateConfig'));
this._controlsConfig = JSON.parse(firstChild.getAttribute('config'));
firstChild.removeAttribute('templateConfig');
firstChild.removeAttribute('config');
block2.close();
var block3 = BOOMR.plugins.WS.startBlock("FastTemplate:ctor:func_preload:"+this.getName());
$ws.helpers.forEach(this._controlsConfig.functions, function(val){
var path = '["' + val.split('/').join('"]["') + '"]';
var acessor = new Function("s", "val", "return arguments.length == 1 ? s" + path + " : (s" + path + " = val);");
var configRoot = self._controlsConfig;
var fSpec = acessor(configRoot).split('#');
switch(fSpec[0]) {
case 'function':
self._dReady.push($ws.core.getHandler(fSpec[1]).addCallback(function(f){
acessor(configRoot, f);
}));
break;
case 'moduleFunc':
var
m = fSpec[1].split("/"),
fName = m.length > 1 ? m.pop() : '',
mName = m.join('/');
self._dReady.push($ws.require("js!" + mName).addCallback(function(mod){
var fn = mod[0][fName];
fn.wsHandlerPath = 'js!' + mName + (fName ? ':' + fName : '');
acessor(configRoot, fn);
}));
break;
case 'floatArea':
acessor(configRoot, function(event) {
var topParentContainer = this.getTopParent().getContainer();
this.setEnabled(false);//Отключаем кнопку, чтобы юзер 100500 раз панельку не вызывал
$ws.core.attachInstance("Control/Area:FloatArea", {
opener: this,
template: fSpec[1],
target: topParentContainer,//$('body'),
side : 'right',
autHide : true,
animationLength : 300,
offset: {
x: 0,
y: (window.scrollY > topParentContainer.offset().top) ? window.scrollY : 0
},
handlers: {
'onAfterClose' : function(){
this.getOpener().setEnabled(true);//включаем обратно
}
}
});
});
break;
case 'dialog':
acessor(configRoot, function(){
$ws.core.attachInstance("Control/Area:Dialog", {
template: fSpec[1],
opener: this,
context: (new $ws.proto.Context().setPrevious(this.getLinkedContext()))
})
});
break;
case 'command':
var fn = function(event) {
event.setResult($ws.single.CommandDispatcher.sendCommand.apply($ws.single.CommandDispatcher, [this, fSpec[1]].concat(Array.prototype.slice.call(arguments, 1))));
};
fn.isCommand = true;
acessor(configRoot, fn);
break;
case 'newpage':
case 'page':
acessor(configRoot, function() {
var link = typeof this.getOpenLink === 'function' ? this.getOpenLink() || fSpec[1] : fSpec[1];
if(fSpec[0] == 'page')
window.location = link;
else
window.open(link);
});
break;
case 'menu':
acessor(configRoot, function(){
if($ws.helpers.instanceOfModule(this, 'SBIS3.CORE.Button')){
this._options.menuName = fSpec[1];
this.unsubscribe('onActivated', arguments.callee);
this._initMenu();
this._notify('onActivated');
}
});
break;
}
});
block3.close();
var block4 = BOOMR.plugins.WS.startBlock("FastTemplate:ctor:handlers_preload:"+this.getName());
$ws.helpers.forEach(this._rootConfig, function(val, key){
if(key.substr(0, 2) == 'on') {
var handlers = val.split('|');
$ws.helpers.forEach(handlers, function(val, idx){
self._dReady.push($ws.core.getHandler(val).addCallback(function(f){
self._loadedHandlers[key] = self._loadedHandlers[key] || [];
self._loadedHandlers[key][idx] = f;
return f;
}));
});
}
});
block4.close();
if(this._rootConfig.style) {
var cssReady = $ws.helpers.insertCss(this._rootConfig.style, true, this.getName());
if (cssReady) {
this._dReady.push(cssReady);
}
}
var include = $ws.helpers.filter(this._dom.firstChild.childNodes, function(child){
return child.nodeName == 'I';
});
if (include.length > 0) {
// Не может быть несколько инклюдов... Всегда берем первый
var spec = include[0].getAttribute('spec');
if(spec) {
try {
spec = JSON.parse(spec);
} catch(e) {
spec = false;
}
if(spec) {
spec.js && self._dReady.push($ws.core.attachSequentally(spec.js));
spec.css && self._dReady.push($ws.core.attachSequentally(spec.css));
}
}
}
this._dReady.done();
block.close();
},
_getIncludeDescriptorNodes: function() {
return $ws.helpers.filter(this._dom.firstChild.childNodes, function(child){
return child.nodeName == 'I';
});
},
_collectAllControlsToPreload: function(source) {
return $ws.helpers.reduce(source, function(res, item){
if (item.children && item.children.length) {
res = res.concat(this._collectAllControlsToPreload(item.children));
}
res.push(item);
return res;
}, [], this);
},
isPage: function() {
return !this._rootConfig.isApplication;
},
getConfig: function() {
$ws.helpers.forEach(this._rootConfig, function(attrValue, attr) {
this._mergeAttrToConfig(this._configuration, attr, attrValue);
}, this);
return this._configuration;
},
getStyle: function() {
return this._rootConfig.windowStyle || "";
},
getAlignment: function() {
var
h = this._rootConfig.HorizontalAlignment,
v = this._rootConfig.VerticalAlignment;
return {
horizontalAlignment: !h ? 'Stretch' : h,
verticalAlignment: !v ? 'Stretch' : v
};
},
getTitle: function() {
return this._rootConfig.title;
},
createMarkup: function(container) {
var block = BOOMR.plugins.WS.startBlock("mkup:" + this.getName());
// Шилов Д.А. вернул, так выполняются скрипты
container.html(this._dom.firstChild.innerHTML);
block.close();
},
getDimensions: function() {
var
width = this._rootConfig.width + "",
height = this._rootConfig.height + "";
return {
width: !width? '': width.toLowerCase() === 'auto' ? 'auto' : width.indexOf('%') >= 0 ? width : parseInt(width, 10) + 'px',
height: !height? '': height.toLowerCase() === 'auto' ? 'auto' : height.indexOf('%') >= 0 ? height : parseInt(height, 10) + 'px'
};
},
getControls: function(parent) {
if(parent)
return [];
return this._controlsConfig.children;
},
needSetZindexByOrder: function() {
return true;
}
});
$ws.proto.CompoundControlTemplate = $ws.proto.Template.extend({
$protected: {
_options: {
template: null
},
_elementId: null,
_docFragment: null,
_instance: null
},
$constructor: function(){
this._elementId = $ws.helpers.randomId();
this._loadedHandlers = this._options.template && this._options.template.handlers || {};
this._dReady.done();
},
_collectAllControlsToPreload: function(source) {
return source[0] && [ source[0] ] || [];
},
getConfig: function(){
var
templateClass = this._options.template,
proto = templateClass.prototype,
result = {},
dimensions = templateClass.dimensions || {};
if (proto && proto._initializer) {
proto._initializer.call(result);
result = result._options || {};
//По умолчанию у xhtml-шаблонов нужно устанавливать isRelativeTemplate, чтоб ресайзера не было у
//области по шаблону, которая загружает его
if (result.isRelativeTemplate === undefined) {
result.isRelativeTemplate = true;
}
}
$ws.core.merge(result, dimensions);
return result;
},
getStyle: function() {
return '';
},
getDimensions: function(){
var config = this.getConfig();
return $ws.helpers.reduce('width height'.split(' '), function(result, opt) {
if (opt in config) {
result[opt] = config[opt];
}
return result;
}, {});
},
isPage: function(){
return true;
},
getTitle: function(){
var config = this.getConfig();
return (config && config.title) || this._options.template.title || '';
},
createMarkup: function(container, config){
var element = document.createElement('div');
element.setAttribute('id', this._elementId);
if (config) {
element._componentConfig = config;
}
container.append(element);
},
getControls: function(parentId, templateRoot){
if (parentId && this._elementId != parentId) {
return [];
}
var elem = templateRoot && templateRoot.find('#' + this._elementId).get(0);
return [$ws.core.merge({
id: this._elementId,
type: this._templateName
}, (elem && elem._componentConfig) || {})];
},
_getIncludeDescriptorNodes: function() {
// У CompoundControl все зависимости и подключения - в requirejs
return [];
}
});
/**
* Загрузчик внешних данных из хранимой процедуры
*
* @class $ws.proto.JSONLoader
* @public
*/
$ws.proto.JSONLoader = $ws.core.extend({}, /** @lends $ws.proto.JSONLoader.prototype */{
$protected: {
_options: {
url: '/data.php',
updateURL : '',
destroyURL : '',
tableName : ''
}
},
/**
* Инициирует загрузку данных
* @param {Object} arguments
* @param {String} action выполняемая операция: чтение, редактирование, удаление
* @returns {$ws.proto.Deferred}
*/
load: function(args, action){
var url = this._options.url;
if(action === "update" && this._options.updateURL)
url = this._options.updateURL;
else if(action === "destroy" && this._options.destroyURL)
url = this._options.destroyURL;
if(this._options.tableName !== '')
args["tableName"] = this._options.tableName;
return $ws.single.ioc.resolve('ITransport', {
method: 'POST',
dataType: 'json',
url: url + (/\?/.test(url) ? "&" : "?") + "$=1" + ($ws.core.debug ? "&" + Math.random() : "")
}).execute(args);
}
});
/**
* Класс "Набор данных"
*
* @class $ws.proto.RecordSet
* @extends $ws.proto.Abstract
* @public
*/
$ws.proto.RecordSet = $ws.proto.Abstract.extend(/** @lends $ws.proto.RecordSet.prototype */{
/**
* @event onBeforeLoad Возникает в момент начала загрузки данных через Reader
* @param {Object} eventObject описание в классе $ws.proto.Abstract
* @param {Object}filter Значение фильтров, с которыми началась загрузка
* @param {Object} options Опции, которые могли быть переданы при запросе
*/
/**
* @event onAfterLoad Возникает после окончания загрузки данных (RecordSet готов к использованию)
* @param {Object} eventObject описание в классе $ws.proto.Abstract
* @param {$ws.proto.RecordSet} recordSet Экземпляр рекорд-сета
* @param {Boolean} isSuccess успешна ли загрузка
* @param {Error} error произошедшая ошибка (если загрузка неуспешная)
* @param {Object} options Опции, которые могли быть переданы при запросе
*/
/**
* @event onRecordUpdated Возникает после обновлении записи в текущем рекордсете
* @param {Object} eventObject описание в классе $ws.proto.Abstract
* @param {$ws.proto.Record} record измененная запись
*/
/**
* @event onRecordDeleted Возникает при удалении обновлении записи в текущем рекордсете
* @param {Object} eventObject описание в классе $ws.proto.Abstract
* @param {String} primaryKey Первичный ключ удаленной записи
* @param {$ws.proto.Record} record Сама удалённая запись
*/
/**
* @event onAbort возникает при прерывании загрузки
* @param {Object} eventObject описание в классе $ws.proto.Abstract
*/
/**
* @event onPageChange при смене страницы рекордсета
* @param {Object} eventObject описание в классе $ws.proto.Abstract
*/
/**
* @event onShadowLoad При загрузке, если указать в методе loadNode параметр shadowed
* @param {Object} eventObject описание в классе $ws.proto.Abstract
* @param