nest-parrot
Version:
Parrot built on react
17,531 lines • 506 kB
JavaScript
/** nest-parrot.V0.6.26 2019-05-16 */
(function (window) {
var patches = {
console: function () {
var noop = function () {};
var methods = ['assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn'];
var length = methods.length;
var console = window.console = window.console || {};
while (length--) {
var method = methods[length];
// Only stub undefined methods.
if (!console[method]) {
console[method] = noop;
}
}
window.console = window.console ? window.console : console;
},
string: function () {
if (String.prototype.upperFirst === undefined) {
String.prototype.upperFirst = function () {
if (this.length == 1) {
return this.toUpperCase();
} else {
return this.substring(0, 1).toUpperCase() + this.substring(1);
}
};
}
if (!String.prototype.endsWith) {
String.prototype.endsWith = function (searchString, position) {
var subjectString = this.toString();
if (position === undefined || position > subjectString.length) {
position = subjectString.length;
}
position -= searchString.length;
var lastIndex = subjectString.indexOf(searchString, position);
return lastIndex !== -1 && lastIndex === position;
};
}
if (!String.prototype.startsWith) {
String.prototype.startsWith = function (prefix) {
return this.slice(0, prefix.length) === prefix;
};
}
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
};
}
if (String.prototype.isEmpty === undefined) {
String.prototype.isEmpty = function () {
return this.length === 0;
};
}
if (String.prototype.isBlank === undefined) {
String.prototype.isBlank = function () {
return this.trim().length === 0;
};
}
/**
* replace place holders %1, %2, etc with given string array
*
* @param strArray
* @returns
*/
if (String.prototype.format === undefined) {
String.prototype.format = function (strArray) {
return this.replace(/%(\d+)/g, function (_, m) {
return strArray[--m];
});
};
}
if (String.prototype.currencyFormat === undefined) {
String.prototype.currencyFormat = function (fraction) {
fraction = fraction ? fraction : 0;
var value = this * 1;
return value.toFixed(fraction).replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
};
}
if (String.prototype.padLeft === undefined) {
String.prototype.padLeft = function (nSize, ch) {
var len = 0;
var s = this ? this : "";
ch = ch ? ch : '0'; // default add 0
len = s.length;
while (len < nSize) {
s = ch + s;
len++;
}
return s;
};
}
if (String.prototype.padRight === undefined) {
String.prototype.padRight = function (nSize, ch) {
var len = 0;
var s = this ? this : "";
ch = ch ? ch : '0'; // default add 0
len = s.length;
while (len < nSize) {
s = s + ch;
len++;
}
return s;
};
}
if (String.prototype.movePointLeft === undefined) {
String.prototype.movePointLeft = function (scale) {
var s, s1, s2, ch, ps, sign;
ch = ".";
sign = '';
s = this ? this : "";
if (scale <= 0) {
return s;
}
ps = s.split('.');
s1 = ps[0] ? ps[0] : "";
s2 = ps[1] ? ps[1] : "";
if (s1.slice(0, 1) == '-') {
s1 = s1.slice(1);
sign = '-';
}
if (s1.length <= scale) {
ch = "0.";
s1 = s1.padLeft(scale);
}
return sign + s1.slice(0, -scale) + ch + s1.slice(-scale) + s2;
};
}
if (String.prototype.movePointRight === undefined) {
String.prototype.movePointRight = function (scale) {
var s, s1, s2, ch, ps, sign;
ch = '.';
s = this ? this : "";
if (scale <= 0) {
return s;
}
ps = s.split('.');
s1 = ps[0] ? ps[0] : "";
s2 = ps[1] ? ps[1] : "";
if (s2.length <= scale) {
ch = '';
s2 = s2.padRight(scale);
}
if (s1.slice(0, 1) == '-') {
s1 = s1.slice(1);
sign = '-';
} else {
sign = '';
}
if (s1 == 0) {
s1 = '';
}
// window.console.log('Return[sign=' + sign + ', s1=' + s1 + ', s2-1=' + s2.slice(0, scale) + ', ch=' + ch + ', s2-2=' + s2.slice(scale, s2.length) + ']');
var integral = (s1 + s2.slice(0, scale)).replace(/^0+/, '');
if (integral.isEmpty()) {
integral = '0';
}
return sign + integral + ch + s2.slice(scale, s2.length);
};
}
},
number: function () {
// if (Number.prototype.currencyFormat === undefined) {
// Number.prototype.currencyFormat = function (fraction) {
// fraction = fraction ? fraction : 0;
// return value.toFixed(fraction).replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
// };
// }
},
array: function () {
if (!Array.prototype.find) {
Array.prototype.find = function (predicate) {
if (this === null) {
throw new TypeError('Array.prototype.find called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return value;
}
}
return undefined;
};
}
if (!Array.prototype.findIndex) {
Array.prototype.findIndex = function (predicate) {
if (this === null) {
throw new TypeError('Array.prototype.findIndex called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return i;
}
}
return -1;
};
}
}
};
patches.console();
patches.string();
patches.number();
patches.array();
})(window);
// copy from https://github.com/princed/caret
(function ($, window) {
function focus(target) {
if (!document.activeElement || document.activeElement !== target) {
target.focus();
}
}
$.fn.caret = function (pos) {
var target = this[0];
var range, range1, range2, bookmark;
var isContentEditable = target.contentEditable === 'true';
//get
if (arguments.length == 0) {
//HTML5
if (window.getSelection) {
//contenteditable
if (isContentEditable) {
focus(target);
var selection = window.getSelection();
// Opera 12 check
if (!selection.rangeCount) {
return 0;
}
range1 = selection.getRangeAt(0);
range2 = range1.cloneRange();
range2.selectNodeContents(target);
range2.setEnd(range1.endContainer, range1.endOffset);
return range2.toString().length;
}
//textarea
return target.selectionStart;
}
//IE<9
if (document.selection) {
focus(target);
//contenteditable
if (isContentEditable) {
range1 = document.selection.createRange();
range2 = document.body.createTextRange();
range2.moveToElementText(target);
range2.setEndPoint('EndToEnd', range1);
return range2.text.length;
}
//textarea
pos = 0;
range = target.createTextRange();
range2 = document.selection.createRange().duplicate();
bookmark = range2.getBookmark();
range.moveToBookmark(bookmark);
while (range.moveStart('character', -1) !== 0) {
pos++;
}
return pos;
}
//not supported
return 0;
}
//set
if (pos == -1) {
pos = this[isContentEditable ? 'text' : 'val']().length;
}
//HTML5
if (window.getSelection) {
//contenteditable
if (isContentEditable) {
focus(target);
window.getSelection().collapse(target.firstChild, pos);
}
//textarea
else target.setSelectionRange(pos, pos);
}
//IE<9
else if (document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(target);
range.moveStart('character', pos);
range.collapse(true);
range.select();
}
if (!isContentEditable) {
focus(target);
}
return pos;
};
})(jQuery, window);
(function (window) {
var $pt = window.$pt;
if ($pt == null) {
$pt = {};
window.$pt = $pt;
}
// exceptions
/**
* component exception
* @param code {string} exception code
* @param message {string} exception message
*/
var ComponentException = function (code, message) {
this.value = code;
this.message = message;
this.toString = function () {
return this.value + ": " + this.message;
};
};
/**
* create component exception
* @param code {string} exception code
* @param message {string} exception message
* @returns {ComponentException}
*/
$pt.createComponentException = function (code, message) {
return new ComponentException(code, message);
};
// messages
var messages = {};
$pt.messages = messages;
$pt.defineMessage = function (key, message) {
// if (messages[key] != null) {
// window.console.debug('Message[' + key + '=' + messages[key] + '] was replaced by [' + message + ']');
// }
messages[key] = message;
return $pt;
};
$pt.getMessage = function (key) {
var message = messages[key];
return message == null ? null : message;
};
$pt.__messagesDomain = {};
var throwMessageTypeConflictException = function (key, source, target) {
var exp = $pt.createComponentException($pt.ComponentConstants.Err_Incorrect_Messages_Format, 'Message [%1] has conflict type in source and target.'.format([key]));
exp.source = source;
exp.target = target;
throw exp;
};
var internalInstallMessages = function (source, target, prefix) {
// console.log('prefix', prefix);
Object.keys(source).forEach(function (key) {
var message = source[key];
var existMessage = target[key];
if (typeof message === 'object' && !Array.isArray(message)) {
if (existMessage) {
if (typeof existMessage === 'object' && !Array.isArray(existMessage)) {
internalInstallMessages(message, existMessage, prefix ? prefix + '.' + key : key);
} else {
throwMessageTypeConflictException(prefix ? prefix + '.' + key : key, message, existMessage);
}
} else {
// not exists in target, create a JSON to handle
target[key] = {};
internalInstallMessages(message, target[key], prefix ? prefix + '.' + key : key);
}
} else {
if (typeof existMessage === 'object' && !Array.isArray(existMessage)) {
throwMessageTypeConflictException(prefix ? prefix + '.' + key : key, message, existMessage);
} else {
if (existMessage) {
console.error('Message [%1] exists in target.'.format([prefix ? prefix + '.' + key : key]));
console.log('source message: ', source);
console.log('target message: ', target);
}
target[key] = message;
}
}
});
};
$pt.installMessages = function (domain, messagesJSON, messageTarget) {
if (typeof messagesJSON !== 'object') {
throw $pt.createComponentException($pt.ComponentConstants.Err_Incorrect_Messages_Format, 'Messages must be an JSON object.');
}
if (domain == null || typeof domain !== 'string' || domain.isBlank()) {
throw $pt.createComponentException($pt.ComponentConstants.Err_Incorrect_Messages_Format, 'Domain of messages must be a string.');
}
console.log('Start to install messages on domain [' + domain + '].');
var target = messageTarget ? messageTarget : $pt.messages;
internalInstallMessages(messagesJSON, target);
if (!$pt.__messagesDomain[domain]) {
$pt.__messagesDomain[domain] = {
domain: domain,
messages: []
};
}
$pt.__messagesDomain[domain].messages.push({
target: target,
json: messagesJSON
});
console.log('End of install messages on domain [' + domain + '].');
};
var internalUninstallMessages = function (source, target, prefix) {
Object.keys(source).forEach(function (key) {
var message = source[key];
var existMessage = target[key];
if (typeof message === 'object' && !Array.isArray(message)) {
if (existMessage) {
if (typeof existMessage === 'object' && !Array.isArray(existMessage)) {
internalUninstallMessages(message, existMessage, prefix ? prefix + '.' + key : key);
if (Object.keys(existMessage).length == 0) {
// all content removed, delete from target
delete target[key];
}
} else {
throwMessageTypeConflictException(prefix ? prefix + '.' + key : key, message, existMessage);
}
}
} else {
if (typeof existMessage === 'object' && !Array.isArray(existMessage)) {
throwMessageTypeConflictException(prefix ? prefix + '.' + key : key, message, existMessage);
} else {
delete target[key];
}
}
});
};
$pt.uninstallMessages = function (domain, messageTarget) {
if (domain == null || typeof domain !== 'string' || domain.isBlank()) {
throw $pt.createComponentException($pt.ComponentConstants.Err_Incorrect_Messages_Format, 'Domain of messages must be a string.');
}
console.log('Start to uninstall messages on domain [' + domain + '].');
var target = messageTarget ? messageTarget : $pt.messages;
var domainMessages = $pt.__messagesDomain[domain];
if (domainMessages) {
domainMessages.messages = domainMessages.messages.map(function (log) {
if (log.target === target) {
internalUninstallMessages(log.json, target);
return null;
} else {
return log;
}
}).filter(function (log) {
return log != null;
});
if (domainMessages.messages.length == 0) {
// all messages uninstalled, remove domain
delete $pt.__messagesDomain[domain];
}
}
console.log('End of uninstall messages on domain [' + domain + '].');
};
// components
$pt.Components = {};
$pt.exposeComponents = function (context) {
Object.keys($pt.Components).forEach(function (component) {
context[component] = $pt.Components[component];
});
};
// component constants
$pt.ComponentConstants = {
// component types
Text: "text",
TextInJSON: { type: 'text', label: true, popover: true, renderError: true, delay: 1000 },
TextArea: 'textarea',
Select: "select",
Check: "check",
ArrayCheck: 'acheck',
Toggle: 'toggle',
Radio: "radio",
Table: { type: "table", label: false, popover: false, renderError: false },
Tree: { type: "tree", label: false, popover: false, renderError: false },
SelectTree: "seltree",
Date: "date",
Search: "search",
Button: { type: "button", label: false, popover: false, renderError: false },
Tab: { type: 'tab', label: false, popover: false, renderError: false },
ArrayTab: { type: 'atab', label: false, popover: false, renderError: false },
Panel: { type: 'panel', label: false, popover: false, renderError: false },
ArrayPanel: { type: 'apanel', label: false, popover: false, renderError: false },
Label: { type: 'label', label: false },
Form: { type: 'form', label: false, popover: false, renderError: false },
ButtonFooter: { type: 'buttonfooter', label: false, popover: false, renderError: false },
File: "file",
Nothing: { type: "nothing", label: false },
// date format
Default_Date_Format: "YYYY/MM/DD HH:mm:ss.SSS", // see momentjs
// code table
CODETABLE_PARENT_VALUE_KEY: 'value',
CODETABLE_SENDER_PROXY: null,
CODETABLE_RECEIVER_PROXY: null,
CODETABLE_REMOTE_PROXY: null,
// error display
ERROR_POPOVER: true,
// exception codes
Err_Unsupported_Component: "PT-00001",
Err_Unuspported_Column_Sort: "PT-00002",
Err_Search_Text_Trigger_Digits_Not_Defined: "PT-00003",
Err_Tab_Index_Out_Of_Bound: "PT-00004",
Err_Incorrect_Messages_Format: 'PT-00005',
// http status
Http_Status: {
"0": "Browser Error",
"400": "Bad Request",
"401": "Unauthorized",
"402": "Payment Required",
"403": "Forbidden",
"404": "Not Found",
"405": "Method Not Allowed",
"406": "Not Acceptable",
"407": "Proxy Authentication Required",
"408": "Request Timeout",
"409": "Conflict",
"410": "Gone",
"411": "Length Required",
"412": "Precondition Failed",
"413": "Request Entity Too Large",
"414": "Request-URI Too Long",
"415": "Unsupported Media Type",
"416": "Requested Range Not Satisfiable",
"417": "Expectation Failed",
"500": "Internal Server Error",
"501": "Not Implemented",
"502": "Bad Gateway",
"503": "Service Unavailable",
"504": "Gateway Timeout",
"505": "HTTP Version Not Supported",
// customize
"506": "Application Exception"
}
};
$pt.parseJSON = function (object) {
if (object == null) {
return null;
}
if (typeof object === 'string') {
return JSON.parse(object);
} else {
return object;
}
};
$pt.isVisibleOnAuth = function (component) {
return true;
};
$pt.markFuncAsWrap = function (func) {
if (func) {
func.wrap = true;
}
return func;
};
/*!
* jQuery Browser Plugin 0.1.0
* https://github.com/gabceb/jquery-browser-plugin
*
* Original jquery-browser code Copyright 2005, 2015 jQuery Foundation, Inc. and other contributors
* http://jquery.org/license
*
* Modifications Copyright 2015 Gabriel Cebrian
* https://github.com/gabceb
*
* Released under the MIT license
*
* Date: 05-07-2015
*/
/*global window: false */
(function () {
"use strict";
function uaMatch(ua) {
// If an UA is not provided, default to the current browser UA.
if (ua === undefined) {
ua = window.navigator.userAgent;
}
ua = ua.toLowerCase();
var match = /(edge)\/([\w.]+)/.exec(ua) || /(opr)[\/]([\w.]+)/.exec(ua) || /(chrome)[ \/]([\w.]+)/.exec(ua) || /(iemobile)[\/]([\w.]+)/.exec(ua) || /(version)(applewebkit)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec(ua) || /(webkit)[ \/]([\w.]+).*(version)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec(ua) || /(webkit)[ \/]([\w.]+)/.exec(ua) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || /(msie) ([\w.]+)/.exec(ua) || ua.indexOf("trident") >= 0 && /(rv)(?::| )([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];
var platform_match = /(ipad)/.exec(ua) || /(ipod)/.exec(ua) || /(windows phone)/.exec(ua) || /(iphone)/.exec(ua) || /(kindle)/.exec(ua) || /(silk)/.exec(ua) || /(android)/.exec(ua) || /(win)/.exec(ua) || /(mac)/.exec(ua) || /(linux)/.exec(ua) || /(cros)/.exec(ua) || /(playbook)/.exec(ua) || /(bb)/.exec(ua) || /(blackberry)/.exec(ua) || [];
var browser = {},
matched = {
browser: match[5] || match[3] || match[1] || "",
version: match[2] || match[4] || "0",
versionNumber: match[4] || match[2] || "0",
platform: platform_match[0] || ""
};
if (matched.browser) {
browser[matched.browser] = true;
browser.version = matched.version;
browser.versionNumber = parseInt(matched.versionNumber, 10);
}
if (matched.platform) {
browser[matched.platform] = true;
}
// These are all considered mobile platforms, meaning they run a mobile browser
if (browser.android || browser.bb || browser.blackberry || browser.ipad || browser.iphone || browser.ipod || browser.kindle || browser.playbook || browser.silk || browser["windows phone"]) {
browser.mobile = true;
}
// These are all considered desktop platforms, meaning they run a desktop browser
if (browser.cros || browser.mac || browser.linux || browser.win) {
browser.desktop = true;
}
// Chrome, Opera 15+ and Safari are webkit based browsers
if (browser.chrome || browser.opr || browser.safari) {
browser.webkit = true;
}
// IE11 has a new token so we will assign it msie to avoid breaking changes
if (browser.rv || browser.iemobile) {
var ie = "msie";
matched.browser = ie;
browser[ie] = true;
}
// Edge is officially known as Microsoft Edge, so rewrite the key to match
if (browser.edge) {
delete browser.edge;
var msedge = "msedge";
matched.browser = msedge;
browser[msedge] = true;
}
// Blackberry browsers are marked as Safari on BlackBerry
if (browser.safari && browser.blackberry) {
var blackberry = "blackberry";
matched.browser = blackberry;
browser[blackberry] = true;
}
// Playbook browsers are marked as Safari on Playbook
if (browser.safari && browser.playbook) {
var playbook = "playbook";
matched.browser = playbook;
browser[playbook] = true;
}
// BB10 is a newer OS version of BlackBerry
if (browser.bb) {
var bb = "blackberry";
matched.browser = bb;
browser[bb] = true;
}
// Opera 15+ are identified as opr
if (browser.opr) {
var opera = "opera";
matched.browser = opera;
browser[opera] = true;
}
// Stock Android browsers are marked as Safari on Android.
if (browser.safari && browser.android) {
var android = "android";
matched.browser = android;
browser[android] = true;
}
// Kindle browsers are marked as Safari on Kindle
if (browser.safari && browser.kindle) {
var kindle = "kindle";
matched.browser = kindle;
browser[kindle] = true;
}
// Kindle Silk browsers are marked as Safari on Kindle
if (browser.safari && browser.silk) {
var silk = "silk";
matched.browser = silk;
browser[silk] = true;
}
// Assign the name and platform variable
browser.name = matched.browser;
browser.platform = matched.platform;
return browser;
}
// Run the matching process, also assign the function to the returned object
// for manual, jQuery-free use if desired
window.jQBrowser = uaMatch(window.navigator.userAgent);
window.jQBrowser.uaMatch = uaMatch;
$pt.browser = window.jQBrowser;
})();
var _context = window;
$pt.getService = function (context, serviceName) {
var innerContext = context ? context : _context;
var innerServiceName = serviceName ? serviceName : '$service';
if (!innerContext[innerServiceName]) {
innerContext[innerServiceName] = {};
}
return innerContext[innerServiceName];
};
})(window);
(function (window, $, deparam) {
var $pt = window.$pt;
if ($pt == null) {
$pt = {};
window.$pt = $pt;
}
$pt.AjaxConstants = {
ContentType: {
POST: "application/json; charset=UTF-8",
GET: "application/json; charset=UTF-8",
DELETE: "application/json; charset=UTF-8",
PUT: "application/json; charset=UTF-8"
},
Stringify: {
POST: true,
GET: false,
DELETE: false,
PUT: true
}
};
/**
* submit to server
* @param options {*} same as jquery ajax options, three more properties
* url: string
* done: same as jquery ajax done callback function
* fail: function or json object
* function: same as jquery ajax fail callback function
* json: key is return status, value is function which is same as jquery ajax fail callback function
* @returns {jqXHR}
*/
var submit = function (options) {
var url = options.url;
var done = options.done;
var fail = options.fail;
var quiet = options.quiet;
delete options.url;
delete options.done;
delete options.fail;
// build on request dialog
// show
if (quiet === true) {} else {
$pt.Components.NOnRequestModal.getOnRequestModal().show();
}
var hideOnRequest = function () {
if (quiet === true) {} else {
$pt.Components.NOnRequestModal.getOnRequestModal().hide();
}
};
return $.ajax(url, options).done(function (data, textStatus, jqXHR) {
if (done !== undefined && done !== null) {
try {
done(data, textStatus, jqXHR);
} catch (err) {
console.error(err);
console.error(data);
console.error(textStatus);
console.error(jqXHR);
var message = 'Unknown error occurred, see console for more information.';
message = err ? err.stack ? err.stack : err.toString ? err.toString() : message : message;
$pt.Components.NExceptionModal.getExceptionModal().show('Javascript Error', message);
}
}
}).fail(function (jqXHR, textStatus, errorThrown) {
if (fail !== undefined && fail !== null) {
var callback = null;
if (typeof fail === 'function') {
callback = fail;
} else {
callback = fail["" + jqXHR.status];
}
if (callback != null) {
try {
callback(jqXHR, textStatus, errorThrown);
} catch (err) {
console.error(err);
console.error(data);
console.error(textStatus);
console.error(jqXHR);
var message = 'Unknown error occurred, see console for more information.';
message = err ? err.stack ? err.stack : err.toString ? err.toString() : message : message;
$pt.Components.NExceptionModal.getExceptionModal().show('Javascript Error', message);
}
} else {
$pt.Components.NExceptionModal.getExceptionModal().show("" + jqXHR.status, jqXHR.responseText);
}
} else {
$pt.Components.NExceptionModal.getExceptionModal().show("" + jqXHR.status, jqXHR.responseText);
}
}).always(function () {
// hide
hideOnRequest();
});
};
var needStringify = function (predefine, specific) {
if (specific === true) {
return true;
} else if (specific === false) {
return false;
} else if (predefine === true) {
return true;
} else {
return false;
}
};
/**
* http post
* @param url {string}
* @param data {*}
* @param settings {*} optional jquery ajax settings
* @returns {jqXHR}
*/
$pt.internalDoPost = $pt.doPost = function (url, data, settings) {
settings = settings ? settings : {};
if (needStringify($pt.AjaxConstants.Stringify.POST, settings.stringify)) {
data = typeof data === 'string' ? data : JSON.stringify(data);
}
return submit($.extend({
method: "POST",
dataType: "json",
contentType: $pt.AjaxConstants.ContentType.POST
}, settings, {
url: url,
// always send string to server side
data: data
}));
};
/**
* http put
* @param url {string}
* @param data {*}
* @param settings {*} optional jquery ajax settings
* @returns {jqXHR}
*/
$pt.doPut = function (url, data, settings) {
settings = settings ? settings : {};
if (needStringify($pt.AjaxConstants.Stringify.PUT, settings.stringify)) {
data = typeof data === 'string' ? data : JSON.stringify(data);
}
return submit($.extend({
method: "PUT",
dataType: "json",
contentType: $pt.AjaxConstants.ContentType.PUT
}, settings, {
url: url,
data: data
}));
};
/**
* http get
* @param url {string}
* @param data {*}
* @param settings {*} optional jquery ajax settings
* @returns {jqXHR}
*/
$pt.doGet = function (url, data, settings) {
settings = settings ? settings : {};
if (needStringify($pt.AjaxConstants.Stringify.GET, settings.stringify)) {
data = typeof data === 'string' ? data : JSON.stringify(data);
}
return submit($.extend({
method: "GET",
dataType: "json",
contentType: $pt.AjaxConstants.ContentType.GET
}, settings, {
url: url,
data: data
}));
};
/**
* http delete
* @param url {string}
* @param data {*}
* @param settings {*} optional jquery ajax settings
* @returns {jqXHR}
*/
$pt.doDelete = function (url, data, settings) {
settings = settings ? settings : {};
if (needStringify($pt.AjaxConstants.Stringify.DELETE, settings.stringify)) {
data = typeof data === 'string' ? data : JSON.stringify(data);
}
return submit($.extend({
method: "DELETE",
dataType: "json",
contentType: $pt.AjaxConstants.ContentType.DELETE
}, settings, {
url: url,
data: data
}));
};
/**
* relocate page, use window.location
* @param url {string}
* @param data {*}
*/
$pt.relocatePage = function (url, data) {
var finalURL = url;
if (data) {
if (finalURL.indexOf('?') != -1) {
finalURL += '&' + $.param(data);
} else {
finalURL += '?' + $.param(data);
}
}
window.location = finalURL;
};
/**
* get data from url parameters
* @returns {*}
*/
$pt.getUrlData = function (params) {
var paramsString = '';
if (params !== undefined) {
if (params == null || params.isBlank()) {
return {};
} else {
paramsString = params;
}
} else {
paramsString = window.location.search;
if (paramsString != null && !paramsString.isBlank() && paramsString.trim() != '?') {
paramsString = paramsString.substring(1);
} else {
return {};
}
}
return $.deparam(paramsString);
};
/**
* mock ajax
* include jquery-mockjax when call this method, or do nothing
* parameters are same as mockjax
*/
$pt.mock = function () {
if (!$.mockjax) {
return;
}
$.mockjax.apply($, arguments);
};
// routes
var routes = {
context: '',
urls: {}
};
$pt.routes = routes;
/**
* define web context
* @param webContext {string}
*/
$pt.defineWebContext = function (webContext) {
routes.context = webContext;
return $pt;
};
/**
* define url with given key
* @param key {string} key of url
* @param urlRelateToWebContext {string} related url with no web context
*/
$pt.defineURL = function (key, urlRelateToWebContext) {
if (routes.urls[key] != null) {
window.console.warn('URL[' + key + '=' + routes.urls[key] + '] was replaced by [' + routes.context + urlRelateToWebContext + ']');
}
routes.urls[key] = urlRelateToWebContext;
return $pt;
};
/**
* get url by given key
* @param key {string} key of url
* @returns {string} return null when not found, or url with web context
*/
$pt.getURL = function (key) {
var url = routes.urls[key];
return url == null ? null : routes.context + url;
};
})(window, jQuery, jQuery.deparam);
(function (window, $, jsface) {
var $pt = window.$pt;
if ($pt == null) {
$pt = {};
window.$pt = $pt;
}
/**
* code table sorter
* @type {class}
*/
var CodeTableSorter = jsface.Class({
constructor: function (otherId) {
this._otherId = otherId;
},
/**
* sort code table element array
* @param codes
*/
sort: function (codes) {
var _this = this;
codes.sort(function (a, b) {
if (_this._otherId) {
if (a.id == _this._otherId) {
return 1;
} else if (b.id == _this._otherId) {
return -1;
}
}
return a.text < b.text ? -1 : a.text > b.text ? 1 : 0;
});
}
});
/**
* code table
* @type {class}
*/
var CodeTable = jsface.Class({
/**
* construct code table
* @param data {{id:string, text:string}[]|{url:string, data:*}}
* array of json object, eg: {id:"1", text:"text"}.
* id is required,
* text is optional when renderer is defined.
* @param renderer {function} optional
* param: element of code table,
* returns: string, put as text property into code table element
* @param sorter {CodeTableSorter} optional
*/
constructor: function (data, renderer, sorter) {
if (data == null || Array.isArray(data)) {
// construct with array, local data
this._local = true;
this._initArray = data;
} else {
// construct with json, remote data
this._local = false;
this._url = data.url;
this._postData = data.data;
this._sendProxy = data.proxy;
this._receiveProxy = data.receiver;
}
this._renderer = renderer;
this._sorter = sorter;
},
isInitialized: function () {
return this._initialized === true;
},
isRemote: function () {
return this._local === false;
},
isRemoteButNotInitialized: function () {
return this.isRemote() && !this.isInitialized();
},
initializeRemote: function () {
if (this.isRemoteButNotInitialized()) {
// return a promise to load remote codes
// since there might be more than one components using the same codetable
// log the first loading promise and returns to all others
if (!this._loading) {
this._loading = this.__loadRemoteCodes(true).always(function () {
delete this._loading;
this._allLoaded = true;
}.bind(this));
}
return this._loading;
} else {
// already initialized, or is local
// return an immediately resolved promise
return $.Deferred(function (deferred) {
deferred.resolve();
}).promise();
}
},
setAsRemoteInitialized: function () {
this._codes = this._codes ? this._codes : [];
this._map = this._map ? this._map : {};
this._initialized = true;
return this;
},
/**
* get renderer of code table
*/
getRenderer: function () {
return this._renderer;
},
/**
* initialize code table element array
* @param codeTableArray {{id:string, text:string}[]}
* array of json object, eg: {id:"1", text:"text"}.
* id is required,
* text is optional when renderer is defined.
* @param renderer {function} optional
* param: element of code table,
* returns: string, put as text property into code table element
* @param sorter {CodeTableSorter} optional
* @private
*/
__initCodesArray: function (codeTableArray, renderer, sorter) {
this._codes = codeTableArray;
if (this._codes == null) {
this._codes = [];
}
var map = {};
// render element
var render = function (code) {
if (renderer) {
code.text = renderer(code);
if (code.children) {
code.children.forEach(render);
}
}
};
this._codes.forEach(function (code) {
map[code.id] = code;
render(code);
});
this._map = map;
// sort elements
if (sorter) {
var sort = function (codes) {
sorter.sort(codes);
codes.forEach(function (code) {
if (code.children) {
sort(code.children);
}
});
};
sort(this._codes);
}
return this;
},
/**
* load remote code table, quiet and synchronized
* @private
*/
__loadRemoteCodes: function (async) {
var _this = this;
var remoteProxy = this.__getSendProxy();
var remoteVisit = remoteProxy ? remoteProxy.call(this, {
url: this._url,
data: this._postData,
quiet: true,
async: async != null ? async : false
}) : $pt.doPost(this._url, this._postData, {
quiet: true,
async: async != null ? async : false
});
return remoteVisit.done(function (data) {
var receiveData = data;
var receiverProxy = _this.__getReceiveProxy();
if (receiverProxy) {
receiveData = receiverProxy.call(_this, receiveData);
}
// init code table element array after get data from remote
_this.__initCodesArray(receiveData, _this._renderer, _this._sorter);
}).fail(function (jqXHR, textStatus, errorThrown) {
// error to console, quiet backend
_this.__initCodesArray(null, _this._renderer, _this._sorter);
window.console.error('Status:' + textStatus + ', error:' + errorThrown);
}).always(function () {
_this._initialized = true;
});
},
__getSendProxy: function () {
return this._sendProxy || $pt.ComponentConstants.CODETABLE_SENDER_PROXY || $pt.ComponentConstants.CODETABLE_REMOTE_PROXY;
},
__getReceiveProxy: function () {
return this._receiveProxy || $pt.ComponentConstants.CODETABLE_RECEIVER_PROXY;
},
/**
* get code table element array
* @returns {{id: string, text: string}[]}
* @private
*/
__getCodes: function () {
if (this._codes === undefined || this._initialized !== true) {
if (this._local) {
// initialize local data
this.__initCodesArray(this._initArray, this._renderer, this._sorter);
// delete the reference
delete this._initArray;
this._initialized = true;
} else {
// initialize remote data
this.__loadRemoteCodes();
}
}
return this._codes;
},
/**
* get code table element map, key is id of element, value is element
* @returns {*}
* @private
*/
__getMap: function () {
this.__getCodes();
return this._map;
},
/**
* filter code table elements by given function or json object.
* when parameter is a json object and data is from remote, check the given value was loaded or not,
* if not loaded, loaded the code table elements by given parameter first and merge into the client elements.
* @param func {function|{value:string, name:string}}
* function: same as function definition Array.filter()
* json:
* value: value to filter code table elements
* name: property name to match the given value
* eg. code table element is
* {id: '1', text: 'Text', parentId: '1'}
* {id: '2', text: 'Text', parentId: '1'}
* {id: '3', text: 'Text', parentId: '2'}
* when the json object is {value: '1', name: 'parentId'},
* then the first and second items returned, third item filtered
* @returns {{id: string, text: string}[]} maybe contain other properties, depends on initial data.
* return empty array when no data
*/
filter: function (func) {
if (typeof func === 'function') {
// parameter is function
return this.__getCodes().filter(func);
} else {
var value = func.value;
this.__loadRemoteCodeSegment(value);
// filter
return this.__getCodes().filter(function (code) {
this.__setParentValueAsLoaded(code[func.name]);
return code[func.name] == value;
}.bind(this));
}
},
isSegmentLoaded: function (parentValue) {
return !this.__needLoadFromRemote(parentValue);
},
loadRemoteCodeSegment: function (parentValue) {
if (this.__needLoadFromRemote(parentValue)) {
// no local data, and loaded keys don't contain current value
// reset post data
this.__rebuildPostData(parentValue);
// store current local data
var existedData = this.__holdExistedCodes();
// get data from server
return this.__loadRemoteCodes(true).done(function () {
this.__setParentValueAsLoaded(parentValue);
}.bind(this)).always(function () {
this.__mergeAll(existedData);
}.bind(this));
} else {
// local or already loaded
// return an immediately resolved promise
return $.Deferred(function (deferred) {
deferred.resolve();
}).promise();
}
},
__needLoadFromRemote: function (parentValue) {
return !this._local && !this._allLoaded && (!this._loadedKeys || this._loadedKeys[parentValue + ''] !== true);
},
__rebuildPostData: function (parentValue) {
var values = {};
values[this.parentValueKey()] = parentValue;
if (this._postData) {
this._postData = $.extend({}, this._postData, values);
} else {
this._postData = values;
}
return this;
},
__holdExistedCodes: function () {
return {
codes: this._codes != null ? this._codes : [],
map: this._map != null ? this._map : {}
};
},
__mergeAll: function (existedData) {
// merge server data and local data
this._codes.push.apply(this._codes, existedData.codes);
this._map = $.extend(existedData.map, this._map);
return this;
},
__setParentValueAsLoaded: function (parentValue) {
// init loaded keys
if (!this._loadedKeys) {
// this._loadedKeys = [];
this._loadedKeys = {};
}
// log the loaded keys
// this._loadedKeys.push(parentValue);
this._loadedKeys[parentValue + ''] = true;
return this;
},
__loadRemoteCodeSegment: function (parentValue) {
if (this.__needLoadFromRemote(parentValue)) {
// no local data, and loaded keys don't contain current value
// reset post data
this.__rebuildPostData(parentValue);
// store current local data
var existedData = this.__holdExistedCodes();
// get data from server
this.__loadRemoteCodes();
this.__setParentValueAsLoaded(parentValue);
this.__mergeAll(existedData);
}
return this;
},
/**
* get element by given code
* @param code {string} code
* @returns {{id:string, text:string}} maybe contain other properties, depends on initial data.
* return null when not found
*/
get: function (code) {
var item = this.__getMap()[code];
return item == null ? null : item;
},
/**
* get element text by given code
* @param code {string} code
* @returns {string} element text, return null when not found
*/
getText: function (code) {
var item = this.get(code);
return item == null ? null : item.text;
},
/**
* get code table element array
* @returns {{id: string, text: string}[]} maybe contain other properties, depends on initial data.
* return empty array when no data
*/
list: function () {
return this.__getCodes();
},
/**
* get code table element array, with hierarchy keys.
* no duplicate id allowed when call this method.
* @returns {{codeId: codeItem}};
*/
listAllChildren: function () {
var items = {};
var fetchItem = function (item) {
items[item.id] = item;
if (item.children) {
item.children.forEach(function (child) {
fetchItem(child);
});
}
};
this.list().forEach(function (item) {
fetchItem(item);
});
return items;
},
/**
* get code table element array, with hierarchy keys
* @param {rootId: string, separtor: string}
* @returns {{codeId: codeItem}};
*/
listWithHierarchyKeys: function (options) {
var separator = options && options.separator ? options.separator : '|';
var rootId = options && options.rootId != null ? options.rootId : '0';
var items = {};
var fetchItem = function (item, parentId) {
items[parentId + separator + item.id] = item;
if (item.children) {
item.children.forEach(function (child) {
fetchItem(child, parentId + separator + item.id);
});
}
};
this.list().forEach(function (item) {
fetchItem(item, rootId);
});
return items;
},
/**
* check code table element by given function
* @param func {function} same as function definition Array.some()
* @returns {boolean}
*/
some: function (func) {
return this.__getCodes().some(func);
},
/**
* run each code table element by given function
* @param func {function} same as function definition Array.map()
* @returns {[]} element of array depends on the parameter
*/
map: function (func) {
return this.__getCodes().map(func);
},
name: function (name) {
if (name) {
this.__name = name;
return this;
} else {
return this.__name;
}
},
parentValueKey: function (key) {
if (key) {
this.__parentValueKey = key;
return this;
} else {
return this.__parentValueKey ? this.__parentValueKey : $pt.ComponentConstants.CODETABLE_PARENT_VALUE_KEY;
}
}
});
/**
* create default code table sorter by given other id
* @param otherId {string} optional. item with this id will be last one when given
* @returns {CodeTableSorter}
*/
$pt.createDefaultCodeTableSorter = function (otherId) {
return new CodeTableSorter(otherId);
};
/**
* construct code table
* @param items {{id:string, text:string}[]|{url:string, data:*}}
* array of json object, eg: {id:"1", text:"text"}.
* id is required,
* text is optional when renderer is defined.
* @param renderer {function} optional
* param: element of code table,
* returns: string, put as text property into code table element
* @param sorter {CodeTableSorter} optional
*/
$pt.createCodeTable = function (items, renderer, sorter) {
return new CodeTable(items, renderer, sorter);
};
/**
* extend code table
* @param {JSON} extendCodeTable extend class definition
* @return {jsface.Class} extend code table class
*/
$pt.extendCodeTable = function (extendCodeTable) {
return jsface.Class(CodeTable, extendCodeTable);
};
})(window, jQuery, jsface);
/**
* depends on jquery, jsface
* depends on parrot-pre-define
*/
(function (window, $, moment, jsface) {
var $pt = window.$pt;
if ($pt == null) {
$pt = {};
window.$pt = $pt;
}
$pt.BUILD_PROPERTY_VISITOR = true;
$pt.PROPERTY_SEPARATOR = '_';
/**
* clone json object
* @param jsonObject {{}}
*/
$pt.cloneJSON = function (jsonObject) {
return $.extend(true, {}, jsonObject);
};
$pt.mergeObject = function (params) {
var deep = params.deep;
var target = params.target ? params.target : {};
var sources = Array.isArray(params.sources) ? params.sources : [params.sources];
// console.log(target);
// console.log(sources);
var source,
propName,
sourceIndex = 0,
sourceCount = sources.length,
targetPropValue,
sourcePropValue,
sourcePropValueIsArray,
destPropValue;
// Handle case when target is a string or something (possible in deep copy)
if (typeof target !== "object" && !$.isFunction(target)) {
target = {};
}
for (; sourceIndex < sourceCount; sourceIndex++) {
// Only deal with non-null/undefined values
if ((source = sources[sourceIndex]) != null) {
// Extend the base object
// console.log(source);
for (propName in source) {
// console.log(propName);
targetPropValue = target[propName];
sourcePropValue = source[propName];
// Prevent never-ending loop
if (target === sourcePropValue) {
continue;
}
// Recurse if we're merging plain objects or arrays
if (deep && sourcePropValue && ($.isPlainObject(sourcePropValue) || (sourcePropValueIsArray = $.isArray(sourcePropValue)))) {
if (sourcePropValueIsArray) {
sourcePropValueIsArray = false;
// always create new array, change from jQuery
destPropValue = [];
} else {
destPropValue = targetPropValue && $.isPlainObject(targetPropValue) ? targetPropValue : {};
}
// Never move original objects, clone them
target[propName] = $pt.mergeObject({ deep: deep, target: destPropValue, sources: [sourcePropValue] });
// Don't bring in undefined values
} else if (sourcePropValue !== undefined) {
target[propName] = sourcePropValue;
}
}
}
}
// Return the modified object
return target;
};
/**
* get value from json object
* @param jsonObject {{}}
* @param id {string} key of property, can be linked by underline
* @returns {*}
*/
$pt.getValueFromJSON = function (jsonObject, id) {
if (id.indexOf($pt.PROPERTY_SEPARATOR) != -1) {
// hierarchy id
var ids = id.split($pt.PROPERTY_SEPARATOR);
var parent = jsonObject;
var value = null;
var values = ids.map(function (id) {
if (parent == null) {
return null;
} else {
value = parent[id];
parent = value;
return value;
}
});
return values[values.length - 1];
} else {
return jsonObject[id];
}
};
$pt.setValueIntoJSON = function (jsonObject, id, value) {
if (id.indexOf($pt.PROPERTY_SEPARATOR) == -1) {
jsonObject[id] = value;
} else {
var ids = id.split($pt.PROPERTY_SEPARATOR);
var parent = jsonObject;
for (var index = 0, count = ids.length - 1; index < count; index++) {
if (parent[ids[index]] == null) {
// new object
parent[ids[index]] = {};
}
parent = parent[ids[index]];
}
parent[ids[ids.length - 1]] = value;
}
return $pt;
};
/**
* table validation result
* @type {class}
*/
var TableValidationResult = jsface.Class({
constructor: function () {
this.__errors = {};
this.__models = {};
this.__startKeyIndex = 1;
},
/**
* push error
* @param model {{}} element of array
* @param error {{}} error messages
*/
push: function (model, error) {
this.__errors[this.__startKeyIndex] = error;
this.__models[this.__startKeyIndex] = model;
this.__startKeyIndex++;
return this;
},
/**
* remove error
* @param model {{}} element of array
*/
remove: function (model) {
var _this = this;
Object.keys(this.__models).forEach(function (key) {
var item = _this.__models[key];
if (item == model) {
delete _this.__models[key];
delete _this.__errors[key];
}
});
return this;
},
/**
* has error
* @returns {boolean}
*/
hasError: function () {
return Object.keys(this.__models).length !== 0;
},
/**
* get error
* @param model {{}} element of array
* @returns {{}}
*/
getError: function (model) {
// var keys = Object.keys(this.__models);
// for (var index = 0, count = keys.length; index < count; index++) {
// var item = this.__models[keys[index]];
// if (item == model) {
// return this.__errors[keys[index]];
// }
// }
// return null;
var errors = Object.keys(this.__models).map(function (key) {
return this.__models[key] == model ? this.__errors[key] : null;
}.bind(this)).filter(function (error) {
return error != null;
});
return errors.length === 0 ? null : errors[0];
}
});
/**
* create table validation result
* @returns {TableValidationResult}
*/
$pt.createTableValidationResult = function () {
return new TableValidationResult();
};
$pt.defineMessage('validate.required', '"%1" is Required.');
$pt.defineMessage('validate.length', 'Length of "%1" should be %2.');
$pt.defineMessage('validate.length.max', 'Length of "%1" cannot be more than %2.');
$pt.defineMessage('validate.length.min', 'Length of "%1" cannot be less than %2.');
$pt.defineMessage('validate.size.max', 'Size of "%1" cannot be more than %2.');
$pt.defineMessage('validate.size.min', 'Size of "%1" cannot be less than %2.');
$pt.defineMessage('validate.date.before', 'Value of "%1" must be before than %2.');
$pt.defineMessage('validate.date.after', 'Value of "%1" must be after than %2.');
/**
* validate rules
* @type {class}
*/
$pt.ValidateRules = {
/**
* check null or empty string
* @param model model itself
* @param value {*} property value
* @param settings {{}}
* @returns {string|boolean} return true when pass the validation
*/
required: function (model, value, settings) {
if (settings === false) {
// close the required check
return true;
}
if (value == null || (value + '').toString().isEmpty()) {
return $pt.getMessage('validate.required');
} else {
return true;
}
},
/**
* length check
* @param model model itself
* @param value {string} property value
* @param length {number}
* @returns {string|boolean} return true when pass the validation
*/
length: function (model, value, length) {
if (value == null || (value + '').length == 0) {
return true;
} else if ((value + '').length != length) {
return $pt.getMessage('validate.length').format(['%1', '' + length]);
}
},
/**
* max length check
* @param model model itself
* @param value {string} property value
* @param length {number}
* @returns {string|boolean} return true when pass the validation
*/
maxlength: function (model, value, length) {
if (value == null || (value + '').length == 0) {
return true;
} else if ((value + '').length > length) {
return $pt.getMessage('validate.length.max').format(['%1', '' + length]);
}
},
/**
* min length check
* @param model model itself
* @param value {string} property value
* @param length {number}
* @returns {string|boolean} return true when pass the validation
*/
minlength: function (model, value, length) {
if (value == null || (value + '').length == 0) {
return true;
} else if ((value + '').length < length) {
return $pt.getMessage('validate.length.min').format(['%1', '' + length]);
}
},
/**
* max size check
* @param model model itself
* @param value {{}[]} property value, should be an array
* @param size {number}
* @returns {string|boolean} return true when pass the validation
*/
maxsize: function (model, value, size) {
if (value == null || value.length == 0) {
return true;
} else if (value.length > size) {
return $pt.getMessage('validate.size.max').format(['%1', '' + size]);
}
},
/**
* min size check
* @param model model itself
* @param value {{}[]} property value, should be an array
* @param size {number}
* @returns {string|boolean} return true when pass the validation
*/
minsize: function (model, value, size) {
if (value == null || value.length < size) {
return $pt.getMessage('validate.size.min').format(['%1', '' + size]);
} else {
return true;
}
},
/**
* before special date check
* @param model model itself
* @param value {momentjs} date
* @param settings {{}} check rules
* @returns {string[]|string|boolean}
*/
before: function (model, value, settings) {
return $pt.ValidateRules.__dateCompare(value, model, settings, $pt.ValidateRules.__beforeSpecial);
},
/**
* before or equals special date
* @param momentValue {momentjs} datetime momentjs
* @param model {{}} model itself
* @param check {{}}
* @param format {string} datetime format, momentjs
* @param label {string} label in error message
* @returns {string|string[]|boolean}
* @private
*/
__beforeSpecial: function (momentValue, model, check, format, label) {
var compareValue = $pt.ValidateRules.__convertToMomentValue(check, model, format);
if (compareValue !== null) {
if (momentValue.unix() > compareValue.unix()) {
if (typeof check === "string") {
return $pt.getMessage('validate.date.before').format(['%1', label ? label : check]);
}
return $pt.getMessage('validate.date.before').format(['%1', compareValue.format(format)]);
}
}
return true;
},
after: function (model, value, settings) {
return $pt.ValidateRules.__dateCompare(value, model, settings, $pt.ValidateRules.__afterSpecial);
},
/**
* after or equals special date
* @param momentValue {momentjs} datetime momentjs
* @param model {{}} model itself
* @param check {{}}
* @param format {string} datetime format, momentjs
* @param label {string} label in error message
* @returns {string|string[]|boolean}
* @private
*/
__afterSpecial: function (momentValue, model, check, format, label) {
var compareValue = $pt.ValidateRules.__convertToMomentValue(check, model, format);
if (compareValue !== null) {
if (momentValue.unix() < compareValue.unix()) {
if (typeof check === "string") {
return $pt.getMessage('validate.date.after').format(['%1', label ? label : check]);
}
return $pt.getMessage('validate.date.after').format(['%1', compareValue.format(format)]);
}
}
return true;
},
/**
* convert value to momentjs object
* @param value {string|momentjs|function}
* @param model {{}} model itself
* @param format {string} datetime format, momentjs
* @returns {momentjs}
* @private
*/
__convertToMomentValue: function (value, model, format) {
var compareValue = null;
if (value === "now") {
compareValue = moment();
} else if (value.unix !== undefined) {
// value is a moment value
compareValue = value;
} else if (typeof value === "function") {
compareValue = value();
} else if (model.get(value) !== undefined) {
// value from another property
if (model.get(value) !== null) {
compareValue = moment(model.get(value), format);
}
} else {
// plain date time string
compareValue = moment(value, format);
}
return compareValue;
},
/**
* compare data
* @param value {momentjs} property value
* @param model {{}} model itself
* @param settings {{format: string, rule: string|function|momentjs, label:string}|string|function|momentjs}
* can be JSON object
* format: momentjs format
* rule: check rule, string or function or momentjs or array of three types.
* can be 'now' if it is a string,
* or be property id of model if it is a string.
* label: label in error message, optional.
* if rule is an array, and label declared, it must be an array which same length with rule property.
* can be string or function or momentjs or array of three types.
* if check rule is function, return a momentjs object
* @param checkFunc {function}
* @returns {string[]|string|boolean}
* @private
*/
__dateCompare: function (value, model, settings, checkFunc) {
if (value == null) {
return true;
}
var format = typeof settings.format === "string" ? settings.format : $pt.ComponentConstants.Default_Date_Format;
var checks = settings.rule ? settings.rule : settings;
var labels = settings.rule ? settings.label : null;
var dateValue = moment(value, format);
var results = [];
if (Array.isArray(checks)) {
for (var index = 0, count = checks.length; index < count; index++) {
var check = checks[index];
var label = labels ? labels[index] : null;
results.push(checkFunc(dateValue, model, check, format, label));
}
} else {
results.push(checkFunc(dateValue, model, checks, format, labels));
}
var messages = results.filter(function (result) {
return result !== true;
});
return messages.length === 0 ? true : messages.length == 1 ? messages[0] : messages;
},
/**
* table detail validate method
* @param model {{}} model itself
* @param value {{}[]} value of property, must be an array
* @param config {{}} validate rules config
* @returns {TableValidationResult|boolean}
*/
table: function (model, value, config, phase) {
if (value == null || value.length == 0) {
// no data
return true;
}
var results = $pt.createTableValidationResult();
var validator = $pt.createModelValidator(config);
value.forEach(function (item) {
var itemModel = $pt.createModel(item, validator);
itemModel.useBaseAsCurrent();
itemModel.parent(model);
if (phase) {
itemModel.validateByPhase(phase);
} else {
itemModel.validate();
}
var error = itemModel.getError();
if (Object.keys(error).length !== 0) {
results.push(item, error);
} else {
results.remove(item);
}
});
return results.hasError() ? results : true;
}
};
/**
* model validator
* @type {class}
*/
var ModelValidator = jsface.Class({
constructor: function (validatorConfigs, rules) {
if (Array.isArray(validatorConfigs)) {
this.__validator = {};
$.extend.apply($, [true, this.__validator].concat(validatorConfigs));
} else {
this.__validator = validatorConfigs;
}
this.__rules = rules;
if (this.__rules === undefined) {
this.__rules = $pt.ValidateRules;
} else {
this.__rules = $.extend(true, $pt.ValidateRules, rules);
}
},
/**
* get configuration
* @param id {string} optional
* @returns {*}
*/
getConfig: function (id) {
if (id) {
return this.__validator[id];
}
return this.__validator;
},
/**
* get rule
* @param ruleName
* @returns {*}
*/
getRule: function (ruleName) {
return this.__rules[ruleName];
},
/**
* validate by phase
* @param model {ModelInterface}
* @param value {*}
* @param ruleKey {string}
* @param phase {string}
* @param ruleBody {function|{_phase:string|string[], _when: function, rule: *}|{}[]}
* @returns {*}
* @private
*/
_validateByPhase: function (model, value, ruleKey, phase, ruleBody, dataId) {
var ret = null;
if (typeof ruleBody === "function") {
// a simple function, call it, gather the return value
ret = ruleBody.call(this, model, value, phase, dataId);
} else if (Array.isArray(ruleBody)) {
// rule body is an array
var _this = this;
var result = ruleBody.map(function (body) {
return _this._validateByPhase(model, value, ruleKey, phase, body, dataId);
});
var finalResult = [];
result.forEach(function (item) {
if (item == null) {
return;
}
if (Array.isArray(item)) {
// array of plain string
item.forEach(function (i) {
finalResult.push(i);
});
} else {
// plain string
finalResult.push(item);
}
});
return finalResult;
} else {
var realRuleBody = ruleBody;
var runRule = true;
if (phase != null && ruleBody._phase) {
realRuleBody = ruleBody.rule;
// phase is null, run all phase, pass this block
// no phase definition, pass this block
if (Array.isArray(ruleBody._phase)) {
var phaseMatched = ruleBody._phase.some(function (p) {
return p == phase;
});
if (!phaseMatched) {
runRule = false;
}
} else if (ruleBody._phase != phase) {
runRule = false;
}
} else if (ruleBody._phase) {
// no phase given, but there is phase on rule
realRuleBody = ruleBody.rule;
}
// a simple json
if (runRule && ruleBody._when) {
realRuleBody = ruleBody.rule;
var when = ruleBody._when.call(this, model);
if (!when) {
// only validate when '_when' returns true
runRule = false;
}
}
if (runRule) {
if (typeof realRuleBody === 'function') {
ret = realRuleBody.call(this, model, value, phase, dataId);
} else {
// console.log(model, value, realRuleBody);
ret = this.getRule(ruleKey).call(this, model, value, realRuleBody, phase, dataId);
}
}
}
return ret;
},
/**
* validate by phase
* @param model
* @param phase
* @param id
*/
validateByPhase: function (model, phase, id) {
var _this = this;
var result = null;
if (id) {
var config = this.getConfig(id);
if (config == null) {
// no rule defined
return true;
} else {
result = Object.keys(config).map(function (rule) {
var ruleBody = config[rule];
var value = model.get(id);
var ret = _this._validateByPhase(model, value, rule, phase, ruleBody, id);
return ret != null && ret !== true ? ret : null;
});
var finalResult = [];
result.forEach(function (item) {
if (item == null) {
return;
}
if (Array.isArray(item)) {
// array of plain string
item.forEach(function (i) {
finalResult.push(i);
});
} else {
// plain string
finalResult.push(item);
}
});
return finalResult.length === 0 ? true : finalResult;
}
} else {
result = {};
Object.keys(this.getConfig()).forEach(function (id) {
var ret = _this.validateByPhase(model, phase, id);
if (ret !== true) {
result[id] = ret;
}
});
return result;
}
},
/**
* validate
* @param model
* @param id
* @returns {*}
*/
validate: function (model, id) {
// validate by phase, all phases
return this.validateByPhase(model, null, id);
},
/**
* check property is required or not
* @param id
* @param phase can be array or plain text
*/
isRequired: function (id, phase) {
var config = this.getConfig(id);
if (config == null || config.required == null) {
// no required declared
return false;
} else if (config.required === true) {
// all phase required
return true;
} else if (phase == null) {
// no phase appointed
return false;
} else {
// phase appointed
var phases = Array.isArray(phase) ? phase : [phase];
var defines = Array.isArray(config.required) ? config.required : [config.required];
// console.log(phases, defines);
// return true when at least one definition which match the given phases and rule is true
return phases.some(function (phase) {
return defines.some(function (define) {
var definedPhase = Array.isArray(define._phase) ? define._phase : [define._phase];
return definedPhase.indexOf(phase) != -1 && define.rule === true;
});
});
}
}
});
/**
* create model validator
* @param config {{}|{}[]} validator configuration
* @param rules {{}} rules
* @returns {ModelValidator}
*/
$pt.createModelValidator = function (config, rules) {
return new ModelValidator(config, rules);
};
/**
* model interface
*/
var ModelInterface = {
/**
* constructor model class
* @param model {*}
* @param validator {ModelValidator}
*/
constructor: function (model, validator) {
this.__base = model;
this.__model = $pt.mergeObject({ deep: true, sources: model });
this.__validator = validator;
this.__validateResults = {};
this.__changed = false;
},
name: function (name) {
if (name) {
this.__name = name;
return this;
} else {
return this.__name;
}
},
/**
* get/set parent model
* @param parent {*} optional
* @returns {*}
*/
parent: function (parent) {
if (parent !== undefined) {
this.__parent = parent;
return this;
} else {
return this.__parent;
}
},
/**
* check the model is changed or not
* @returns {boolean}
*/
isChanged: function () {
return this.__changed;
},
/**
* get original model
* @returns {*}
*/
getOriginalModel: function () {
return this.__base;
},
/**
* get current model
* @returns {*}
*/
getCurrentModel: function () {
return this.__model;
},
/**
* use base model as current model
*/
useBaseAsCurrent: function () {
this.__model = this.__base;
return this;
},
/**
* merge data into current model.
* Attention: this is deep copy. if don't want do deep copy, use getCurrentModel, and merge manually.
* @param newModel {{}}
*/
mergeCurrentModel: function (newModel) {
this.__model = $pt.mergeObject({ deep: true, target: this.__model, sources: newModel });
return this;
},
/**
* apply current data to base model.
*/
applyCurrentToBase: function () {
this.__base = $pt.mergeObject({ deep: true, target: {}, sources: this.__model });
this.__changed = false;
return this;
},
/**
* get validator
* @returns {ModelValidator}
*/
getValidator: function () {
return this.__validator;
},
setValidator: function (validator, clearError) {
this.__validator = validator;
if (clearError) {
this.__validateResults = {};
}
return this;
},
/**
* get value by given id
* @param id {string} property id
* @returns {*}
*/
get: function (id) {
return $pt.getValueFromJSON(this.__model, id);
},
/**
* set value by given id and value
* @param id {string} property id
* @param value {*}
*/
set: function (id, value) {
var oldValue = this.get(id);
if (typeof oldValue === typeof value && oldValue == value) {
// value is same as old value
return;
}
$pt.setValueIntoJSON(this.__model, id, value);
this.__changed = true;
this.fireEvent({
model: this,
id: id,
old: oldValue,
"new": value,
time: "post",
type: "change"
});
return this;
},
/**
* add listener
* @param id {string} id of property
* @param time {string} post|pre
* @param type {string} change|add|remove
* @param listener {function} function with parameter event
*/
addListener: function (id, time, type, listener) {
if (!id) {
// monitor all
id = '--all';
}
if (id instanceof RegExp) {
return this.__addRegExpListener(id, time, type, listener);
} else {
return this.__addListener(id, time, type, listener);
}
},
__addRegExpListener: function (id, time, type, listener) {
if (this.__regExpListeners === undefined) {
this.__regExpListeners = {};
}
var key = time + "-" + type;
var listeners = this.__regExpListeners[key];
if (listeners === undefined) {
listeners = [];
this.__regExpListeners[key] = listeners;
}
var index = listeners.findIndex(function (item) {
return item.listener === listener && item.regexp.toString() == id.toString();
});
if (index == -1) {
listeners.push({
regexp: id,
listener: listener
});
}
return this;
},
__addListener: function (id, time, type, listener) {
if (this.__listeners === undefined) {
this.__listeners = {};
}
var key = id + '-' + time + "-" + type;
var listeners = this.__listeners[key];
if (listeners === undefined) {
listeners = [];
this.__listeners[key] = listeners;
}
var index = listeners.findIndex(function (item) {
return item === listener;
});
if (index == -1) {
listeners.push(listener);
}
return this;
},
addPostChangeListener: function (id, listener) {
return this.addListener(id, 'post', 'change', listener);
},
addPostAddListener: function (id, listener) {
return this.addListener(id, 'post', 'add', listener);
},
addPostRemoveListener: function (id, listener) {
return this.addListener(id, 'post', 'remove', listener);
},
addPostValidateListener: function (id, listener) {
return this.addListener(id, 'post', 'validate', listener);
},
/**
* remove listener
* @param id {string} id of property
* @param time {string} post|pre
* @param type {string} change|add|remove
* @param listener {function} function which added
*/
removeListener: function (id, time, type, listener) {
if (!id) {
id = '--all';
}
if (id instanceof RegExp) {
return this.__removeRegExpListener(id, time, type, listener);
} else {
return this.__removeListener(id, time, type, listener);
}
},
__removeRegExpListener: function (id, time, type, listener) {
if (this.__regExpListeners !== undefined) {
var listeners = this.__regExpListeners[time + "-" + type];
if (listeners != null) {
var index = listeners.findIndex(function (item) {
// 正则和监听器都相同
return item.listener === listener && item.regexp.toString() == id.toString();
});
if (index != -1) {
listeners.splice(index, 1);
}
}
}
return this;
},
__removeListener: function (id, time, type, listener) {
if (this.__listeners !== undefined) {
var listeners = this.__listeners[id + "-" + time + "-" + type];
if (listeners != null) {
// listeners is an array
var index = listeners.findIndex(function (item) {
return item === listener;
});
if (index != -1) {
listeners.splice(index, 1);
}
}
}
return this;
},
removePostChangeListener: function (id, listener) {
return this.removeListener(id, 'post', 'change', listener);
},
removePostAddListener: function (id, listener) {
return this.removeListener(id, 'post', 'add', listener);
},
removePostRemoveListener: function (id, listener) {
return this.removeListener(id, 'post', 'remove', listener);
},
removePostValidateListener: function (id, listener) {
return this.removeListener(id, 'post', 'validate', listener);
},
/**
* fire event
* @param evt {{type:string, time:string, id:string}} and more properties depend on event type
*/
fireEvent: function (evt) {
// copy listeners array is very important.
// since change will invoke validate by FormCell,
// then validate will invoke forceUpdate,
// then forceUpdate will invoke remove listener,
// finally the original listeners are changed,
// so maybe some listener will not be invoked.
// this bug was found the relationship of 2 select2 component.
var listenersCopy = [];
// find listeners which defined on property
var key = evt.id + "-" + evt.time + "-" + evt.type;
var listeners = this.__listeners ? this.__listeners[key] : null;
if (listeners != null && listeners.length != 0) {
listenersCopy.push.apply(listenersCopy, listeners);
}
// find listeners which monitor all
key = '--all-' + evt.time + "-" + evt.type;
listeners = this.__listeners ? this.__listeners[key] : null;
if (listeners != null && listeners.length != 0) {
listenersCopy.push.apply(listenersCopy, listeners);
}
// find listeners which defined as regexp
key = evt.time + '-' + evt.type;
listeners = this.__regExpListeners ? this.__regExpListeners[key] : null;
if (listeners != null && listeners.length != 0) {
listeners.forEach(function (item) {
if (evt.id.match(item.regexp)) {
listenersCopy.push.apply(listenersCopy, [item.listener]);
}
});
}
var _this = this;
listenersCopy.forEach(function (listener) {
listener.call(_this, evt);
});
return this;
},
firePostChangeEvent: function (id, _old, _new) {
return this.fireEvent({
model: this,
id: id,
old: _old,
"new": _new ? _new : this.get(id),
time: "post",
type: "change"
});
},
firePostAddEvent: function (id, index) {
var array = this.get(id);
return this.fireEvent({
model: this,
id: id,
array: array,
index: index,
old: null,
"new": array[index],
type: "add",
time: "post"
});
},
firePostRemoveEvent: function (id, _old, index) {
return this.fireEvent({
model: this,
id: id,
array: this.get(id),
index: index,
old: _old,
"new": null,
type: "remove",
time: "post"
});
},
firePostValidateEvent: function (id) {
return this.fireEvent({
model: this,
id: id,
time: 'post',
type: 'validate'
});
},
/**
* reset model
*/
reset: function () {
this.__model = $pt.mergeObject({ deep: true, sources: this.__base }); //$pt.cloneJSON(this.__base);
this.__validateResults = {};
this.__changed = false;
return this;
},
/**
* clear validate results
* @param id {string} property id
*/
clearValidateResults: function (id) {
if (id) {
delete this.__validateResults[id];
} else {
this.__validateResults = {};
}
return this;
},
/**
* validate by phase
* @param phase
* @param id
*/
validateByPhase: function (phase, id) {
var validator = this.getValidator();
if (validator === undefined || validator === null) {
// do nothing
return this;
}
if (id) {
var ret = validator.validateByPhase(this, phase, id);
if (ret !== true) {
this.__validateResults[id] = ret;
} else {
delete this.__validateResults[id];
}
this.__mergeErrorToParent().fireEvent({
model: this,
id: id,
time: "post",
type: "validate"
});
} else {
this.__validateResults = validator.validateByPhase(this, phase);
this.__mergeErrorToParent();
var _this = this;
Object.keys(this.__validateResults ? this.__validateResults : {}).forEach(function (id) {
_this.fireEvent({
model: _this,
id: id,
time: "post",
type: "validate"
});
});
}
return this;
},
__mergeErrorToParent: function () {
if (this.__parent == null) {
return this;
}
var parentModel = this.__parent.getCurrentModel();
Object.keys(parentModel).forEach(function (key) {
var value = parentModel[key];
if (value == this.__model) {
// console.log('Regular Value');
this.__parent.mergeError(this.hasError() ? this.getError() : null, key);
} else if (Array.isArray(value)) {
// console.log('Array Value');
value.forEach(function (elm) {
// console.log(elm, this.__model, elm == this.__model);
if (elm == this.__model) {
var errors = this.__parent.getError(key);
var tableErrorIndex = errors ? errors.findIndex(function (error) {
return typeof error != 'string';
}) : -1;
if (this.hasError()) {
// has error
var tableError = null;
if (tableErrorIndex == -1) {
tableError = $pt.createTableValidationResult();
if (!errors) {
errors = [];
}
errors.push(tableError);
} else {
tableError = errors[tableErrorIndex];
tableError.remove(elm);
}
tableError.push(elm, this.__validateResults);
this.__parent.mergeError(errors, key);
// console.log(this.__parent.getError());
} else {
// no error
if (tableErrorIndex != -1) {
// remove the exists error
errors[tableErrorIndex].remove(elm);
}
}
}
}.bind(this));
}
}.bind(this));
this.__parent.__mergeErrorToParent();
return this;
},
/**
* validate value of give property id, or whole model if no parameter passed.
* will fire post-validate event for each property after validation
* @param id {string} property id
*/
validate: function (id) {
return this.validateByPhase(null, id);
},
/**
* check the property is required or not
* @param id {string} property id
* @returns {boolean}
*/
isRequired: function (id, phase) {
var validator = this.getValidator();
return validator != null && validator.isRequired(id, phase);
},
/**
* get error of given id, or return all error when no parameter passed
* @param id {string} property id
* @returns {*}
*/
getError: function (id) {
if (id) {
// property level
return this.__validateResults[id];
} else {
return this.__validateResults;
}
},
/**
* check the model is has error or not, check property only when id appointed,
* or check whole model when no parameter passed.
* @returns {boolean}
*/
hasError: function (id) {
if (id) {
// property level
return this.__validateResults[id] != null;
} else {
return Object.keys(this.__validateResults).length != 0;
}
},
mergeError: function (error, id) {
if (id) {
if (error) {
this.__validateResults[id] = error;
} else {
delete this.__validateResults[id];
}
} else {
this.__validateResults = error ? error : {};
}
return this;
},
/**
* remove row from given id
* @param id {string} property id
* @param row {{}} row data
*/
remove: function (id, row) {
var data = this.get(id);
if (data == null || data.length == 0) {
return this;
}
var index = data.findIndex(function (item) {
return item === row;
});
if (index != -1) {
data.splice(index, 1);
this.__changed = true;
this.fireEvent({
model: this,
id: id,
array: data, // after remove
index: index,
old: row,
"new": null,
type: "remove",
time: "post"
});
}
return this;
},
/**
* add row into array by given id
* @param id {string} property id
* @param row {{}} row data
*/
add: function (id, row, index) {
var data = this.get(id);
if (data == null) {
data = [];
// do not call #set, since #set will invoke event
this.__model[id] = data;
}
var findIndex = data.findIndex(function (item) {
return item === row;
});
if (findIndex == -1) {
if (index == null) {
data.push(row);
index = data.length - 1;
} else {
if (index >= data.length) {
index = data.length;
}
data.splice(index, 0, row);
}
this.__changed = true;
this.fireEvent({
model: this,
id: id,
array: data, // after add
index: index,
old: null,
"new": row,
type: "add",
time: "post"
});
}
return this;
},
/**
* update new item instead of old item
* @param id {string} property id
* @param _old {{}} old row data
* @param _new {{}} new row data
*/
update: function (id, _old, _new) {
var data = this.get(id);
if (data == null) {
return this;
}
var index = data.findIndex(function (item) {
return item === _old;
});
if (index != -1) {
data.splice(index, 1, _new);
this.__changed = true;
this.fireEvent({
model: this,
id: id,
array: data, // after update
index: index,
old: _old,
"new": _new,
type: "change",
time: "post"
});
}
return this;
}
};
/**
* create model
* @param jsonObjects {*|*[]}
* @param validator {ModelValidator}
*/
$pt.createModel = function (jsonObjects, validator) {
// create model
var inputModel = null;
if (Array.isArray(jsonObjects)) {
// merge all json objects into one
inputModel = {};
$.extend.apply($, [true, inputModel].concat(jsonObjects));
} else {
inputModel = jsonObjects == null ? {} : jsonObjects;
}
var model = {};
if ($pt.BUILD_PROPERTY_VISITOR) {
// create getter and setter
Object.keys(inputModel).forEach(function (key) {
var name = key.upperFirst();
model['get' + name] = function () {
return this.get(key);
};
model['set' + name] = function (value) {
this.set(key, value);
};
});
}
var ModelClass = jsface.Class($.extend(model, ModelInterface));
return new ModelClass(inputModel, validator);
};
})(window, jQuery, moment, jsface);
(function (window, $, jsface, React) {
var $pt = window.$pt;
if ($pt == null) {
$pt = {};
window.$pt = $pt;
}
/**
* cell layout
* @type {class}
*/
var CellLayout = jsface.Class({
$static: {
DEFAULT_POSITION: {},
DEFAULT_ROW: 9999,
DEFAULT_COLUMN: 9999,
DEFAULT_WIDTH: 3,
DEFAULT_COMPONENT: {
type: $pt.ComponentConstants.Text
}
},
/**
* construct cell layout
* @param id {string} property id or fake id
* @param cell {{label: string,
* dataId: string,
* comp:{
* type: string|{type: string, label: boolean, popover: boolean}
* relatedDataId: string|string[]
* },
* css:{
* cell: string,
* comp: string
* },
* pos:{row: number, col: number, width: number, section: string, card: string}
* }}
*/
constructor: function (id, cell) {
this.__id = id;
// check if the cell definition is referenced by pre-definition
if (cell.base) {
if (Array.isArray(cell.base)) {
cell = $pt.mergeObject({ deep: true, target: {}, sources: cell.base.concat(cell) });
} else {
cell = $pt.mergeObject({ deep: true, target: {}, sources: [cell.base, cell] });
}
}
this.__dataId = cell.dataId ? cell.dataId : this.__id;
this.__cell = cell;
},
unwrapValueWhenIsAFunc: function (value, forceWrap, delegate) {
if (typeof value === 'function') {
if (forceWrap || value.wrap === true) {
return value.call(delegate ? delegate : this);
} else {
return value;
}
} else {
return value;
}
},
/**
* get definition json
* @returns {*}
*/
getDefinition: function () {
return this.__cell;
},
/**
* get id
* @returns {string}
*/
getId: function () {
return this.__id;
},
/**
* get data id.
* data id can be given by 'dataId' key
* @returns {string}
*/
getDataId: function () {
return this.__dataId;
},
/**
* get position
* @returns {*}
* @private
*/
getPosition: function () {
return this.__cell.pos ? this.__cell.pos : CellLayout.DEFAULT_POSITION;
},
/**
* get row index
* @returns {string}
*/
getRowIndex: function () {
var row = this.getPosition().row;
return row == null ? CellLayout.DEFAULT_ROW : row;
},
/**
* get column index
* @returns {Array|string|boolean|*}
*/
getColumnIndex: function () {
var col = this.getPosition().col;
return col == null ? CellLayout.DEFAULT_COLUMN : col;
},
/**
* get width of cell, default is 3
* @returns {number}
*/
getWidth: function () {
var width = this.getPosition().width;
return width == null ? CellLayout.DEFAULT_WIDTH : width;
},
/**
* get section
* @returns {string}
*/
getSection: function () {
var section = this.getPosition().section;
return section != null ? section : SectionLayout.DEFAULT_KEY;
},
/**
* get card
* @returns {string}
*/
getCard: function () {
var card = this.getPosition().card;
return card != null ? card : CardLayout.DEFAULT_KEY;
},
/**
* get component type
* @returns {string}
*/
getComponentType: function () {
var type = this.getComponentOption("type");
type = type == null ? $pt.ComponentConstants.TextInJSON : type;
return typeof type === "string" ? { type: type, label: true, popover: true } : type;
},
/**
* get component option by given key, return null when not defined
* @param key optional, return all options if parameter not passed
* @param defaultValue optional, only effective when key passed
* @returns {*}
*/
getComponentOption: function (key, defaultValue, delegate) {
if (key) {
// key passed
// set default value as null if not passed
if (defaultValue === undefined) {
defaultValue = null;
}
if (this.__cell.comp) {
// comp defined
var option = this.__cell.comp[key];
// not defined with given key, use default value instead
option = this.unwrapValueWhenIsAFunc(option, false, delegate);
return option === undefined ? defaultValue : option;
} else {
// comp not defined, use default value instead
return defaultValue;
}
}
// no parameter passed, return comp definition
return !this.__cell.comp ? {} : this.__cell.comp;
},
/**
* get label
* @returns {string}
*/
getLabel: function (delegate) {
return this.unwrapValueWhenIsAFunc(this.__cell.label, true, delegate);
},
/**
* get label CSS, if not defined, return original CSS
* @param originalCSS optional
* @returns {string}
*/
getLabelCSS: function (originalCSS) {
return this.getAdditionalCSS("label", originalCSS);
},
/**
* get cell CSS, if not defined, return original CSS
* @param originalCSS optional
* @returns {string}
*/
getCellCSS: function (originalCSS) {
return this.getAdditionalCSS("cell", originalCSS);
},
/**
* get component css, if not defined, return original CSS
* @param originalCSS
* @returns {string}
*/
getComponentCSS: function (originalCSS) {
return this.getAdditionalCSS('comp', originalCSS);
},
/**
* is additional css defined
* @param key optional
* @returns {boolean}
*/
isAdditionalCSSDefined: function (key) {
if (key) {
return this.__cell.css != null && this.__cell.css[key] != null;
}
return this.__cell.css != null;
},
/**
* get additional css object, return {} when not defined
* @param key optional, return string or empty string(not defined) when passed this parameter
* @param originalCSS optional, combine with additional CSS if exists
* @returns {*|string}
*/
getAdditionalCSS: function (key, originalCSS) {
if (key) {
var additionalCSS = this.isAdditionalCSSDefined(key) ? this.__cell.css[key] : '';
var cssList = additionalCSS ? additionalCSS.split(' ') : [];
var css = {};
cssList.forEach(function (cssClassName) {
if (cssClassName && !cssClassName.isBlank()) {
css[cssClassName.trim()] = true;
}
});
if (originalCSS != null && !originalCSS.isBlank()) {
css[originalCSS.trim()] = true;
}
return $pt.LayoutHelper.classSet(css);
}
return this.isAdditionalCSSDefined() ? this.__cell.css : {};
},
/**
* get event monitor
* @param key optional, return event function or null (not defined) when passed this parameter.
* @returns {function|*}
*/
getEventMonitor: function (key) {
if (key) {
if (this.__cell && this.__cell.evt) {
var monitor = this.__cell.evt[key];
if (monitor) {
return monitor;
} else {
var name = Object.keys(this.__cell.evt).find(function (name) {
return name.toLowerCase() === key.toLowerCase();
});
if (name) {
return this.__cell.evt[name];
}
}
} else {
return null;
}
} else {
return !this.__cell.evt ? {} : this.__cell.evt;
}
},
/**
* get validate phase
*/
getValidationPhase: function () {
if (this.__cell && this.__cell.validate) {
return this.transformValidationPhase(this.__cell.validate);
} else {
return null;
}
},
transformValidationPhase: function (phase) {
if (phase == null || typeof phase === 'string' || typeof phase === 'function') {
return phase;
} else if (phase.phase) {
// it must be a json object
return this.transformValidationPhase(phase.phase);
} else {
// no phase defined
return null;
}
},
getValidationOption: function (key, defaultValue) {
if (key === 'phase') {
return this.getValidationPhase();
} else {
if (this.__cell && this.__cell.validate) {
var define = this.__cell.validate;
if (typeof define === 'string' || typeof define === 'function') {
// only define the phase, see method transformValidationPhase
return defaultValue;
} else {
// definition must be a JSON object, and returns the delay property
return typeof define[key] === 'undefined' ? defaultValue : define[key];
}
} else {
// no validate part defined
return defaultValue;
}
}
}
});
/**
* create cell layout
* @param id {string} property id
* @param cell {{}} cell definition
* @returns {CellLayout}
*/
$pt.createCellLayout = function (id, cell) {
return new CellLayout(id, cell);
};
/**
* row layout
* @type {class}
*/
var RowLayout = jsface.Class({
constructor: function (rowIndex) {
this.__rowIndex = rowIndex;
},
/**
* get row index
* @returns {number}
*/
getRowIndex: function () {
return this.__rowIndex;
},
/**
* add cell
*/
addCell: function (cell) {
if (this.__cells === undefined) {
this.__cells = [];
}
var index = this.__cells.findIndex(function (element) {
return element.getId() == cell.getId();
});
if (index == -1) {
// not found, simply push into array
this.__cells.push(cell);
} else {
// found, remove the original one, replace with new one
this.__cells.splice(index, 1, cell);
}
this.__cells.sort(function (c1, c2) {
return c1.getColumnIndex() - c2.getColumnIndex();
});
return this;
},
/**
* get cells
* @returns {[CellLayout]}
*/
getCells: function () {
return this.__cells;
}
});
/**
* create row layout
* @param rowIndex {number} row index
* @param cells {CellLayout|CellLayout[]} optional, cells of this row
* @returns {class}
*/
$pt.createRowLayout = function (rowIndex, cells) {
var layout = new RowLayout(rowIndex);
if (cells) {
if (Array.isArray(cells)) {
cells.forEach(function (cell) {
layout.addCell(cell);
});
} else {
layout.addCell(cells);
}
}
return layout;
};
/**
* section layout
* @type {class}
*/
var SectionLayout = jsface.Class({
$static: {
DEFAULT_KEY: '_defaultSection',
DEFAULT_ROW_INDEX: 9999,
DEFAULT_COLUMN_INDEX: 9999,
DEFAULT_WIDTH: 12
},
/**
* construct section layout.
* @param section {{label:string,
* collapsible: boolean,
* expanded: boolean,
* row: number,
* col: number,
* width: number,
* layout: {}}}
* @param key {string} id of section layout
* @param parentCard {CardLayout} card where section located
*/
constructor: function (section, key, parentCard) {
// layout definition
this.__layout = {};
var _this = this;
if (section == null) {
section = {};
}
Object.keys(section).forEach(function (key) {
if (key != 'layout') {
_this.__layout[key] = section[key];
}
});
this.__id = key;
this.__parent = parentCard;
// all cells map
this.__all = {};
this.__rows = {};
var sectionLayouts = section.layout;
if (sectionLayouts) {
Object.keys(sectionLayouts).forEach(function (key) {
if (sectionLayouts[key].getCellCSS) {
// already be CellLayout
_this.addCell(sectionLayouts[key]);
} else {
_this.addCell(new CellLayout(key, sectionLayouts[key]));
}
});
}
},
hasCell: function () {
return Object.keys(this.__all).length != 0;
},
/**
* push cell into section.
* auto create RowLayout
* @param cell {CellLayout}
*/
addCell: function (cell) {
this.__all[cell.getId()] = cell;
var rowIndex = cell.getRowIndex();
var rowLayout = this.__rows[rowIndex];
if (rowLayout === undefined) {
// initialize row layout
rowLayout = $pt.createRowLayout(rowIndex);
this.__rows[rowIndex] = rowLayout;
}
rowLayout.addCell(cell);
this.__sortRows();
return this;
},
/**
* sort rows in section
* @private
*/
__sortRows: function () {
this.__rowsArray = [];
var rowsArray = this.__rowsArray;
var rows = this.__rows;
Object.keys(rows).forEach(function (key) {
rowsArray.push(rows[key]);
});
// sort
rowsArray.sort(function (r1, r2) {
return r1.getRowIndex() - r2.getRowIndex();
});
},
/**
* get row index of section, default 9999
* @return {number}
*/
getRowIndex: function () {
if (this.__layout == null || this.__layout.row == null) {
return SectionLayout.DEFAULT_ROW_INDEX;
} else {
return this.__layout.row;
}
},
/**
* get column index of section, default 9999
* @return {number}
*/
getColumnIndex: function () {
if (this.__layout == null || this.__layout.col == null) {
return SectionLayout.DEFAULT_COLUMN_INDEX;
} else {
return this.__layout.col;
}
},
/**
* get width of section, default 12
* @return {number}
*/
getWidth: function () {
if (this.__layout == null || this.__layout.width == null) {
return SectionLayout.DEFAULT_WIDTH;
} else {
return this.__layout.width;
}
},
/**
* get style of section
* @returns {*}
*/
getStyle: function () {
if (this.__layout == null || this.__layout.style == null) {
return 'default';
} else {
return this.__layout.style;
}
},
getCSS: function () {
if (this.__layout == null || this.__layout.css == null) {
return null;
} else {
return this.__layout.css;
}
},
/**
* get label of section
* @returns {*}
*/
getLabel: function () {
if (this.__layout == null || this.__layout.label == null) {
return null;
} else {
return this.__layout.label;
}
},
/**
* check the section is collapsible or not
* @returns {boolean}
*/
isCollapsible: function () {
if (this.__layout == null || this.__layout.collapsible == null) {
return false;
} else {
return this.__layout.collapsible === true;
}
},
getCollapsedLabel: function () {
if (this.__layout == null || this.__layout.collapsedLabel == null) {
return false;
} else {
return this.__layout.collapsedLabel;
}
},
/**
* check section is default expanded or not
* @returns {boolean}
*/
isExpanded: function () {
if (this.__layout == null || this.__layout.expanded == null) {
return true;
} else {
return this.__layout.expanded !== false;
}
},
getExpandedLabel: function () {
if (this.__layout == null || this.__layout.expandedLabel == null) {
return null;
} else {
return this.__layout.expandedLabel;
}
},
/**
* get check box in title definition
* @returns {{}}
*/
hasCheckInTitle: function () {
return this.__layout && this.__layout.checkInTitle != null;
},
getCheckInTitleValue: function (model) {
var id = this.getCheckInTitleDataId();
return id ? model.get(id) : null;
},
getCheckInTitleDataId: function () {
if (!this.hasCheckInTitle()) {
return null;
}
var checkInTitle = this.__layout.checkInTitle;
return checkInTitle ? checkInTitle.data : null;
},
getCheckInTitleLabel: function () {
if (!this.hasCheckInTitle()) {
return null;
}
var checkInTitle = this.__layout.checkInTitle;
return checkInTitle ? checkInTitle.label : null;
},
getCheckInTitleCollapsible: function () {
if (!this.hasCheckInTitle()) {
return null;
}
var checkInTitle = this.__layout.checkInTitle;
return checkInTitle ? checkInTitle.collapsible : null;
},
getCheckInTitleOption: function () {
if (!this.hasCheckInTitle()) {
return null;
}
var options = $.extend({}, this.__layout.checkInTitle);
delete options.collapsible;
delete options.label;
delete options.data;
return options;
},
getVisible: function () {
return this.__layout.visible;
},
/**
* get id of section
* @returns {string}
*/
getId: function () {
return this.__id;
},
/**
* get all rows
* @returns {RowLayout[]}
*/
getRows: function () {
return this.__rowsArray;
},
/**
* get cell layout by given id
* @param id {string} id of cell layout
* @returns {CellLayout}
*/
getCell: function (id) {
return this.__all[id];
},
/**
* get all cells
* @returns {{}}
*/
getCells: function () {
return this.__all;
},
/**
* get parent card
* @returns {CardLayout}
*/
getParentCard: function () {
return this.__parent;
}
});
/**
* create section layout
* @param settings
* @param key
* @param parentCard
* @returns {SectionLayout}
*/
$pt.createSectionLayout = function (settings, key, parentCard) {
return new SectionLayout(settings, key, parentCard);
};
/**
* card layout
* @type {class}
*/
var CardLayout = jsface.Class({
$static: {
DEFAULT_KEY: '_defaultCard',
DEFAULT_CARD_INDEX: 9999
},
/**
* construct card layout
* @param card {{
* _sections: {},
* index: number,
* label: string,
* icon: string,
* badge: string,
* badgeRender: function,
* active: boolean,
* backable: boolean,
* finishButton: {}
* rightButtons: {}|{}[],
* leftButtons: {}|{}[]}}
* @param key {string} id of card
*/
constructor: function (card, key) {
var _this = this;
// layout definition
this.__layout = $.extend({}, card);
this.__id = key;
// all cells map
this.__all = {};
// all sections
this.__sections = {};
if (this.__layout._sections) {
Object.keys(this.__layout._sections).forEach(function (sectionKey) {
var section = $pt.createSectionLayout(_this.__layout._sections[sectionKey], sectionKey, _this);
if (section.hasCell()) {
_this.__sections[sectionKey] = section;
$.extend(_this.__all, section.getCells());
_this.__sortSections();
}
});
}
},
hasCell: function () {
return Object.keys(this.__all).length != 0;
},
/**
* push cell to card
* @param cell {CellLayout}
*/
addCell: function (cell) {
this.__all[cell.getId()] = cell;
// find section and push cell into section
var sectionKey = cell.getSection();
var sectionLayout = this.__sections[sectionKey];
if (sectionLayout == null) {
var sectionDefine = this.__layout._sections ? this.__layout._sections[sectionKey] : null;
sectionLayout = $pt.createSectionLayout(sectionDefine, sectionKey, this);
this.__sections[sectionKey] = sectionLayout;
}
sectionLayout.addCell(cell);
this.__sortSections();
return this;
},
/**
* sort sections
* @private
*/
__sortSections: function () {
this.__sectionsArray = [];
var sectionsArray = this.__sectionsArray;
var sections = this.__sections;
Object.keys(sections).forEach(function (key) {
sectionsArray.push(sections[key]);
});
// sort
sectionsArray.sort(function (s1, s2) {
var r1 = s1.getRowIndex();
var r2 = s2.getRowIndex();
if (r1 == r2) {
return s1.getColumnIndex() - s2.getColumnIndex();
} else {
return r1 - r2;
}
});
},
/**
* get card index
* @returns {number}
*/
getIndex: function () {
if (this.__layout.index == null) {
return CardLayout.DEFAULT_CARD_INDEX;
} else {
return this.__layout.index;
}
},
/**
* get label of card
* @returns {string}
*/
getLabel: function () {
return this.__layout.label;
},
/**
* get icon of card
* @returns {string}
*/
getIcon: function () {
return this.__layout.icon;
},
/**
* has badge icon or not
* @returns {boolean}
*/
hasBadge: function () {
return this.__layout.badge != null;
},
/**
* get badge icon dependency id
* @returns {string}
*/
getBadgeId: function () {
return this.__layout.badge;
},
getBadgeRender: function () {
return this.__layout.badgeRender;
},
/**
* get id of card
* @returns {*}
*/
getId: function () {
return this.__id;
},
/**
* check card is default active or not
* @returns {boolean}
*/
isActive: function () {
return this.__layout.active === true;
},
/**
* set card to be active
* @param active
*/
setActive: function (active) {
this.__layout.active = active;
return this;
},
/**
* check the card can be backable or not
* @returns {boolean}
*/
isBackable: function () {
return this.__layout.backable !== false;
},
/**
* get right buttons
* @returns {{}|{}[]}
*/
getRightButtons: function () {
return this.__layout.rightButtons ? this.__layout.rightButtons : [];
},
/**
* get left buttons
* @returns {{}|{}[]}
*/
getLeftButtons: function () {
return this.__layout.leftButtons ? this.__layout.leftButtons : [];
},
/**
* get finish button definition
* @returns {{}}
*/
getFinishButton: function () {
return this.__layout.finishButton;
},
/**
* get sections
* @return {SectionLayout[]}
*/
getSections: function () {
return this.__sectionsArray;
},
/**
* get all cells
* @returns {{}}
*/
getCells: function () {
return this.__all;
},
/**
* get cell by given cell id
* @param cellId
* @returns {CellLayout}
*/
getCell: function (cellId) {
return this.__all[cellId];
}
});
/**
* create card layout
* @param card {{}}
* @param key {string} id of card
* @returns {CardLayout}
*/
$pt.createCardLayout = function (card, key) {
return new CardLayout(card, key);
};
/**
* form layout
* @type {class}
*/
var FormLayout = jsface.Class({
/**
* constructor of FormLayout, accepts one or more json object
* @param layouts {{_freeCard: boolean,
* _cardButtonShown: boolean}|{}[]}
*/
constructor: function (layouts) {
// all cells map
this.__all = {};
var layout = this.__mergeLayouts(layouts);
var cards = this.__createDefaultCard(layout);
delete layout._cards;
delete layout._freeCard;
delete layout._cardButtonShown;
delete layout._sections;
var cardLayouts = this.__readCells(layout, cards, this.__all);
this.__sortCards(cardLayouts);
},
/**
* merge layouts to one
* @param layouts {{}[]}
* @return {{}}
* @private
*/
__mergeLayouts: function (layouts) {
if (layouts.length == 1) {
return $.extend(true, {}, layouts[0]);
} else {
return $.extend.apply($, [].concat(true, {}, layouts));
}
},
/**
* create default card and default section
* @param layout {{}}
* @return {{}}
* @private
*/
__createDefaultCard: function (layout) {
var cards = layout._cards;
this.__freeCard = layout._freeCard;
this.__cardButtonShown = layout._cardButtonShown;
if (cards == null) {
cards = {};
// create default card
cards[CardLayout.DEFAULT_KEY] = { _sections: {} };
// all sections in default card
if (layout._sections == null) {
// no section defined, create default
cards[CardLayout.DEFAULT_KEY]._sections[SectionLayout.DEFAULT_KEY] = {};
} else {
// use defined
cards[CardLayout.DEFAULT_KEY]._sections = layout._sections;
}
}
return cards;
},
/**
* read cells
* @param layout {{}} layout definition
* @param cards {{}} cards definition
* @param all {{}} all cells
* @returns {{}}
* @private
*/
__readCells: function (layout, cards, all) {
// all cards
var cardLayouts = {};
// go through the cards definitions
Object.keys(cards).forEach(function (key) {
var card = new CardLayout(cards[key], key);
if (card.hasCell()) {
cardLayouts[key] = card;
$.extend(all, card.getCells());
}
});
// go through the cell definitions
Object.keys(layout).forEach(function (key) {
var cell = new CellLayout(key, layout[key]);
all[cell.getId()] = cell;
// find card and push cell into card
var cardKey = cell.getCard();
var cardLayout = cardLayouts[cardKey];
if (cardLayout == null) {
var cardDefine = cards[cardKey];
cardLayout = new CardLayout(cardDefine, cardKey);
cardLayouts[cardKey] = cardLayout;
}
cardLayout.addCell(cell);
});
return cardLayouts;
},
/**
* sort cards
* @param cards
* @private
*/
__sortCards: function (cards) {
// sort cards
this.__cardsArray = [];
var cardsArray = this.__cardsArray;
Object.keys(cards).forEach(function (key) {
cardsArray.push(cards[key]);
});
this.__cardsArray.sort(function (c1, c2) {
return c1.getIndex() - c2.getIndex();
});
},
/**
* get all cards
* @returns {CardLayout[]}
*/
getCards: function () {
return this.__cardsArray;
},
/**
* is free card or not
* @returns {boolean}
*/
isFreeCard: function () {
return this.__freeCard === true;
},
/**
* is card button shown or not
* @returns {boolean}
*/
isCardButtonShown: function () {
return this.__cardButtonShown !== false;
},
/**
* get cell layout by given id
* @param id {string} id of cell
* @returns {CellLayout}
*/
getCell: function (id) {
return this.__all[id];
},
/**
* get all cells
* @returns {{}}
*/
getCells: function () {
return this.__all;
}
});
/**
* create form layout
* @param layouts {{}|{}[]}
* @returns {FormLayout}
*/
$pt.createFormLayout = function (layouts) {
return new FormLayout(Array.prototype.slice.call(arguments));
};
/**
* table column layout, an array like object
* @type {class}
*/
var TableColumnLayout = jsface.Class({
/**
*
* @param columns {{
* title: string,
* width: number,
* data: string,
* codes: CodeTable,
* render: function,
* sort: boolean|string|function}[]}
*/
constructor: function (columns) {
this.__columns = columns.slice(0);
},
/**
* get all columns
* @returns {{}[]}
*/
columns: function () {
return this.__columns;
},
/**
* get column definition of given column index
* @param index {number}
* @returns {{}}
*/
get: function (index) {
return this.__columns[index];
},
/**
* push new column definition to columns
* @param column {{}}
*/
push: function (column) {
this.__columns.push(column);
},
/**
* splice, same as array
* @param index {number}
* @param removeCount {number} remove column count
* @param newItem {{}} new column definition
*/
splice: function (index, removeCount, newItem) {
this.__columns.splice(index, removeCount, newItem);
},
/**
* same as array
* @param func {function} same as Array.map
* @returns {[]}
*/
map: function (func) {
return this.__columns.map(func);
},
/**
* same as array
* @param func {function} same as Array.forEach
*/
forEach: function (func) {
this.__columns.forEach(func);
},
/**
* get column count
* @returns {number}
*/
length: function () {
return this.__columns.length;
},
/**
* same as array
* @param func {function} same as Array.some
* @returns {boolean}
*/
some: function (func) {
return this.__columns.some(func);
}
});
/**
* create table column layout
* @param columns {{}[]}
* @returns {TableColumnLayout}
*/
$pt.createTableColumnLayout = function (columns) {
return new TableColumnLayout(columns);
};
// component
/**
* Component Base
* @type {*}
*/
var ComponentBase = {
// react methods
getInitialState: function () {
return {};
},
executePointcutBefore: function (pointcut) {
if (pointcut && pointcut.before) {
pointcut.before.apply(this, Array.prototype.slice.call(arguments, 1));
}
},
executePointcutAfter: function (pointcut) {
if (pointcut && pointcut.after) {
pointcut.after.apply(this, Array.prototype.slice.call(arguments, 1));
}
},
installBaseMonitors: function () {
this.executePointcutBefore.apply(this, arguments);
// add post change listener to handle model change
this.addPostChangeListener(this.onModelChanged);
if (this.isArrayData && this.isArrayData()) {
this.addPostAddListener(this.onModelChanged);
this.addPostRemoveListener(this.onModelChanged);
}
this.addPostValidateListener(this.onModelValidateChanged);
if (this.getDependencyOptions) {
var options = this.getDependencyOptions();
if (options) {
this.addDependencyMonitor(this.getDependencies(options));
}
}
this.addEnableDependencyMonitor();
this.addVisibleDependencyMonitor();
this.registerToComponentCentral();
this.executePointcutAfter.apply(this, arguments);
},
uninstallBaseMonitors: function () {
this.executePointcutBefore.apply(this, arguments);
// remove post change listener to handle model change
this.removePostChangeListener(this.onModelChanged);
if (this.isArrayData && this.isArrayData()) {
this.removePostAddListener(this.onModelChanged);
this.removePostRemoveListener(this.onModelChanged);
}
this.removePostValidateListener(this.onModelValidateChanged);
if (this.getDependencyOptions) {
var options = this.getDependencyOptions();
if (options) {
this.removeDependencyMonitor(this.getDependencies(options));
}
}
this.removeEnableDependencyMonitor();
this.removeVisibleDependencyMonitor();
this.unregisterFromComponentCentral();
this.executePointcutAfter.apply(this, arguments);
},
componentWillUpdate: function (nextProps, nextState) {
this.uninstallBaseMonitors({
before: this.beforeWillUpdate,
after: this.afterWillUpdate
}, nextProps, nextState);
},
componentDidUpdate: function (prevProps, prevState) {
this.installBaseMonitors({
before: this.beforeDidUpdate,
after: this.afterDidUpdate
}, prevProps, prevState);
},
componentDidMount: function () {
this.installBaseMonitors({
before: this.beforeDidMount,
after: this.afterDidMount
});
},
componentWillUnmount: function () {
this.uninstallBaseMonitors({
before: this.beforeWillUnmount,
after: this.afterWillUnmount
});
},
onModelChanged: function () {
this.forceUpdate();
},
onModelValidateChanged: function (evt) {
this.forceUpdate();
},
// customized methods
/**
* get model
* @returns {ModelInterface}
*/
getModel: function () {
if (this.useFormModel()) {
return this.getFormModel();
} else {
return this.getInnerModel();
}
},
/**
* use form model when the component inner data model is given
* @returns {boolean}
*/
useFormModel: function () {
return this.getComponentOption('useFormModel') === true;
},
/**
* get form model
* @returns {ModelInterface}
*/
getFormModel: function () {
return this.props.model;
},
/**
* get inner data model, return form model if not defined
* @returns {ModelInterface}
*/
getInnerModel: function () {
var model = this.getComponentOption('model');
return model ? model : this.getFormModel();
},
/**
* get value from model
* @returns {*}
*/
getValueFromModel: function () {
return this.getModel().get(this.getDataId());
},
/**
* set value to model
* @param value
*/
setValueToModel: function (value) {
this.getModel().set(this.getDataId(), value);
},
/**
* get layout
* @returns {CellLayout}
*/
getLayout: function () {
return this.props.layout;
},
/**
* component is view mode or not
* first check "state.isViewMode"
* second check "props.view"
* last check the componen opion "view"
* returns value of "state.isViewMode" has value.
* otherwise returns true when one of "props.view" and component option "view" is true
* @returns {boolean}
*/
isViewMode: function () {
var isViewMode = this.state ? this.state.isViewMode : null;
if (isViewMode == null) {
return this.props.view === true || this.getComponentOption('view') === true;
} else {
return isViewMode;
}
},
setViewMode: function (isViewMode) {
this.setState({ isViewMode: isViewMode });
},
/**
* render in view mode. default render as a label.
* @returns {XML}
*/
renderInViewMode: function () {
var externalViewModeRenderer = $pt.LayoutHelper.getComponentViewModeRenderer(this.getLayout().getComponentType());
if (externalViewModeRenderer) {
return externalViewModeRenderer.call(this, this.getModel(), this.getLayout(), this.props.direction, true);
}
var label = null;
if (this.getTextInViewMode) {
label = this.getTextInViewMode();
} else {
label = this.getValueFromModel();
}
var labelLayout = $pt.createCellLayout(this.getId(), $.extend(true, {}, {
comp: this.getComponentOption(),
// view css
css: this.getAdditionalCSS('view')
// pos, dataId, evt are all not necessary, since label will not use.
}, {
label: label,
dataId: this.getDataId(),
comp: {
type: $pt.ComponentConstants.Label,
textFromModel: false
}
}));
var parameters = $pt.LayoutHelper.transformParameters(this.getModel(), labelLayout, this.props.direction, true);
parameters.ref = 'viewLabel';
return React.createElement($pt.Components.NLabel, parameters);
},
/**
* get id of component
* @returns {string}
*/
getId: function () {
return this.getLayout().getId();
},
/**
* get data id of component
* @returns {*}
*/
getDataId: function () {
return this.getLayout().getDataId();
},
/**
* get component css
* @param originalCSS original CSS
* @returns {string}
*/
getComponentCSS: function (originalCSS) {
return this.getLayout().getComponentCSS(originalCSS);
},
/**
* get combine css
* @param originalCSS css class names
* @param additionalKey key of additional css in layout
* @returns {string}
*/
getAdditionalCSS: function (additionalKey, originalCSS) {
return this.getLayout().getAdditionalCSS(additionalKey, originalCSS);
},
/**
* get option
* @param key
* @param defaultValue
*/
getComponentOption: function (key, defaultValue) {
// pass delegate to CellLayout#getComponentOption
var option = this.getLayout().getComponentOption(key, defaultValue, this);
if (option == null && this.props.defaultOptions != null) {
option = this.props.defaultOptions[key];
}
return option === undefined ? null : option;
},
/**
* get id of component central.
*/
getComponentCentralId: function () {
return this.getComponentOption('centralId');
},
/**
* register to component central
*/
registerToComponentCentral: function () {
var id = this.getComponentCentralId();
if (id) {
$pt.LayoutHelper.registerComponent(id, this);
}
},
/**
* unregsiter from component central
*/
unregisterFromComponentCentral: function () {
var id = this.getComponentCentralId();
if (id) {
$pt.LayoutHelper.unregisterComponent(id, this);
}
},
/**
* get event monitor
* @param key {string} event name, if not passed, return whole event definition
* @returns {*}
*/
getEventMonitor: function (key) {
return this.getLayout().getEventMonitor(key);
},
notifyEvent: function (evt) {
var type = evt.type;
var monitor = this.getEventMonitor(type);
if (monitor) {
monitor.call(this, evt);
}
},
/**
* get component rule value.
* get component option by given key. return default value if not defined.
* otherwise call when function and return.
* rule must be defined as {when: func, depends: props}
* @param key
* @param defaultValue
* @returns {*}
*/
getComponentRuleValue: function (key, defaultValue) {
return this.getRuleValue(this.getComponentOption(key), defaultValue);
},
/**
* get rule value. return default value if not defined.
* otherwise call when function and return.
* rule must be defined as {when: func, depends: props}
* @param rule {{when: func, depends: props}}
* @param defaultValue {*}
* @param model {ModelInterface} given model, optional
* @returns {*}
*/
getRuleValue: function (rule, defaultValue, model) {
if (rule === null) {
return defaultValue;
} else if (rule === true || rule === false) {
return rule;
} else {
return rule.when.call(this, model ? model : this.getModel(), this.getValueFromModel());
}
},
/**
* get component rule dependencies.
* rule must be defined as {when: func, depends: props}
* @param key
* @returns {[*]} always return an array, never return null or undefined.
*/
getComponentRuleDependencies: function (key) {
return this.getRuleDependencies(this.getComponentOption(key));
},
/**
* get rule dependencies. rule must be defined as {when: func, depends: props}
* @param dependencies {{when: func, depends: props}}
* @returns {[*]} always return an array, never return null or undefined.
*/
getRuleDependencies: function (dependencies) {
if (dependencies === null || dependencies.depends === undefined || dependencies.depends === null) {
return [];
} else {
if (Array.isArray(dependencies.depends)) {
return dependencies.depends;
} else {
return [dependencies.depends];
}
}
},
/**
* is enabled
* @returns {boolean}
*/
isEnabled: function () {
if (this.isViewMode()) {
// always enabled when in view mode
return true;
}
return this.getComponentRuleValue("enabled", true);
},
/**
* is visible
* @returns {boolean}
*/
isVisible: function () {
var visible = $pt.isVisibleOnAuth(this);
if (visible === false) {
return false;
}
// when the component is not visible
// or declared only view in edit mode
// hide it
visible = this.getComponentRuleValue("visible", true);
if (visible) {
var view = this.getComponentOption('view');
if (this.isViewMode()) {
visible = view == 'edit' != true;
} else if (!this.isViewMode()) {
visible = view == 'view' != true;
}
}
return visible;
},
isMobile: function () {
return $pt.browser.mobile === true;
},
isMobilePhone: function () {
return this.isMobile() && $('body').width() < 768;
},
/**
* is required
* @returns {boolean}
*/
isRequiredSignNeeded: function () {
return this.getComponentRuleValue('required', false);
},
/**
* get dependencies
* @returns {Array|string}
*/
getDependencies: function (attrs) {
var dependencies = [];
if (!Array.isArray(attrs)) {
attrs = [attrs];
}
var _this = this;
attrs.forEach(function (key) {
dependencies.push.apply(dependencies, _this.getComponentRuleDependencies(key));
});
return dependencies;
},
// monitor
addVisibleDependencyMonitor: function () {
this.addDependencyMonitor(this.getDependencies("visible"));
},
addEnableDependencyMonitor: function () {
this.addDependencyMonitor(this.getDependencies("enabled"));
},
removeVisibleDependencyMonitor: function () {
this.removeDependencyMonitor(this.getDependencies("visible"));
},
removeEnableDependencyMonitor: function () {
this.removeDependencyMonitor(this.getDependencies("enabled"));
},
addRequiredDependencyMonitor: function () {
this.addDependencyMonitor(this.getDependencies('required'));
},
removeRequiredDependencyMonitor: function () {
this.removeDependencyMonitor(this.getDependencies('required'));
},
addValidateDependencyMonitor: function () {
this.addDependencyMonitor(this.getDependencies('validation'), this.validate);
},
removeValidateDependencyMonitor: function () {
this.removeDependencyMonitor(this.getDependencies('validation'), this.validate);
},
/**
* validate current cell by given phase
*/
validate: function () {
var phase = this.getLayout().getValidationPhase();
if (typeof phase === 'function') {
phase = phase.call(this, this.getModel(), this.getDataId());
}
if (phase) {
// only validate the given phase
this.getModel().validateByPhase(phase, this.getDataId());
} else {
// no phase defined, validate all
this.getModel().validate(this.getDataId());
}
},
getValidationOption: function (key, defaultValue) {
return this.getLayout().getValidationOption(key, defaultValue);
},
/**
* force update, call react API
* @private
*/
__forceUpdate: function () {
this.forceUpdate();
},
/**
* render normal bottom border
* @returns {XML}
*/
renderNormalLine: function () {
var css = {
disabled: !this.isEnabled()
};
css[this.getAdditionalCSS('normal-line', 'normal-line')] = true;
return React.createElement("hr", { className: $pt.LayoutHelper.classSet(css), ref: "normalLine" });
},
/**
* render focus bottom border
* @returns {XML}
*/
renderFocusLine: function () {
return React.createElement("hr", { className: this.getAdditionalCSS('focus-line', 'focus-line'), ref: "focusLine" });
},
/**
* add dependencies monitor
* @param dependencies {[]}
* @param monitor {function} optional
* @param model {ModelInterface} monitored model, optional
*/
addDependencyMonitor: function (dependencies, monitor, model) {
monitor = monitor == null ? this.__forceUpdate : monitor;
var _this = this;
if (model) {
dependencies.forEach(function (key) {
model.addPostChangeListener(key, monitor);
});
} else {
dependencies.forEach(function (key) {
if (typeof key === 'object') {
var id = key.id;
if (key.on === 'form') {
_this.getFormModel().addPostChangeListener(id, monitor);
} else if (key.on === 'inner') {
_this.getInnerModel().addPostChangeListener(id, monitor);
} else {
_this.getModel().addPostChangeListener(id, monitor);
}
} else {
_this.getModel().addPostChangeListener(key, monitor);
}
});
}
return this;
},
/**
* remove dependencies monitor
* @param dependencies {[]}
* @param monitor {function} optional
* @param model {ModelInterface} monitored model, optional
*/
removeDependencyMonitor: function (dependencies, monitor, model) {
monitor = monitor == null ? this.__forceUpdate : monitor;
var _this = this;
if (model) {
dependencies.forEach(function (key) {
model.removePostChangeListener(key, monitor);
});
} else {
dependencies.forEach(function (key) {
if (typeof key === 'object') {
var id = key.id;
if (key.on === 'form') {
_this.getFormModel().removePostChangeListener(id, monitor);
} else if (key.on === 'inner') {
_this.getInnerModel().removePostChangeListener(id, monitor);
} else {
_this.getModel().removePostChangeListener(id, monitor);
}
} else {
_this.getModel().removePostChangeListener(key, monitor);
}
});
}
return this;
},
// event
addPostChangeListener: function (listener) {
this.getModel().addPostChangeListener(this.getDataId(), listener);
},
removePostChangeListener: function (listener) {
this.getModel().removePostChangeListener(this.getDataId(), listener);
},
addPostAddListener: function (listener) {
this.getModel().addPostAddListener(this.getDataId(), listener);
},
removePostAddListener: function (listener) {
this.getModel().removePostAddListener(this.getDataId(), listener);
},
addPostRemoveListener: function (listener) {
this.getModel().addPostRemoveListener(this.getDataId(), listener);
},
removePostRemoveListener: function (listener) {
this.getModel().removePostRemoveListener(this.getDataId(), listener);
},
addPostValidateListener: function (listener) {
this.getModel().addPostValidateListener(this.getDataId(), listener);
},
removePostValidateListener: function (listener) {
this.getModel().removePostValidateListener(this.getDataId(), listener);
}
};
/**
* Array component mixin
*/
$pt.mixins = {
ArrayComponentMixin: {
isArrayData: function () {
return true;
},
addRowListener: function (rowModel) {
this.getRowListeners().forEach(function (listener) {
rowModel.addListener(listener.id, listener.time ? listener.time : 'post', listener.type ? listener.type : 'change', listener.listener);
});
rowModel.addPostChangeListener(null, this.onRowModelChanged);
rowModel.addPostAddListener(null, this.onRowModelChanged);
rowModel.addPostRemoveListener(null, this.onRowModelChanged);
},
onRowModelChanged: function (evt) {
var hierarchyPublisher = this.getComponentOption('hierarchyPublisher');
if (hierarchyPublisher) {
var jsonModel = evt.model.getCurrentModel();
var array = this.getModel().get(this.getDataId());
var index = array.indexOf(jsonModel);
hierarchyPublisher.call(this, this.getModel(), this.getDataId(), evt, index);
} else if (hierarchyPublisher === false) {
// do not publish to parent
} else {
// default behavior
// fire a change operation no matter what type of event
this.getModel().update(this.getDataId(), evt.model.getCurrentModel(), evt.model.getCurrentModel());
}
},
/**
* get row listeners, return empty array if no row listener defined
*/
getRowListeners: function () {
var listeners = this.getComponentOption(this.getRowListenerKey());
return listeners ? Array.isArray(listeners) ? listeners : [listeners] : [];
},
getRowListenerKey: function () {
return 'rowListener';
},
createRowModel: function (item, useBaseAsCurrent) {
var parentModel = this.getModel();
var parentValidator = parentModel.getValidator();
var validator = null;
if (parentValidator) {
var parentValidationConfig = parentValidator.getConfig()[this.getDataId()];
if (parentValidationConfig && parentValidationConfig.table) {
validator = $pt.createModelValidator(parentValidationConfig.table);
}
}
var model = validator ? $pt.createModel(item, validator) : $pt.createModel(item);
model.parent(parentModel);
// synchronized the validation result from parent model
// get errors about current value
var errors = this.getModel().getError(this.getDataId());
if (errors) {
errors.forEach(function (error) {
if (typeof error !== 'string') {
model.mergeError(error.getError(item));
}
});
}
if (useBaseAsCurrent) {
model.useBaseAsCurrent();
}
return model;
}
},
PopoverMixin: {
// A) method list should be defined in component scripts:
// 1. getComponent, return jQuery object, required
// 2. getPopoverContainerCSS, return css class name string, optional
// 3. renderPopoverContent, return JSX DOM object, required
// 4. beforePopoverRenderComplete, optional
// 5. afterPopoverRenderComplete, optional
// 6. isPopoverMatchComponentWidth, return boolean, default true, optional
// 7. hasPopoverContentWrapper, return boolean, default true, optional
// 8. beforeDestoryPopover, optional
// 9. afterDestoryPopover, optional
// B) state properties list:
// 1. this.state.popoverDiv
renderPopoverContainer: function () {
if (this.state.popoverDiv == null) {
this.state.popoverDiv = $('<div>');
this.state.popoverDiv.appendTo($('body'));
if (!this.isMobile()) {
$(document).on('mousedown', this.onDocumentMouseDownWhenPopoverShown).on('keyup', this.onDocumentKeyUpWhenPopoverShown).on('keydown', this.onDocumentKeyDownWhenPopoverShown).on('mousewheel', this.onDocumentMouseWheelWhenPopoverShown);
$(window).on('resize', this.onWindowResizeWhenPopoverShown);
} else {
$('body').on('touchmove mousewheel', this.onDocumentMouseWheelWhenPopoverShown);
}
}
},
renderPopover: function () {
if (this.beforeRenderPopover) {
this.beforeRenderPopover.apply(this, arguments);
}
var styles = { display: 'block' };
if (!this.isPopoverMatchComponentWidth || this.isPopoverMatchComponentWidth() !== false) {
var component = this.getComponent();
styles.width = component.outerWidth();
}
styles.top = -10000; // let it out of screen
styles.left = 0;
var css = {
'popover bottom in': true
};
if (this.getPopoverContainerCSS) {
css[this.getPopoverContainerCSS()] = true;
}
var additionalPopoverContainerCSS = this.getAdditionalCSS('popover');
if (additionalPopoverContainerCSS) {
css[additionalPopoverContainerCSS] = true;
}
if (this.isMobilePhone()) {
css['mobile-phone'] = true;
css['fix-bottom'] = this.isPopoverFixOnBottom();
// use default display style
styles = { display: 'block' }; // reset styles
}
var content = this.renderPopoverContent.apply(this, arguments);
if (!this.hasPopoverContentWrapper || this.hasPopoverContentWrapper() !== false) {
content = React.createElement(
"div",
{ className: "popover-content" },
content
);
}
var popover = React.createElement(
"div",
{ role: "tooltip", className: $pt.LayoutHelper.classSet(css), style: styles },
React.createElement("div", { className: "arrow" }),
content
);
ReactDOM.render(popover, this.state.popoverDiv.get(0), this.onPopoverRenderComplete);
},
onPopoverRenderComplete: function () {
if (this.beforePopoverRenderComplete) {
this.beforePopoverRenderComplete.apply(this, arguments);
}
this.state.popoverDiv.show();
if (this.isMobilePhone()) {
$('html').addClass('on-mobile-popover-shown');
} else {
var popover = this.state.popoverDiv.children('.popover');
var component = this.getComponent();
this.recalcPopoverPosition(popover, component);
}
if (this.afterPopoverRenderComplete) {
this.afterPopoverRenderComplete.apply(this, arguments);
}
},
recalcPopoverPosition: function (popover, component) {
var styles = {};
styles.width = component.outerWidth();
var offset = component.offset();
styles.top = offset.top + component.outerHeight();
styles.left = offset.left;
var onTop = false;
var rightToLeft = false;
var realHeight = popover.outerHeight();
var realWidth = popover.outerWidth();
// set the real top, assumpt it is on bottom
styles.top = offset.top + component.outerHeight();
// check popover in top or bottom
if (styles.top + realHeight > $(window).height() + $(window).scrollTop()) {
// cannot show in bottom and in current viewport
// check it is enough top or not
if (offset.top - $(window).scrollTop() >= realHeight) {
// enough
styles.top = offset.top - realHeight;
onTop = true;
} else if (styles.top + realHeight <= $(document).height()) {
// can show in bottom and in current document
onTop = false;
} else if (offset.top < realHeight) {
// cannot show in top and in current document
onTop = false;
} else {
styles.top = offset.top - realHeight;
onTop = true;
}
} else {
// can show in bottom and in current viewport
onTop = false;
}
// check popover to left or right
if (realWidth > styles.width) {
var width = $(document).width();
if (styles.left + realWidth <= width) {
// normal from left to right, do nothing
} else if (styles.left + styles.width >= realWidth) {
// from right to left
styles.left = styles.left + styles.width - realWidth;
rightToLeft = true;
} else {
// still left to right, do nothing
}
}
if (onTop) {
popover.addClass('top');
popover.removeClass('bottom');
} else {
popover.removeClass('top');
popover.addClass('bottom');
}
if (rightToLeft) {
popover.addClass('right-to-left');
}
popover.css({ top: styles.top, left: styles.left });
},
showPopover: function () {
if (this.beforeShowPopover) {
this.beforeShowPopover.apply(this, arguments);
}
this.renderPopoverContainer();
this.renderPopover.apply(this, arguments);
},
hidePopover: function () {
this.destroyPopover();
},
destroyPopover: function () {
if (this.beforeDestoryPopover) {
this.beforeDestoryPopover.apply(this, arguments);
}
$('html').removeClass('on-mobile-popover-shown');
if (this.state.popoverDiv) {
$(document).off('mousedown', this.onDocumentMouseDownWhenPopoverShown).off('keyup', this.onDocumentKeyUpWhenPopoverShown).off('keydown', this.onDocumentKeyDownWhenPopoverShown).off('mousewheel', this.onDocumentMouseWheelWhenPopoverShown);
$(window).off('resize', this.onWindowResizeWhenPopoverShown);
if (this.isMobilePhone()) {
$('body').off('touchmove mousewheel', this.onDocumentMouseWheelWhenPopoverShown);
}
this.state.popoverDiv.remove();
delete this.state.popoverDiv;
}
if (this.afterDestoryPopover) {
this.afterDestoryPopover.apply(this, arguments);
}
},
onDocumentMouseDownWhenPopoverShown: function (evt) {
var target = $(evt.target);
if (target.closest(this.getComponent()).length == 0 && target.closest(this.state.popoverDiv).length == 0) {
this.hidePopover();
}
},
onDocumentKeyUpWhenPopoverShown: function (evt) {
if (evt.keyCode === 27 || evt.keyCode === 9) {
// escape and tab
this.hidePopover();
}
},
onDocumentKeyDownWhenPopoverShown: function (evt) {
if (evt.keyCode === 38 || evt.keyCode === 40) {
evt.preventDefault();
}
},
onDocumentMouseWheelWhenPopoverShown: function (evt) {
if (this.isMobilePhone()) {
// when mobile phone, prevent the touch move and mouse wheel event
evt.preventDefault();
return;
}
var target = $(evt.target);
if (target.closest(this.state.popoverDiv).length == 0) {
this.hidePopover();
}
},
onWindowResizeWhenPopoverShown: function () {
this.hidePopover();
},
isPopoverFixOnBottom: function () {
return this.isMobilePhone() && this.POP_FIX_ON_BOTTOM === true;
}
}
};
/**
* define cell component
* @param config {{}} special component config, will replace the definition from component base if with same name
*/
$pt.defineCellComponent = function (config) {
var renderProxy = {};
if (config.keepRender !== true) {
renderProxy = {
render: function () {
if (!this.isVisible()) {
return null;
} else {
return config.render.call(this);
}
}
};
}
return $.extend({}, ComponentBase, config, renderProxy);
};
var LayoutHelper = jsface.Class({
constructor: function () {
this.__comp = {};
this.__components = {};
},
/**
* copy from React.addons.classSet
* @param classNames
* @returns {string}
*/
classSet: function (classNames) {
if (typeof classNames == 'object') {
return Object.keys(classNames).filter(function (className) {
return classNames[className];
}).join(' ');
} else {
return Array.prototype.join.call(arguments, ' ');
}
},
setDefaultCellWidth: function (width) {
CellLayout.DEFAULT_WIDTH = width * 1;
},
setDefaultSectionWidth: function (width) {
SectionLayout.DEFAULT_WIDTH = width * 1;
},
/**
* register react component to central
*/
registerComponent: function (id, component) {
if (this.__comp[id]) {
// already some components use this id
var exists = this.__comp[id];
if (Array.isArray(exists)) {
// push to array if not exists
var found = exists.find(function (existed) {
return existed === component;
});
if (!found) {
exists.push(component);
}
} else {
// set as array if not equals
if (exists !== component) {
this.__comp[id] = [exists, component];
}
}
} else {
// set new component
this.__comp[id] = component;
}
return this;
},
/**
* unregister component from central
*/
unregisterComponent: function (id, component) {
if (component) {
// delete key, unregister all components with given id
delete this.__comp[id];
} else {
// find all existed component with given id
var exists = this.__comp[id];
if (exists) {
if (Array.isArray(exists)) {
var index = exists.findIndex(function (existed) {
return existed === component;
});
if (index != -1) {
// unregister the found component
exists.splice(index, 1);
}
} else if (exists === component) {
// only one, equals, delete key
delete this.__comp[id];
}
}
}
return this;
},
/**
* get component by given id
*/
getComponent: function (id) {
return this.__comp[id];
},
__forceUpdate: function (component) {
if (component.forceUpdate) {
component.forceUpdate();
}
return this;
},
/**
* force update components which has give id
*/
forceUpdate: function (id) {
var components = this.getComponent(id);
if (components) {
if (Array.isArray(components)) {
components.forEach(this.__forceUpdate);
} else {
this.__forceUpdate(components);
}
}
return this;
},
// register components
registerComponentRenderer: function (type, func) {
if (typeof type !== 'string') {
type = type.type;
}
if (this.__components[type]) {
window.console.warn('Component [' + type + '] is replaced.');
}
this.__components[type] = func;
},
getComponentRenderer: function (type) {
if (typeof type !== 'string') {
type = type.type;
}
if (this.__components[type]) {
return this.__components[type];
} else {
throw $pt.createComponentException($pt.ComponentConstants.Err_Unsupported_Component, "Component type [" + type + "] is not supported yet.");
}
},
registerComponentViewModeRenderer: function (type, func) {
if (typeof type !== 'string') {
type = type.type;
}
type = type + '@view';
if (this.__components[type]) {
window.console.warn('Component [' + type + '] is replaced.');
}
this.__components[type] = func;
},
getComponentViewModeRenderer: function (type) {
if (typeof type !== 'string') {
type = type.type;
}
type = type + '@view';
if (this.__components[type]) {
return this.__components[type];
} else {
// no view mode renderer registered yet
return null;
}
},
transformParameters: function (model, layout, direction, viewMode) {
return {
model: model,
layout: layout,
direction: direction,
view: viewMode,
ref: layout.getId()
};
}
});
$pt.LayoutHelper = new LayoutHelper();
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Nothing, function () {
return null;
});
})(window, jQuery, jsface, React);
(function (window, $, React, ReactDOM, $pt) {
var NArrayCheck = React.createClass($pt.defineCellComponent({
displayName: 'NArrayCheck',
getDefaultProps: function () {
return {
defaultOptions: {
direction: 'horizontal',
labelAttached: 'right'
}
};
},
renderItem: function (enabled, item, itemIndex) {
var model = $pt.createModel({
id: item.id,
checked: this.isCodeChecked(item)
});
var layout = $pt.createCellLayout('checked', {
label: item.text,
comp: {
labelAttached: this.getComponentOption('labelAttached'),
enabled: item.enabled != null ? item.enabled : enabled
}
});
model.addPostChangeListener('checked', this.onCodeItemCheckedChanged.bind(this, item));
return React.createElement($pt.Components.NCheck, { model: model, layout: layout, key: itemIndex, view: this.isViewMode() });
},
render: function () {
var enabled = this.isEnabled();
var css = {
'n-disabled': !enabled,
vertical: this.getComponentOption('direction') === 'vertical'
};
css[this.getComponentCSS('n-array-check')] = true;
return React.createElement($pt.Components.NCodeTableWrapper, { codetable: this.getCodeTable(),
className: $pt.LayoutHelper.classSet(css),
model: this.getModel(),
layout: this.getLayout(),
renderer: this.getRealRenderer });
},
getRealRenderer: function () {
var enabled = this.isEnabled();
var css = {
'n-disabled': !enabled,
vertical: this.getComponentOption('direction') === 'vertical'
};
css[this.getComponentCSS('n-array-check')] = true;
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css) },
this.getCodeTable().list().map(this.renderItem.bind(this, enabled))
);
},
onCodeItemCheckedChanged: function (codeTableItem, evt) {
if (evt.new) {
// checked
this.onCodeItemChecked(codeTableItem);
} else {
// unchecked
this.onCodeTableUnchecked(codeTableItem);
}
},
onCodeTableUnchecked: function (codeTableItem) {
var values = this.getValueFromModel();
if (values == null) {
return;
}
this.setValueToModel(values.filter(function (value) {
return value != codeTableItem.id;
}).slice(0));
},
onCodeItemChecked: function (codeTableItem) {
// checked
var values = this.getValueFromModel();
if (values == null) {
values = [codeTableItem.id];
} else {
var index = values.findIndex(function (value) {
return value == codeTableItem.id;
});
if (index == -1) {
values.push(codeTableItem.id);
}
}
this.setValueToModel(values.slice(0));
},
getCodeTable: function () {
return this.getComponentOption('data');
},
isCodeChecked: function (codeTableItem) {
var values = this.getValueFromModel();
return values != null && values.some(function (value) {
return value == codeTableItem.id;
});
}
}));
$pt.Components.NArrayCheck = NArrayCheck;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.ArrayCheck, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NArrayCheck, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NArrayPanel = React.createClass($pt.defineCellComponent({
displayName: 'NArrayPanel',
mixins: [$pt.mixins.ArrayComponentMixin],
statics: {
UNTITLED: 'Untitled Item'
},
getDefaultProps: function () {
return {
defaultOptions: {
collapsible: true,
expanded: true
}
};
},
/**
* render item
* @param item {{}}
* @returns {XML}
*/
renderItem: function (item, itemIndex) {
var model = this.createRowModel(item, true);
this.addRowListener(model);
var _this = this;
this.getDependencies('itemTitle').forEach(function (key) {
model.addListener(key, "post", "change", function () {
_this.forceUpdate();
});
});
var cellLayout = {
label: this.getPanelTitle(model, itemIndex),
comp: {
type: $pt.ComponentConstants.Panel,
collapsible: this.getComponentOption('collapsible'),
expanded: this.getExpanded(model, itemIndex),
editLayout: this.getEditLayout(model, itemIndex),
style: this.getComponentOption('style'),
checkInTitle: this.getCheckInTitle(model, itemIndex),
expandedLabel: this.getComponentOption('expandedLabel'),
collapsedLabel: this.getComponentOption('collapsedLabel'),
headerButtons: this.getHeaderButtons(model, itemIndex),
customHeader: this.getCustomerHeader(model, itemIndex),
centralId: this.getComponentCentralId() + '-' + itemIndex
}
};
if (this.getComponentOption('itemCss')) {
var itemCss = this.getComponentOption('itemCss');
cellLayout.css = typeof itemCss == 'string' ? { comp: itemCss } : itemCss;
}
return React.createElement(
'div',
{ className: 'row', key: itemIndex },
React.createElement(
'div',
{ className: 'col-sm-12 col-md-12 col-lg-12' },
React.createElement($pt.Components.NPanel, { model: model,
layout: $pt.createCellLayout('pseudo-panel', cellLayout),
direction: this.props.direction,
view: this.isViewMode() })
)
);
},
/**
* render
* @returns {XML}
*/
render: function () {
return React.createElement(
'div',
{ className: this.getComponentCSS('n-array-panel') },
this.getValueFromModel().map(this.renderItem)
);
},
/**
* return [] when is null
* @returns {[*]}
*/
getValueFromModel: function () {
var data = this.getModel().get(this.getDataId());
return data == null ? [] : data;
},
/**
* get edit layout
* @param model {ModelInterface} item model
* @returns {{}}
*/
getEditLayout: function (model, itemIndex) {
var layout = this.getComponentOption('editLayout');
if (typeof layout === 'function') {
return layout.call(this, model, itemIndex);
} else {
return layout;
}
},
getHeaderButtons: function (model, itemIndex) {
var buttons = this.getComponentOption('headerButtons');
if (typeof buttons === 'function') {
return buttons.call(this, model, itemIndex);
} else {
return buttons;
}
},
getExpanded: function (model, itemIndex) {
var expanded = this.getComponentOption('expanded');
if (typeof expanded === 'function') {
return expanded.call(this, model, itemIndex);
} else {
return expanded;
}
},
getCustomerHeader: function (model, itemIndex) {
var header = this.getComponentOption('customHeader');
if (typeof header === 'function') {
return header.call(this, model, itemIndex);
} else {
return header;
}
},
/**
* get check in title
* @param model {ModelInterface} item model
* @returns {{}}
*/
getCheckInTitle: function (model, itemIndex) {
var checkInTitle = this.getComponentOption('checkInTitle');
if (typeof checkInTitle === 'function') {
return checkInTitle.call(this, model, itemIndex);
} else {
return checkInTitle;
}
},
/**
* get panel titled
* @param model {ModelInterface} item model
* @returns {string}
*/
getPanelTitle: function (model, itemIndex) {
var title = this.getComponentOption('itemTitle');
if (title == null) {
return NArrayPanel.UNTITLED;
} else if (typeof title === 'string') {
return title;
} else {
var titleText = title.when.call(this, model, itemIndex);
return titleText == null || titleText.isBlank() ? NArrayPanel.UNTITLED : titleText;
}
}
}));
$pt.Components.NArrayPanel = NArrayPanel;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.ArrayPanel, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NArrayPanel, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NArrayTab = React.createClass($pt.defineCellComponent({
displayName: 'NArrayTab',
mixins: [$pt.mixins.ArrayComponentMixin],
statics: {
UNTITLED: 'Untitled Item',
ADD_ICON: 'plus-circle',
ADD_LABEL: 'Add'
},
getDefaultProps: function () {
return {
defaultOptions: {
tabType: 'tab',
justified: false,
titleDirection: 'horizontal'
}
};
},
/**
* render tab content
* @param tab
* @param tabIndex
* @returns {XML}
*/
renderTabContent: function (tab, tabIndex) {
var activeTabIndex = this.getActiveTabIndex();
var css = {
'n-array-tab-card': true,
show: tabIndex == activeTabIndex,
hide: tabIndex != activeTabIndex
};
// no base here. since no apply operation
var _this = this;
// add item title and item icon listener
this.getDependencies(['itemTitle', 'itemIcon']).forEach(function (key) {
tab.data.addListener(key, "post", "change", function () {
_this.forceUpdate();
});
});
// add badge listener
var badge = this.getComponentOption('badge');
if (badge != null) {
if (typeof badge === 'string') {
tab.data.addListener(badge, "post", "change", function () {
_this.forceUpdate();
});
} else {
this.getDependencies(badge).forEach(function (key) {
tab.data.addListener(key, "post", "change", function () {
_this.forceUpdate();
});
});
}
}
return React.createElement($pt.Components.NForm, { model: tab.data,
layout: $pt.createFormLayout(tab.layout),
direction: this.props.direction,
view: this.isViewMode(),
className: $pt.LayoutHelper.classSet(css),
key: tabIndex });
},
/**
* render
* @returns {XML}
*/
render: function () {
var tabs = this.getTabs();
var canActiveProxy = function (newTabValue, newTabIndex, activeTabValue, activeTabIndex) {
if (this.isAddable() && newTabIndex == tabs.length - 1) {
var onAdd = this.getComponentOption('onAdd');
onAdd.call(this, this.getModel(), this.getValueFromModel());
return false;
} else {
var canActive = this.getComponentOption('canActive');
if (canActive) {
return canActive.call(this, newTabValue, newTabIndex, activeTabValue, activeTabIndex);
}
}
}.bind(this);
// when state.clear is true, forcily clear the tab title dom
// and then delete the state
// see the following code in #onModelChanged
// this.setState({clear: true}, this.forceUpdate);
// forceUpdate must be passed as callback to #setState
// all these are to remove the flicking when remove data from model side
var tabTitle = null;
if (this.state.clear !== true) {
tabTitle = React.createElement($pt.Components.NTab, { type: this.getComponentOption('tabType'),
justified: this.getComponentOption('justified'),
direction: this.getComponentOption('titleDirection'),
size: this.getComponentOption('titleIconSize'),
tabClassName: this.getAdditionalCSS('tabs'),
tabs: tabs,
canActive: canActiveProxy,
onActive: this.onTabClicked,
ref: 'tabs' });
} else {
delete this.state.clear;
}
return React.createElement(
'div',
{ className: this.getComponentCSS('n-array-tab') },
tabTitle,
React.createElement(
'div',
{ className: 'n-array-tab-content', ref: 'content' },
tabs.map(this.renderTabContent)
)
);
},
/**
* get tabs
* @returns {Array}
*/
getTabs: function () {
var activeTabIndex = 0;
if (this.state.transientActiveTabIndex != null) {
activeTabIndex = this.state.transientActiveTabIndex;
delete this.state.transientActiveTabIndex;
} else {
activeTabIndex = this.getActiveTabIndex();
}
this.state.tabs = this.getValueFromModel().map(function (item, itemIndex) {
var model = this.createRowModel(item, true);
this.addRowListener(model);
return {
label: this.getTabTitle(model),
icon: this.getTabIcon(model),
layout: this.getEditLayout(model),
badge: this.getTabBadge(model),
data: model,
active: itemIndex == activeTabIndex
};
}.bind(this));
if (this.isAddable()) {
this.state.tabs.push({
icon: NArrayTab.ADD_ICON,
label: NArrayTab.ADD_LABEL,
layout: {
nothing: {
comp: {
type: $pt.ComponentConstants.Nothing
}
}
},
data: $pt.createModel({}),
css: 'add-tab'
});
}
return this.state.tabs;
},
clearTabs: function (callback) {
if (callback) {
this.setState({ tabs: null }, callback.bind(this));
} else {
this.setState({ tabs: null });
}
},
/**
* return [] when is null
* @returns {[*]}
*/
getValueFromModel: function () {
var data = this.getModel().get(this.getDataId());
return data == null ? [] : data;
},
/**
* on model changed
* @param evt
*/
onModelChanged: function (evt) {
if (evt.type === 'add') {
this.state.transientActiveTabIndex = evt.index;
// this.clearTabs(this.setActiveTabIndex.bind(this, evt.index));
} else if (evt.type === 'remove') {
var index = evt.index;
var data = this.getValueFromModel();
if (index == data.length) {
index = index - 1;
}
this.state.transientActiveTabIndex = index;
// this.clearTabs(this.setActiveTabIndex.bind(this, index));
} else {
// this.forceUpdate();
this.state.transientActiveTabIndex = evt.index != null ? evt.index : null;
}
this.setState({ clear: true }, this.forceUpdate);
// this.forceUpdate();
},
/**
* get edit layout
* @param model {ModelInterface} item model
* @returns {FormLayout}
*/
getEditLayout: function (model) {
var layout = this.getComponentOption('editLayout');
if (typeof layout === 'function') {
return layout.call(this, model);
} else {
return layout;
}
},
/**
* get item title
* @param model {ModelInterface} item model
* @returns {string}
*/
getTabTitle: function (model) {
var title = this.getComponentOption('itemTitle');
if (title == null) {
return NArrayTab.UNTITLED;
} else if (typeof title === 'string') {
return title;
} else {
var titleText = title.when.call(this, model);
return titleText == null || titleText.isBlank() ? NArrayTab.UNTITLED : titleText;
}
},
getTabBadge: function (model) {
var badge = this.getComponentOption('badge');
if (badge == null) {
return null;
} else if (typeof badge === 'string') {
var badgeValue = model.get(badge);
var badgeRender = this.getComponentOption('badgeRender');
if (badgeRender) {
badgeValue = badgeRender.call(this, badgeValue, model, this.getModel());
}
return badgeValue;
} else {
return badge.when.call(this, model);
}
},
/**
* get item icon
* @param model {ModelInterface} item model
* @returns {string}
*/
getTabIcon: function (model) {
var icon = this.getComponentOption('itemIcon');
if (icon == null) {
return null;
} else if (typeof icon === 'string') {
return icon;
} else {
return icon.when.call(this, model);
}
},
isAddable: function () {
return !this.isViewMode() && this.getComponentOption('onAdd') != null;
},
/**
* on tab clicked
* @param tabValue {string} tab value
* @param index {number}
*/
onTabClicked: function (tabValue, index) {
this.setActiveTabIndex(index);
var onActive = this.getComponentOption('onActive');
if (onActive) {
onActive.call(this, tabValue, index);
}
},
/**
* get active tab index
* @returns {number}
*/
getActiveTabIndex: function () {
var tabs = this.state.tabs;
if (tabs == null) {
return -1;
}
// find the active tab
var activeTabIndex = tabs.findIndex(function (tab, index) {
return tab.active === true;
});
if (activeTabIndex == -1) {
// find the first visible tab if no active tab found
activeTabIndex = tabs.findIndex(function (tab, index) {
var visible = tab.visible !== false;
if (visible) {
tab.active = true;
return true;
}
});
}
return activeTabIndex;
},
/**
* set active tab index
* @param {number}
*/
setActiveTabIndex: function (index) {
this.refs.tabs.setActiveTabIndex(index);
this.forceUpdate();
}
}));
$pt.Components.NArrayTab = NArrayTab;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.ArrayTab, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NArrayTab, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NFormButton = React.createClass($pt.defineCellComponent({
displayName: 'NFormButton',
getDefaultProps: function () {
return {
defaultOptions: {
style: 'default',
labelFromModel: false
}
};
},
renderIcon: function (icon) {
if (icon == null) {
return null;
} else {
var css = {
fa: true,
'fa-fw': true
};
css['fa-' + icon] = true;
return React.createElement('span', { className: $pt.LayoutHelper.classSet(css), key: 'icon' });
}
},
/**
* render icon
* @returns {*}
*/
renderButtonIcon: function () {
return this.renderIcon(this.getIcon());
},
renderMoreButtons: function (css) {
var more = this.getComponentOption('more');
if (more) {
// onClick={this.onClicked}
var dropdown = React.createElement(
'a',
{ href: 'javascript:void(0);',
className: $pt.LayoutHelper.classSet(css) + ' dropdown-toggle',
disabled: !this.isEnabled(),
'data-toggle': 'dropdown',
'aria-haspopup': 'true',
'aria-expanded': 'false',
key: 'a' },
!this.getComponentOption('click') ? this.getButtonContext() : null,
React.createElement('span', { className: 'caret' })
);
var emptyFunction = function () {};
var _this = this;
var menus = React.createElement(
'ul',
{ className: 'dropdown-menu', key: 'ul' },
more.map(function (menu, menuIndex) {
if (menu.divider) {
return React.createElement('li', { role: 'separator', className: 'divider', key: menuIndex });
} else {
var click = menu.click ? menu.click : emptyFunction;
var label = menu.text;
var icon = _this.renderIcon(menu.icon);
if (label && icon) {
label = ' ' + label;
}
return React.createElement(
'li',
{ key: menuIndex },
React.createElement(
'a',
{ href: 'javascript:void(0);', onClick: click.bind(_this, _this.getModel()) },
icon,
label
)
);
}
})
);
return [dropdown, menus];
} else {
return null;
}
},
getButtonContext: function () {
var label = this.getLabel();
var icon = this.renderButtonIcon();
var buttonContext = null;
if (this.getLabelPosition() === 'left') {
// label in left
if (label && icon) {
label = React.createElement(
'span',
{ key: 'lbl' },
label + ' '
);
}
buttonContext = [label, icon];
} else {
// default label in right
if (label && icon) {
label = React.createElement(
'span',
{ key: 'lbl' },
' ' + label
);
}
buttonContext = [icon, label];
}
return buttonContext;
},
render: function () {
if (!this.isVisible()) {
return null;
}
var compCSS = {};
compCSS[this.getComponentCSS('n-button')] = true;
compCSS['n-disabled'] = !this.isEnabled();
var css = {
btn: true,
disabled: !this.isEnabled()
};
css['btn-' + this.getStyle()] = true;
var defaultClick = this.getComponentOption("click");
var more = this.getComponentOption('more');
var defaultButton = null;
if (defaultClick || !more) {
defaultButton = React.createElement(
'a',
{ href: 'javascript:void(0);',
className: $pt.LayoutHelper.classSet(css),
onMouseUp: this.onClicked,
disabled: !this.isEnabled(),
title: this.getComponentOption('tooltip'),
ref: 'a' },
this.getButtonContext()
);
}
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(compCSS) },
React.createElement(
'div',
{ className: 'btn-group' },
defaultButton,
this.renderMoreButtons(css)
)
);
},
onClicked: function (evt) {
if (evt.button != 0) {
// not left button
return;
}
// console.log(evt.button);
if (this.isEnabled()) {
$(ReactDOM.findDOMNode(this.refs.a)).toggleClass('effect');
var onclick = this.getComponentOption("click");
if (onclick) {
onclick.call(this, this.getModel(), evt.target);
}
}
},
/**
* get icon
* @returns {string}
*/
getIcon: function () {
return this.getComponentOption("icon");
},
/**
* get button style
* @returns {string}
*/
getStyle: function () {
var style = this.getComponentOption('style');
if (typeof style === 'function') {
return style.call(this);
} else {
return style;
}
},
/**
* get label position
* @returns {string}
*/
getLabelPosition: function () {
return this.getComponentOption("labelPosition");
},
getLabel: function () {
var labelFromModel = this.getComponentOption('labelFromModel');
if (labelFromModel) {
return this.getValueFromModel();
} else {
return this.getLayout().getLabel(this);
}
},
/**
* @overrides do nothing
* @param value
*/
setValueToModel: function (value) {
// nothing
}
}));
$pt.Components.NFormButton = NFormButton;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Button, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NFormButton, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NCheck = React.createClass($pt.defineCellComponent({
displayName: 'NCheck',
/**
* render label
* @param labelInLeft {boolean}
* @returns {XML}
*/
renderLabel: function (labelInLeft) {
if (this.isLabelAttached()) {
var label = this.getLayout().getLabel(this);
if (label == null || label.isEmpty()) {
return null;
}
var enabled = this.isEnabled();
var css = {
'check-label': true,
disabled: !this.isEnabled(),
'check-label-left': labelInLeft
};
return React.createElement(
'span',
{ className: $pt.LayoutHelper.classSet(css),
onClick: enabled && !this.isViewMode() ? this.onButtonClicked : null },
this.getLayout().getLabel(this)
);
}
return null;
},
/**
* render check box, using font awesome instead
* @returns {XML}
*/
renderCheckbox: function () {
var checked = this.isChecked();
var enabled = this.isEnabled();
var css = {
disabled: !enabled,
checked: checked,
'check-container': true
};
return React.createElement(
'div',
{ className: 'check-container' },
React.createElement(
'span',
{ className: $pt.LayoutHelper.classSet(css),
onClick: enabled && !this.isViewMode() ? this.onButtonClicked : null,
onKeyUp: enabled && !this.isViewMode() ? this.onKeyUp : null,
tabIndex: '0',
ref: 'out' },
React.createElement('span', { className: 'check', onClick: this.onInnerClicked })
)
);
},
/**
* render
* @returns {XML}
*/
render: function () {
var css = {
'n-disabled': !this.isEnabled(),
'n-view-mode': this.isViewMode()
};
css[this.getComponentCSS('n-checkbox')] = true;
var isLabelAtLeft = this.isLabelAtLeft();
// <input type="checkbox" style={{display: "none"}}
// onChange={this.onComponentChanged} ref='txt'/>
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css) },
isLabelAtLeft ? this.renderLabel(true) : null,
this.renderCheckbox(),
!isLabelAtLeft ? this.renderLabel(false) : null
);
},
/**
* inner span clicked, force focus to outer span
* for fix the outer span cannot gain focus in IE11
*/
onInnerClicked: function () {
$(ReactDOM.findDOMNode(this.refs.out)).focus();
},
/**
* handle button clicked event
*/
onButtonClicked: function () {
this.setValueToModel(!this.isChecked());
},
onKeyUp: function (evt) {
if (evt.keyCode == '32') {
this.onButtonClicked();
}
},
/**
* is checked or not
* @returns {boolean}
*/
isChecked: function () {
return this.getValueFromModel() === true;
},
/**
* is label attached
* @returns {boolean}
*/
isLabelAttached: function () {
return this.getComponentOption('labelAttached') !== null;
},
/**
* get component
* @returns {jQuery}
*/
// getComponent: function () {
// return $(ReactDOM.findDOMNode(this.refs.txt));
// },
isLabelAtLeft: function () {
return this.getComponentOption('labelAttached') === 'left';
}
}));
$pt.Components.NCheck = NCheck;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Check, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NCheck, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NCodeTableWrapper = React.createClass($pt.defineCellComponent({
displayName: 'CodeTableWrapper',
statics: {
ON_LOADING_ICON: 'fa fa-fw fa-spinner fa-spin',
ON_LOADING: 'On Loading...'
},
getDefaultProps: function () {
return {};
},
componentWillUpdate: function () {},
componentDidUpdate: function () {},
/**
* did mount
*/
componentDidMount: function () {
var codetable = this.getCodeTable();
if (this.state.paintWrapper) {
if (this.hasParent()) {
var parentValue = this.getParentValue();
if (parentValue == null) {
if (this.loadWhenNoParentValue()) {
// load all when no parent value
codetable.initializeRemote().done(this.repaint);
} else {
codetable.setAsRemoteInitialized();
this.repaint();
}
} else {
// only load segments according to parent value
codetable.loadRemoteCodeSegment(parentValue).done(this.repaint);
}
} else {
// no parent, load all
codetable.initializeRemote().done(this.repaint);
}
} else {
var onMounted = this.props.onMounted;
if (onMounted) {
onMounted.call(this);
}
}
},
componentWillUnmount: function () {},
repaint: function () {
var onMounted = this.props.onMounted;
this.setState({
paintWrapper: false
}, onMounted ? onMounted : undefined);
},
needWrapper: function () {
var codetable = this.getCodeTable();
var need = true;
if (Array.isArray(codetable)) {
// is an array, not code table instance
need = false;
} else if (this.hasParent() && this.getParentValue() == null && !this.loadWhenNoParentValue()) {
// has parent, no parent value, no options when no parent value
need = false;
} else if (!codetable.isRemoteButNotInitialized()) {
// initialized remote data
need = false;
}
return need;
},
render: function () {
if (this.needWrapper()) {
this.state.paintWrapper = true;
var className = this.props.className ? this.props.className + ' n-codetable-wrapper form-control' : 'n-codetable-wrapper form-control';
return React.createElement(
'div',
{ className: className },
React.createElement('span', { className: 'n-ctol-icon ' + NCodeTableWrapper.ON_LOADING_ICON }),
React.createElement(
'span',
{ className: 'n-ctol-label' },
NCodeTableWrapper.ON_LOADING
)
);
} else {
this.state.paintWrapper = false;
return this.getWrappedRenderer().call(this);
}
},
getCodeTable: function () {
return this.props.codetable;
},
getWrappedRenderer: function () {
return this.props.renderer;
},
hasParent: function () {
return this.props.hasParent;
},
loadWhenNoParentValue: function () {
return this.props.hasParent ? this.props.loadWhenNoParentValue : true;
},
getParentValue: function () {
return this.props.parentValue;
}
}));
$pt.Components.NCodeTableWrapper = NCodeTableWrapper;
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, moment, React, ReactDOM, $pt) {
var NDateTime = React.createClass($pt.defineCellComponent({
displayName: 'NDateTime',
mixins: [$pt.mixins.PopoverMixin],
statics: {
POP_FIX_ON_BOTTOM: false,
FORMAT: 'YYYY/MM/DD',
HEADER_MONTH_FORMAT: 'MMMM',
HEADER_YEAR_FORMAT: 'YYYY',
VALUE_FORMAT: $pt.ComponentConstants.Default_Date_Format,
FORMAT_TYPES: {
// use binary
ALL: 64 + 32 + 16 + 8 + 4 + 2,
YMD: 64 + 32 + 16,
YM: 64 + 32,
HM: 8 + 4,
HMS: 8 + 4 + 2,
YEAR: 64,
MONTH: 32,
DAY: 16,
HOUR: 8,
MINUTE: 4,
SECOND: 2,
MILLSECOND: 1
},
CLOCK_RADIUS: 100,
CLOCK_HOUR_PERCENTAGE: 0.6,
CLOCK_BIG_ENGRAVE_LENGTH: 8,
CLOCK_SMALL_ENGRAVE_LENGTH: 4,
CLOCK_CHAR_POS: {
TOP: { X: 100, Y: -2 },
LEFT: { X: -1, Y: 99 },
RIGHT: { X: 201, Y: 99 },
BOTTOM: { X: 100, Y: 203 }
},
CLOCK_HAND_OFFSET: 10,
CLOSE_TEXT: 'Close',
TODAY_TEXT: 'Now',
CLEAR_TEXT: 'Clear',
DATE_SWITCH_TEXT: 'Date',
TIME_SWITCH_TEXT: 'Time',
FIXED_WEEKDS: false
},
getDefaultProps: function () {
return {
defaultOptions: {
locale: 'en',
headerMonthFirst: true,
bodyYearFormat: 'YYYY',
icons: {
calendar: 'calendar',
today: 'crosshairs',
clear: 'trash-o',
close: 'close'
}
}
};
},
beforeDidUpdate: function () {
this.setValueToTextInput(this.getValueFromModel());
},
beforeDidMount: function () {
this.setValueToTextInput(this.getValueFromModel());
},
beforeWillUnmount: function () {
this.destroyPopover();
},
renderIcon: function (options) {
var css = {
fa: true,
'fa-fw': true
};
css['fa-' + options.icon] = true;
if (options.className) {
css[options.className] = true;
}
return React.createElement('span', { className: $pt.LayoutHelper.classSet(css), onClick: options.click });
},
renderInputArea: function () {
// desktop
var css = {
'input-group-addon': true,
link: true,
disabled: !this.isEnabled()
};
return React.createElement(
'div',
{ className: 'input-group',
onClick: this.onCalendarButtonClicked,
ref: 'comp' },
React.createElement('input', { type: 'text',
className: 'form-control',
disabled: !this.isEnabled(),
onChange: this.onTextInputChange,
onFocus: this.onTextInputFocused,
onBlur: this.onTextInputBlurred,
'aria-readonly': this.isMobile() ? true : false,
readOnly: this.isMobile() ? true : false,
ref: 'text' }),
React.createElement(
'span',
{ className: $pt.LayoutHelper.classSet(css) },
this.renderIcon({ icon: this.getIcon('calendar'), click: this.onCalendarButtonClicked })
)
);
},
render: function () {
if (this.isViewMode()) {
return this.renderInViewMode();
}
var divCSS = {
'n-disabled': !this.isEnabled()
};
divCSS[this.getComponentCSS('n-datetime')] = true;
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(divCSS) },
this.renderInputArea(),
this.renderNormalLine(),
this.renderFocusLine()
);
},
renderHeaderMonth: function (date) {
return React.createElement(
'span',
{ onClick: this.renderPopover.bind(this, { date: date, type: NDateTime.FORMAT_TYPES.MONTH }),
className: 'header-date-btn',
key: 'header-month' },
this.convertValueToString(date, this.getHeaderMonthFormat())
);
},
renderHeaderYear: function (date) {
return React.createElement(
'span',
{ onClick: this.renderPopover.bind(this, { date: date, type: NDateTime.FORMAT_TYPES.YEAR }),
className: 'header-date-btn',
key: 'header-year' },
this.convertValueToString(date, this.getHeaderYearFormat())
);
},
renderDayHeader: function (date) {
var mainHeader = [this.renderHeaderMonth(date), ' ', this.renderHeaderYear(date)];
var monthFirst = this.getComponentOption('headerMonthFirst');
return React.createElement(
'div',
{ className: 'calendar-header day-view' },
this.renderIcon({
icon: 'angle-double-left',
className: 'header-btn left',
click: this.renderPopover.bind(this, { date: date.clone().subtract(1, 'y'), type: NDateTime.FORMAT_TYPES.DAY })
}),
this.renderIcon({
icon: 'angle-left',
className: 'header-btn left',
click: this.renderPopover.bind(this, { date: date.clone().subtract(1, 'M'), type: NDateTime.FORMAT_TYPES.DAY })
}),
monthFirst ? mainHeader : mainHeader.reverse(),
this.renderIcon({
icon: 'angle-double-right',
className: 'header-btn right',
click: this.renderPopover.bind(this, { date: date.clone().add(1, 'y'), type: NDateTime.FORMAT_TYPES.DAY })
}),
this.renderIcon({
icon: 'angle-right',
className: 'header-btn right',
click: this.renderPopover.bind(this, { date: date.clone().add(1, 'M'), type: NDateTime.FORMAT_TYPES.DAY })
})
);
},
getWeekdayHeader: function (date) {
var orgLocale = moment.locale();
moment.locale(this.getLocale());
var header = moment.weekdaysMin();
moment.locale(orgLocale);
var firstDayOfWeek = this.getFirstDayOfWeek();
// move how many to last
var removed = header.splice(0, firstDayOfWeek);
header = header.concat.apply(header, removed);
return header;
},
getDaysOfDayBody: function (date) {
var days = this.getDaysOfMonth(date);
var firstDay = date.clone();
firstDay.date(1); // set to the first day of month
var dayOfWeekOfFirstDay = this.getDayOfWeek(firstDay);
var firstDayOfWeek = this.getFirstDayOfWeek();
var gapDaysOfPrevMonth = 0;
if (dayOfWeekOfFirstDay >= firstDayOfWeek) {
gapDaysOfPrevMonth = dayOfWeekOfFirstDay - firstDayOfWeek;
} else {
gapDaysOfPrevMonth = dayOfWeekOfFirstDay + 7 - firstDayOfWeek;
}
// calculate
var index = 0;
var viewDays = [];
var viewDay = null;
// gap days by previous month
for (index = 1; index <= gapDaysOfPrevMonth; index++) {
viewDay = firstDay.clone();
viewDay.subtract(index, 'd');
viewDays.splice(0, 0, viewDay);
}
// this month
for (index = 0; index < days; index++) {
viewDay = firstDay.clone();
viewDay.add(index, 'd');
viewDays.push(viewDay);
}
// gap days by next month
var gapDaysOfNextMonth = 7 - viewDays.length % 7;
gapDaysOfNextMonth = gapDaysOfNextMonth == 7 ? 0 : gapDaysOfNextMonth;
var lastDay = viewDays[viewDays.length - 1];
for (index = 1; index <= gapDaysOfNextMonth; index++) {
viewDay = lastDay.clone();
viewDay.add(index, 'd');
viewDays.push(viewDay);
}
if (NDateTime.FIXED_WEEKDS) {
lastDay = viewDays[viewDays.length - 1];
var dayCount = viewDays.length;
if (dayCount < 42) {
for (index = 1; index <= 42 - dayCount; index++) {
viewDay = lastDay.clone();
viewDay.add(index, 'd');
viewDays.push(viewDay);
}
}
}
return viewDays;
},
renderDayBody: function (date) {
var _this = this;
var header = this.getWeekdayHeader();
var days = this.getDaysOfDayBody(date);
var currentMonth = date.month();
var value = this.getValueFromModel();
var today = this.getToday();
var min = this.getMinDate();
var max = this.getMaxDate();
return React.createElement(
'div',
{ className: 'calendar-body day-view' },
React.createElement(
'div',
{ className: 'day-view-body-header row' },
header.map(function (weekday, weekdayIndex) {
return React.createElement(
'div',
{ className: 'cell-7-1', key: 'weekday-' + weekdayIndex },
weekday
);
})
),
React.createElement(
'div',
{ className: 'day-view-body-body row' },
days.map(function (day, dayIndex) {
var css = {
'cell-7-1': true,
'gap-day': day.month() != currentMonth,
'disable-day': day.isBefore(min) || day.isAfter(max),
today: day.isSame(today, 'day'),
'current-value': value != null && day.isSame(value, 'day')
};
var click = _this.onDaySelected.bind(_this, day);
if (css['disable-day'] === true) {
click = null;
}
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css),
onClick: click,
key: 'day-' + dayIndex },
React.createElement(
'span',
null,
day.date()
)
);
})
)
);
},
renderMonthHeader: function (date) {
return React.createElement(
'div',
{ className: 'calendar-header month-view' },
this.renderIcon({
icon: 'angle-double-left',
className: 'header-btn left',
click: this.renderPopover.bind(this, { date: date.clone().subtract(10, 'y'), type: NDateTime.FORMAT_TYPES.MONTH })
}),
this.renderIcon({
icon: 'angle-left',
className: 'header-btn left',
click: this.renderPopover.bind(this, { date: date.clone().subtract(1, 'y'), type: NDateTime.FORMAT_TYPES.MONTH })
}),
this.renderHeaderYear(date),
this.renderIcon({
icon: 'angle-double-right',
className: 'header-btn right',
click: this.renderPopover.bind(this, { date: date.clone().add(10, 'y'), type: NDateTime.FORMAT_TYPES.MONTH })
}),
this.renderIcon({
icon: 'angle-right',
className: 'header-btn right',
click: this.renderPopover.bind(this, { date: date.clone().add(1, 'y'), type: NDateTime.FORMAT_TYPES.MONTH })
})
);
},
renderMonthBody: function (date) {
var _this = this;
var orgLocale = moment.locale();
moment.locale(this.getLocale());
var months = moment.monthsShort('-MMM-');
moment.locale(orgLocale);
var day = this.getValueFromModel();
if (day == null) {
day = this.getToday();
}
var value = this.getValueFromModel();
var today = this.getToday();
return React.createElement(
'div',
{ className: 'calendar-body month-view' },
React.createElement(
'div',
{ className: 'month-view-body-body row' },
months.map(function (month, index) {
var selectedDay = day.clone();
selectedDay.year(date.year());
selectedDay.month(index);
var css = {
'cell-4-1': true,
today: index == today.month(),
'current-value': value != null && index == value.month()
};
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css),
onClick: _this.onMonthSelected.bind(_this, selectedDay),
key: index },
month
);
})
)
);
},
renderYearHeader: function (date) {
var yearHeader = [this.convertValueToString(date.clone().subtract(10, 'y'), this.getHeaderYearFormat()), ' - ', this.convertValueToString(date.clone().add(9, 'y'), this.getHeaderYearFormat())];
return React.createElement(
'div',
{ className: 'calendar-header year-view' },
this.renderIcon({
icon: 'angle-double-left',
className: 'header-btn left',
click: this.renderPopover.bind(this, { date: date.clone().subtract(40, 'y'), type: NDateTime.FORMAT_TYPES.YEAR })
}),
this.renderIcon({
icon: 'angle-left',
className: 'header-btn left',
click: this.renderPopover.bind(this, { date: date.clone().subtract(20, 'y'), type: NDateTime.FORMAT_TYPES.YEAR })
}),
yearHeader,
this.renderIcon({
icon: 'angle-double-right',
className: 'header-btn right',
click: this.renderPopover.bind(this, { date: date.clone().add(40, 'y'), type: NDateTime.FORMAT_TYPES.YEAR })
}),
this.renderIcon({
icon: 'angle-right',
className: 'header-btn right',
click: this.renderPopover.bind(this, { date: date.clone().add(20, 'y'), type: NDateTime.FORMAT_TYPES.YEAR })
})
);
},
renderYearBody: function (date) {
var _this = this;
var day = this.getValueFromModel();
if (day == null) {
day = this.getToday();
}
var value = this.getValueFromModel();
var today = this.getToday();
var years = [];
for (var index = -10; index <= 9; index++) {
var year = date.clone().set({ month: day.month(), date: day.date(), hour: day.hour(), minute: day.minute(), second: day.second(), millisecond: day.millisecond() });
year.add(index, 'y');
years.push(year);
}
return React.createElement(
'div',
{ className: 'calendar-body month-view' },
React.createElement(
'div',
{ className: 'year-view-body-body row' },
years.map(function (year, yearIndex) {
var css = {
'cell-4-1': true,
today: year.year() == today.year(),
'current-value': value != null && year.year() == value.year()
};
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css),
onClick: _this.onYearSelected.bind(_this, year),
key: yearIndex },
year.format(_this.getBodyYearFormat())
);
})
)
);
},
renderPopoverContentFooterButton: function (options) {
return React.createElement(
'div',
{ className: 'cell-3-1', onClick: options.click },
this.renderIcon({ icon: this.getIcon(options.icon) })
);
},
renderPopoverContentFooterForMobile: function (today, type) {
var viewSwitch = null;
if (this.hasDateToDisplay() && this.hasTimeToDisplay()) {
viewSwitch = [];
// is date view, show time switch
viewSwitch.push(React.createElement(
'a',
{ href: 'javascript:void(0);',
onClick: this.switchToTimeViewOnMobile,
className: 'time-switch' + (this.state.dateViewWhenMobilePhone ? '' : ' hidden'),
key: 'date' },
React.createElement(
'span',
null,
NDateTime.TIME_SWITCH_TEXT
)
));
// not date view, show date switch
viewSwitch.push(React.createElement(
'a',
{ href: 'javascript:void(0);',
onClick: this.switchToDateViewOnMobile,
className: 'date-switch' + (this.state.dateViewWhenMobilePhone ? ' hidden' : ''),
key: 'time' },
React.createElement(
'span',
null,
NDateTime.DATE_SWITCH_TEXT
)
));
}
return React.createElement(
'div',
{ className: 'calendar-footer row' },
React.createElement(
'div',
null,
React.createElement(
'a',
{ href: 'javascript:void(0);', onClick: this.hidePopover },
React.createElement(
'span',
null,
NDateTime.CLOSE_TEXT
)
),
React.createElement(
'a',
{ href: 'javascript:void(0);', onClick: this.renderPopover.bind(this, {
date: null,
type: type,
set: true
}) },
React.createElement(
'span',
null,
NDateTime.CLEAR_TEXT
)
),
React.createElement(
'a',
{ href: 'javascript:void(0);', onClick: this.renderPopover.bind(this, {
date: today,
type: type,
set: true
}) },
React.createElement(
'span',
null,
NDateTime.TODAY_TEXT
)
),
viewSwitch
)
);
},
renderPopoverContentFooterForDesk: function (today, type) {
return React.createElement(
'div',
{ className: 'calendar-footer row' },
this.renderPopoverContentFooterButton({
icon: 'today',
click: this.renderPopover.bind(this, {
date: today,
type: type,
set: true
})
}),
this.renderPopoverContentFooterButton({
icon: 'clear',
click: this.renderPopover.bind(this, {
date: null,
type: type,
set: true
})
}),
this.renderPopoverContentFooterButton({
icon: 'close',
click: this.hidePopover
})
);
},
renderPopoverContentFooter: function (today, type) {
if (this.isMobilePhone()) {
return this.renderPopoverContentFooterForMobile(today, type);
} else {
return this.renderPopoverContentFooterForDesk(today, type);
}
},
renderEngrave: function (degree, radius, length, className, offset) {
var startLength = radius - length;
return React.createElement('line', { className: className,
x1: startLength * Math.cos(Math.PI * 2 * degree / 360) + offset,
y1: offset - startLength * Math.sin(Math.PI * 2 * degree / 360),
x2: radius * Math.cos(Math.PI * 2 * degree / 360) + offset,
y2: offset - radius * Math.sin(Math.PI * 2 * degree / 360),
key: degree });
},
render12HourDial: function (date, popoverType) {
var _this = this;
var am = date.hour() <= 11; // 0-23
var hourRadius = this.getHourRadius();
return React.createElement(
'g',
{ key: 'hour-12-dial' },
React.createElement(
'text',
{ className: 'text hour-12 am' + (am ? ' yes' : ''),
onClick: this.onAMPMSelected.bind(this, true, popoverType),
x: 0,
y: 0 },
'AM'
),
React.createElement(
'text',
{ className: 'text hour-12 pm' + (am ? '' : ' yes'),
onClick: this.onAMPMSelected.bind(this, false, popoverType),
x: NDateTime.CLOCK_RADIUS * 2,
y: 0 },
'PM'
),
React.createElement(
'text',
{ className: 'text hour-12 top-num',
x: NDateTime.CLOCK_CHAR_POS.TOP.X,
y: NDateTime.CLOCK_CHAR_POS.TOP.Y + NDateTime.CLOCK_RADIUS - hourRadius },
'0'
),
React.createElement(
'text',
{ className: 'text hour-12 left-num',
x: NDateTime.CLOCK_CHAR_POS.LEFT.X + NDateTime.CLOCK_RADIUS - hourRadius,
y: NDateTime.CLOCK_CHAR_POS.LEFT.Y },
'9'
),
React.createElement(
'text',
{ className: 'text hour-12 right-num',
x: NDateTime.CLOCK_CHAR_POS.RIGHT.X - NDateTime.CLOCK_RADIUS + hourRadius,
y: NDateTime.CLOCK_CHAR_POS.RIGHT.Y },
'3'
),
React.createElement(
'text',
{ className: 'text hour-12 bottom-num',
x: NDateTime.CLOCK_CHAR_POS.BOTTOM.X,
y: NDateTime.CLOCK_CHAR_POS.BOTTOM.Y - NDateTime.CLOCK_RADIUS + hourRadius },
'6'
),
[30, 60, 120, 150, 210, 240, 300, 330].map(function (degree) {
return _this.renderEngrave(degree, hourRadius, NDateTime.CLOCK_SMALL_ENGRAVE_LENGTH, 'big', NDateTime.CLOCK_RADIUS);
})
);
},
render24HourDial: function () {
var _this = this;
var hourRadius = this.getHourRadius();
return React.createElement(
'g',
{ key: 'hour-24-dial' },
React.createElement(
'text',
{ className: 'text hour-24 top-num',
x: NDateTime.CLOCK_CHAR_POS.TOP.X,
y: NDateTime.CLOCK_CHAR_POS.TOP.Y + NDateTime.CLOCK_RADIUS - hourRadius },
'0'
),
React.createElement(
'text',
{ className: 'text hour-24 left-num',
x: NDateTime.CLOCK_CHAR_POS.LEFT.X + NDateTime.CLOCK_RADIUS - hourRadius,
y: NDateTime.CLOCK_CHAR_POS.LEFT.Y },
'18'
),
React.createElement(
'text',
{ className: 'text hour-24 right-num',
x: NDateTime.CLOCK_CHAR_POS.RIGHT.X - NDateTime.CLOCK_RADIUS + hourRadius,
y: NDateTime.CLOCK_CHAR_POS.RIGHT.Y },
'6'
),
React.createElement(
'text',
{ className: 'text hour-24 bottom-num',
x: NDateTime.CLOCK_CHAR_POS.BOTTOM.X,
y: NDateTime.CLOCK_CHAR_POS.BOTTOM.Y - NDateTime.CLOCK_RADIUS + hourRadius },
'12'
),
[45, 135, 225, 315].map(function (degree) {
return _this.renderEngrave(degree, hourRadius, NDateTime.CLOCK_BIG_ENGRAVE_LENGTH, 'big', NDateTime.CLOCK_RADIUS);
}),
[15, 30, 60, 75, 105, 120, 150, 165, 195, 210, 240, 255, 285, 300, 330, 345].map(function (degree) {
return _this.renderEngrave(degree, hourRadius, NDateTime.CLOCK_SMALL_ENGRAVE_LENGTH, 'small', NDateTime.CLOCK_RADIUS);
})
);
},
renderMinuteDial: function () {
if (!this.hasMinute()) {
// no minute need to display
return null;
}
var _this = this;
return React.createElement(
'g',
{ key: 'minute-dial' },
React.createElement(
'text',
{ className: 'text minute top-num',
x: NDateTime.CLOCK_CHAR_POS.TOP.X,
y: NDateTime.CLOCK_CHAR_POS.TOP.Y },
'0'
),
React.createElement(
'text',
{ className: 'text minute left-num',
x: NDateTime.CLOCK_CHAR_POS.LEFT.X,
y: NDateTime.CLOCK_CHAR_POS.LEFT.Y },
'45'
),
React.createElement(
'text',
{ className: 'text minute right-num',
x: NDateTime.CLOCK_CHAR_POS.RIGHT.X,
y: NDateTime.CLOCK_CHAR_POS.RIGHT.Y },
'15'
),
React.createElement(
'text',
{ className: 'text minute bottom-num',
x: NDateTime.CLOCK_CHAR_POS.BOTTOM.X,
y: NDateTime.CLOCK_CHAR_POS.BOTTOM.Y },
'30'
),
[30, 60, 120, 150, 210, 240, 300, 330].map(function (degree) {
return _this.renderEngrave(degree, NDateTime.CLOCK_RADIUS, NDateTime.CLOCK_BIG_ENGRAVE_LENGTH, 'big', NDateTime.CLOCK_RADIUS);
}),
[6, 12, 18, 24, 36, 42, 48, 54, 66, 72, 78, 84, 96, 102, 108, 114, 126, 132, 138, 144, 156, 162, 168, 174, 186, 192, 198, 204, 216, 222, 228, 234, 246, 252, 258, 264, 276, 282, 288, 294, 306, 312, 318, 324, 336, 342, 348, 354].map(function (degree) {
return _this.renderEngrave(degree, NDateTime.CLOCK_RADIUS, NDateTime.CLOCK_SMALL_ENGRAVE_LENGTH, 'small', NDateTime.CLOCK_RADIUS);
})
);
},
renderHourHand: function (date, offset) {
var hour = date.hour();
var degree = null;
if (this.is12Hour()) {
degree = 450 - hour * 30;
} else {
degree = 450 - hour * 15;
}
var hourRadius = this.getHourRadius();
return React.createElement('line', { x1: offset + NDateTime.CLOCK_HAND_OFFSET * Math.cos(Math.PI * 2 * (degree - 180) / 360),
y1: offset - NDateTime.CLOCK_HAND_OFFSET * Math.sin(Math.PI * 2 * (degree - 180) / 360),
x2: offset + (hourRadius - NDateTime.CLOCK_HAND_OFFSET) * Math.cos(Math.PI * 2 * degree / 360),
y2: offset - (hourRadius - NDateTime.CLOCK_HAND_OFFSET) * Math.sin(Math.PI * 2 * degree / 360),
className: 'hour-hand' });
},
renderMinuteHand: function (date, radius, offset) {
if (!this.hasMinute()) {
// no minute need to display
return null;
}
var minute = date.minute();
var degree = 450 - minute * 6;
return React.createElement('line', { x1: offset + NDateTime.CLOCK_HAND_OFFSET * Math.cos(Math.PI * 2 * (degree - 180) / 360),
y1: offset - NDateTime.CLOCK_HAND_OFFSET * Math.sin(Math.PI * 2 * (degree - 180) / 360),
x2: offset + (radius - NDateTime.CLOCK_HAND_OFFSET) * Math.cos(Math.PI * 2 * degree / 360),
y2: offset - (radius - NDateTime.CLOCK_HAND_OFFSET) * Math.sin(Math.PI * 2 * degree / 360),
className: 'minute-hand' });
},
renderSecondHand: function (date, radius, offset) {
var popoverType = this.guessDisplayFormatType();
if (!this.hasSecond()) {
// no minute need to display
return null;
}
var _this = this;
var second = date.second();
var degree = 450 - second * 6;
return React.createElement('line', { x1: offset + NDateTime.CLOCK_HAND_OFFSET * Math.cos(Math.PI * 2 * (degree - 180) / 360),
y1: offset - NDateTime.CLOCK_HAND_OFFSET * Math.sin(Math.PI * 2 * (degree - 180) / 360),
x2: offset + radius * Math.cos(Math.PI * 2 * degree / 360),
y2: offset - radius * Math.sin(Math.PI * 2 * degree / 360),
className: 'second-hand' });
},
renderTime: function (date, popoverType) {
var _this = this;
var allPopoverType = this.guessDisplayFormatType();
if (!this.hasTimeToDisplay(allPopoverType)) {
return null;
}
var styles = {
float: 'left',
width: this.hasDateToDisplay(allPopoverType) ? '50%' : '100%'
};
var titleFormat = 'HH';
if (this.hasSecond()) {
titleFormat = 'HH:mm:ss';
} else if (this.hasMinute()) {
titleFormat = 'HH:mm';
}
var hiddenWhenMobile = this.state.dateViewWhenMobilePhone ? ' hidden' : '';
return React.createElement(
'div',
{ className: 'time-view' + hiddenWhenMobile, style: styles },
React.createElement(
'div',
{ className: 'calendar-header' },
date.format(titleFormat)
),
React.createElement(
'div',
{ className: 'calendar-body' },
React.createElement(
'div',
{ className: 'time-view-body-body' },
React.createElement(
'div',
{ style: { height: NDateTime.CLOCK_RADIUS * 2, width: NDateTime.CLOCK_RADIUS * 2, position: 'relative' } },
React.createElement(
'svg',
{ className: 'clock',
height: NDateTime.CLOCK_RADIUS * 2,
width: NDateTime.CLOCK_RADIUS * 2 },
this.renderMinuteDial(),
this.is12Hour() ? this.render12HourDial(date, popoverType) : this.render24HourDial(),
this.renderHourHand(date, NDateTime.CLOCK_RADIUS),
this.renderMinuteHand(date, NDateTime.CLOCK_RADIUS, NDateTime.CLOCK_RADIUS),
this.renderSecondHand(date, NDateTime.CLOCK_RADIUS, NDateTime.CLOCK_RADIUS)
),
React.createElement('div', { style: { position: 'absolute',
backgroundColor: 'transparent',
top: 0,
left: 0,
height: NDateTime.CLOCK_RADIUS * 2,
width: NDateTime.CLOCK_RADIUS * 2 },
onClick: this.onClockClicked.bind(this, popoverType) })
)
)
)
);
},
renderPopoverContent: function (options) {
var date = options ? options.date : null;
var popoverType = options ? options.type : null;
date = date ? date : this.getValueFromModel();
date = date ? date : this.getToday();
if (popoverType == null) {
popoverType = this.getInitialPopoverType() || this.guessDisplayFormatType();
if (this.isMobilePhone() && this.hasDateToDisplay(popoverType)) {
// first time show in mobile phone
this.state.dateViewWhenMobilePhone = true;
}
}
var styles = {
float: 'left',
width: this.hasTimeToDisplay(this.guessDisplayFormatType()) ? '50%' : '100%'
};
if ((popoverType & NDateTime.FORMAT_TYPES.DAY) != 0) {
// has day, YMD
this.startClockInterval(NDateTime.FORMAT_TYPES.DAY, date);
return React.createElement(
'div',
{ className: 'popover-content row' },
React.createElement(
'div',
{ className: 'date-view', style: styles },
this.renderDayHeader(date),
this.renderDayBody(date)
),
this.renderTime(date, NDateTime.FORMAT_TYPES.DAY),
this.renderPopoverContentFooter(this.getToday(), NDateTime.FORMAT_TYPES.DAY)
);
} else if ((popoverType & NDateTime.FORMAT_TYPES.MONTH) != 0) {
// has month, YM
this.startClockInterval(NDateTime.FORMAT_TYPES.MONTH, date);
return React.createElement(
'div',
{ className: 'popover-content row' },
React.createElement(
'div',
{ className: 'date-view', style: styles },
this.renderMonthHeader(date),
this.renderMonthBody(date)
),
this.renderTime(date, NDateTime.FORMAT_TYPES.MONTH),
this.renderPopoverContentFooter(this.getToday(), NDateTime.FORMAT_TYPES.MONTH)
);
} else if ((popoverType & NDateTime.FORMAT_TYPES.YEAR) != 0) {
// has year, YEAR
this.startClockInterval(NDateTime.FORMAT_TYPES.YEAR, date);
return React.createElement(
'div',
{ className: 'popover-content row' },
React.createElement(
'div',
{ className: 'date-view', style: styles },
this.renderYearHeader(date),
this.renderYearBody(date)
),
this.renderTime(date, NDateTime.FORMAT_TYPES.YEAR),
this.renderPopoverContentFooter(this.getToday(), NDateTime.FORMAT_TYPES.YEAR)
);
} else {
this.startClockInterval(popoverType, date);
// only time
return React.createElement(
'div',
{ className: 'popover-content row' },
this.renderTime(date, this.guessDisplayFormatType()),
this.renderPopoverContentFooter(this.getToday(), popoverType)
);
}
},
isPopoverMatchComponentWidth: function () {
return false;
},
hasPopoverContentWrapper: function () {
return false;
},
getPopoverContainerCSS: function () {
var displayFormatType = this.guessDisplayFormatType();
return $pt.LayoutHelper.classSet({
'n-datetime': true,
'time-only': !this.hasDateToDisplay(displayFormatType) && this.hasTimeToDisplay(displayFormatType),
'date-only': this.hasDateToDisplay(displayFormatType) && !this.hasTimeToDisplay(displayFormatType),
'date-and-time': this.hasDateToDisplay(displayFormatType) && this.hasTimeToDisplay(displayFormatType)
});
},
beforeRenderPopover: function (options) {
if (!options) {
options = {};
}
if (options.set) {
this.setValueToModel(options.date);
}
},
beforeDestoryPopover: function () {
this.stopClockInterval();
},
stopClockInterval: function () {
if (this.state.clockInterval) {
clearTimeout(this.state.clockInterval.handler);
this.state.clockInterval = null;
}
},
startClockInterval: function (popoverType, currentTime) {
if (!this.getComponentOption('runClock', true)) {
return;
}
var value = this.getValueFromModel();
if (value == null) {
this.stopClockInterval();
this.state.clockInterval = {
handler: setTimeout(function () {
this.renderPopover({ date: currentTime.add(1, 's'), type: popoverType });
}.bind(this), 1000),
type: popoverType
};
} else {
this.stopClockInterval();
}
},
/**
* check display type has time or not
* @returns {boolean}
*/
hasTimeToDisplay: function (type) {
if (typeof type === 'undefined') {
type = this.guessDisplayFormatType();
}
return (type & (NDateTime.FORMAT_TYPES.HOUR | NDateTime.FORMAT_TYPES.MINUTE | NDateTime.FORMAT_TYPES.SECOND | NDateTime.FORMAT_TYPES.MILLSECOND)) != 0;
},
/**
* check display type has date or not
* @returns {boolean}
*/
hasDateToDisplay: function (type) {
if (typeof type === 'undefined') {
type = this.guessDisplayFormatType();
}
return (type & NDateTime.FORMAT_TYPES.YMD) != 0;
},
/**
* guess display format type
* @returns {number} format type
* @see NDateTime.FORMAT_TYPES
*/
guessDisplayFormatType: function () {
var format = this.getPrimaryDisplayFormat();
var hasYear = format.indexOf('Y') != -1;
var hasMonth = format.indexOf('M') != -1;
var hasDay = format.indexOf('D') != -1;
var hasHour = format.indexOf('H') != -1;
var hasMinute = format.indexOf('m') != -1;
var hasSecond = format.indexOf('s') != -1;
var hasMillsecond = format.indexOf('S') != -1;
return (hasYear ? NDateTime.FORMAT_TYPES.YEAR : 0) + (hasMonth ? NDateTime.FORMAT_TYPES.MONTH : 0) + (hasDay ? NDateTime.FORMAT_TYPES.DAY : 0) + (hasHour ? NDateTime.FORMAT_TYPES.HOUR : 0) + (hasMinute ? NDateTime.FORMAT_TYPES.MINUTE : 0) + (hasSecond ? NDateTime.FORMAT_TYPES.SECOND : 0) + (hasMillsecond ? NDateTime.FORMAT_TYPES.MILLSECOND : 0);
},
onCalendarButtonClicked: function () {
if (!this.isEnabled() || this.isViewMode()) {
// do nothing
return;
}
this.showPopover();
if (!this.isMobilePhone()) {
this.getTextInput().focus();
} else {
this.getTextInput().blur();
}
},
onTextInputFocused: function () {
$(ReactDOM.findDOMNode(this.refs.focusLine)).toggleClass('focus');
$(ReactDOM.findDOMNode(this.refs.normalLine)).toggleClass('focus');
},
onTextInputBlurred: function () {
$(ReactDOM.findDOMNode(this.refs.focusLine)).toggleClass('focus');
$(ReactDOM.findDOMNode(this.refs.normalLine)).toggleClass('focus');
var text = this.getTextInput().val();
if (text.length == 0 || text.isBlank()) {
this.setValueToModel(null);
} else {
var date = this.convertValueFromString(text, this.getDisplayFormat());
if (date == null && text.length != 0) {
// invalid date
this.setValueToModel(null);
this.setValueToTextInput(null);
} else {
this.setValueToModel(date);
this.setValueToTextInput(this.getValueFromModel());
}
}
},
onYearSelected: function (date) {
this.setValueToModel(date);
var type = this.guessDisplayFormatType();
// no time display and only year display, hide
if (!this.hasTimeToDisplay(type) && (type & NDateTime.FORMAT_TYPES.MONTH) == 0) {
this.hidePopover();
} else {
this.renderPopover({ date: date, type: NDateTime.FORMAT_TYPES.MONTH });
}
},
onMonthSelected: function (date) {
this.setValueToModel(date);
var type = this.guessDisplayFormatType();
// no time display and no day display, hide
if (!this.hasTimeToDisplay(type) && (type & NDateTime.FORMAT_TYPES.DAY) == 0) {
this.hidePopover();
} else {
this.renderPopover({ date: date, type: NDateTime.FORMAT_TYPES.DAY });
}
},
onDaySelected: function (date) {
this.setValueToModel(date);
var type = this.guessDisplayFormatType();
// no time display, hide
if (!this.hasTimeToDisplay(type)) {
this.hidePopover();
} else {
this.renderPopover({ date: date, type: NDateTime.FORMAT_TYPES.DAY });
}
},
onClockClicked: function (popoverType, evt) {
var offset = $(evt.target).offset();
// be careful of the quadrant
var point = {
x: evt.pageX - offset.left - NDateTime.CLOCK_RADIUS,
y: NDateTime.CLOCK_RADIUS - (evt.pageY - offset.top)
};
// window.console.log('Mouse Point: ' + point.x + ',' + point.y);
// calculate the radius length of point
var length = Math.sqrt(Math.pow(point.x, 2) + Math.pow(point.y, 2));
// window.console.log('Point radius: ' + length);
// calculate it is what
if (length > 101) {
// do nothing
return;
}
var eventType = NDateTime.FORMAT_TYPES.SECOND;
if (length > NDateTime.CLOCK_RADIUS - NDateTime.CLOCK_HAND_OFFSET) {
// change second or minute or hour
if (this.hasSecond()) {
eventType = NDateTime.FORMAT_TYPES.SECOND;
} else if (this.hasMinute()) {
eventType = NDateTime.FORMAT_TYPES.MINUTE;
} else {
eventType = NDateTime.FORMAT_TYPES.HOUR;
}
} else if (length > NDateTime.CLOCK_RADIUS * NDateTime.CLOCK_HOUR_PERCENTAGE) {
// change minute or hour
if (this.hasMinute()) {
eventType = NDateTime.FORMAT_TYPES.MINUTE;
} else {
eventType = NDateTime.FORMAT_TYPES.HOUR;
}
} else {
// change hour
eventType = NDateTime.FORMAT_TYPES.HOUR;
}
// window.console.log('Event Type: ' + eventType);
// calculate degree in coordinate system
var degree = 0;
if (point.x == 0) {
degree = point.y >= 0 ? 90 : 270;
} else {
// atan is from -Math.PI/2 to Math.PI/2
degree = Math.atan(point.y / point.x) * 180 / Math.PI;
// transform to coordinate system degree
if (point.x > 0 && point.y >= 0) {
// do nothing
} else if (point.x < 0) {
degree += 180;
} else {
degree += 360;
}
}
// transform to real clock coordinate system
if (degree <= 90) {
degree = 90 - degree;
} else {
degree = 450 - degree;
}
// window.console.log('Degree: ' + degree);
var currentHour, hour, minute, second;
var date = this.getValueFromModel();
date = date == null ? this.getToday() : date;
currentHour = date.hour();
if (eventType == NDateTime.FORMAT_TYPES.SECOND) {
second = Math.floor(degree / 6) + (degree % 6 < 3 ? 0 : 1);
date.second(second);
} else if (eventType == NDateTime.FORMAT_TYPES.MINUTE) {
minute = Math.floor(degree / 6) + (degree % 6 < 3 ? 0 : 1);
date.minute(minute);
} else if (this.is12Hour()) {
hour = Math.floor(degree / 30) + (degree % 30 < 15 ? 0 : 1);
date.hour(currentHour <= 11 ? hour : hour + 12);
} else {
hour = Math.floor(degree / 15) + (degree % 15 < 7.5 ? 0 : 1);
date.hour(hour);
}
// window.console.log('Hour: [' + hour + '], Minute: [' + minute + '], Second: [' + second + ']');
this.renderPopover({ date: date, type: popoverType, set: true });
},
onAMPMSelected: function (isAM, type) {
var value = this.getValueFromModel();
value = value == null ? this.getToday() : value;
var hour = value.hour();
if (isAM) {
hour = hour > 11 ? hour - 12 : hour;
} else {
hour = hour <= 11 ? hour + 12 : hour;
}
value.hour(hour);
this.setValueToModel(value);
this.renderPopover({ date: value, type: type });
},
switchToTimeViewOnMobile: function (evt) {
var target = $(evt.target);
if (target[0].tagName === 'SPAN') {
target = target.closest('a');
}
target.addClass('hidden').siblings('.date-switch').removeClass('hidden');
this.state.dateViewWhenMobilePhone = false;
var parent = target.closest('.popover-content');
parent.children('.time-view').removeClass('hidden');
parent.children('.date-view').addClass('hidden');
},
switchToDateViewOnMobile: function (evt) {
var target = $(evt.target);
if (target[0].tagName === 'SPAN') {
target = target.closest('a');
}
target.addClass('hidden').siblings('.time-switch').removeClass('hidden');
this.state.dateViewWhenMobilePhone = true;
var parent = target.closest('.popover-content');
parent.children('.time-view').addClass('hidden');
parent.children('.date-view').removeClass('hidden');
},
onTextInputChange: function () {
// since the text input might be incorrect date format,
// or use un-strict mode to format
// cannot know the result of moment format
// move process to changing at blur event
var text = this.getTextInput().val();
if (text.length == 0 || text.isBlank()) {
this.setValueToModel(null);
} else {
var date = this.convertValueFromString(text, this.getDisplayFormat(), true);
if (date == null && text.length != 0) {
// TODO invalid date, do nothing now. donot know how to deal with it...
} else {
this.setValueToModel(date);
}
}
},
onModelChanged: function (evt) {
var newValue = evt.new;
var text = this.getTextInput().val();
if (newValue == null || (newValue + '').isBlank()) {
if (text == null || text.isBlank()) {
// do nothing
} else {
this.forceUpdate();
}
} else {
var valueDate = this.getValueFromModel();
var textDate = this.convertValueFromString(text, this.getDisplayFormat(), true);
if (valueDate.isSame(textDate)) {
// do nothing
} else {
this.forceUpdate();
}
}
},
getValueFromModel: function () {
return this.convertValueFromString(this.getModel().get(this.getDataId()));
},
setValueToModel: function (value) {
var formattedValue = value;
if (value != null) {
if (typeof value === 'string') {
if (value.isBlank()) {
formattedValue = null;
} else {
formattedValue = moment(value, this.getPrimaryDisplayFormat()).format(this.getValueFormat());
}
} else {
formattedValue = value.format(this.getPrimaryDisplayFormat());
formattedValue = moment(formattedValue, this.getPrimaryDisplayFormat()).format(this.getValueFormat());
}
}
this.getModel().set(this.getDataId(), formattedValue);
},
setValueToTextInput: function (value) {
this.getTextInput().val(this.convertValueToString(value, this.getPrimaryDisplayFormat()));
},
/**
* convert value from string
* @param value {string}
* @param format {string} optional, use value format if not passed
* @returns {moment}
*/
convertValueFromString: function (value, format, useStrict) {
var date = value == null || value.isBlank() ? null : moment(value, format ? format : this.getValueFormat(), this.getLocale(), useStrict === true ? true : undefined);
return date == null || !date.isValid() ? null : date;
},
/**
* convert value to string
* @param value {moment}
* @param format {string} optional, use value format if not passed
* @returns {string}
*/
convertValueToString: function (value, format) {
return value == null ? null : value.format(format ? format : this.getValueFormat());
},
/**
* get first day of week
* returns {number} 0-6, sunday to saturday
*/
getFirstDayOfWeek: function () {
return this.getMomentLocaleData().firstDayOfWeek();
},
/**
* get day of week.
* returns {number} 0-6, sunday to saturday
*/
getDayOfWeek: function (date) {
return date.day();
},
/**
* get days count of month
* @returns {number}
*/
getDaysOfMonth: function (date) {
var month = date.month() + 1;
switch (month) {
case 1:case 3:case 5:case 7:case 8:case 10:case 12:
return 31;
case 4:case 6:case 9:case 11:
return 30;
case 2:
return date.isLeapYear() ? 29 : 28;
default:
// never run to here
window.console.warn('Something wrong with momentjs.');
window.console.warn(date);
return 31;
}
},
getComponent: function () {
return $(ReactDOM.findDOMNode(this.refs.comp));
},
getTextInput: function () {
return $(ReactDOM.findDOMNode(this.refs.text));
},
/**
* get display format
* @returns {string}
*/
getDisplayFormat: function () {
var format = this.getComponentOption('format');
return format ? format : NDateTime.FORMAT;
},
getPrimaryDisplayFormat: function () {
var format = this.getDisplayFormat();
if (Array.isArray(format)) {
return format[0];
} else {
return format;
}
},
getHeaderMonthFormat: function () {
var format = this.getComponentOption('headerMonthFormat');
return format ? format : NDateTime.HEADER_MONTH_FORMAT;
},
getHeaderYearFormat: function () {
var format = this.getComponentOption('headerYearFormat');
return format ? format : NDateTime.HEADER_YEAR_FORMAT;
},
getBodyYearFormat: function () {
return this.getComponentOption('bodyYearFormat');
},
getMinDate: function () {
var min = this.getComponentOption('min');
if (min == null) {
min = moment('0001-01-01');
} else if (typeof min === 'function') {
min = min.call(this);
}
return min;
},
getMaxDate: function () {
var max = this.getComponentOption('max');
if (max == null) {
max = moment('9999-12-31');
} else if (typeof max === 'function') {
max = max.call(this);
}
return max;
},
/**
* get value format
* @returns {string}
*/
getValueFormat: function () {
var valueFormat = this.getComponentOption('valueFormat');
return valueFormat ? valueFormat : NDateTime.VALUE_FORMAT;
},
is12Hour: function () {
// seems mobile doesn't support event on svg and its inner nodes
// so doesn't support 12 hours format in mobile equipments
return this.getComponentOption('hour') == 12 && !this.isMobile();
},
getHourRadius: function () {
var hourRadius = NDateTime.CLOCK_RADIUS;
if (this.hasMinute()) {
// with minute and second
hourRadius *= NDateTime.CLOCK_HOUR_PERCENTAGE;
}
return hourRadius;
},
hasYear: function () {
return (this.guessDisplayFormatType() & NDateTime.FORMAT_TYPES.YEAR) != 0;
},
hasMonth: function () {
return (this.guessDisplayFormatType() & NDateTime.FORMAT_TYPES.MONTH) != 0;
},
hasDay: function () {
return (this.guessDisplayFormatType() & NDateTime.FORMAT_TYPES.DAY) != 0;
},
hasHour: function () {
return (this.guessDisplayFormatType() & NDateTime.FORMAT_TYPES.HOUR) != 0;
},
hasMinute: function () {
return (this.guessDisplayFormatType() & NDateTime.FORMAT_TYPES.MINUTE) != 0;
},
hasSecond: function () {
return (this.guessDisplayFormatType() & NDateTime.FORMAT_TYPES.SECOND) != 0;
},
/**
* get icon definition by given icon key
* @returns {string}
*/
getIcon: function (key) {
return this.getComponentOption('icons')[key];
},
getTextInViewMode: function () {
return this.convertValueToString(this.getValueFromModel(), this.getPrimaryDisplayFormat());
},
getLocale: function () {
return this.getComponentOption('locale');
},
getMomentLocaleData: function () {
return moment.localeData(this.getLocale());
},
getToday: function () {
var today = moment().locale(this.getLocale());
var defaultTime = this.getComponentOption('defaultTime');
if (defaultTime) {
if (typeof defaultTime === 'function') {
today = defaultTime.call(this, today);
} else {
today = defaultTime;
}
}
return today;
},
getInitialPopoverType: function () {
return this.getComponentOption('popoverType');
}
}));
$pt.Components.NDateTime = NDateTime;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Date, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NDateTime, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, moment, React, ReactDOM, $pt);
/**
* exception modal dialog
* z-index is 9999 and 9998, the max z-index.
*/
(function (window, $, React, ReactDOM, $pt) {
var NExceptionModal = React.createClass({
displayName: 'NExceptionModal',
statics: {
getExceptionModal: function (className) {
if ($pt.exceptionDialog === undefined || $pt.exceptionDialog === null) {
// must initial here. since the function will execute immediately after load,
// and NExceptionModal doesn't defined in that time
var exceptionContainer = $("#exception_modal_container");
if (exceptionContainer.length == 0) {
$("<div id='exception_modal_container' />").appendTo($(document.body));
}
$pt.exceptionDialog = ReactDOM.render(React.createElement($pt.Components.NExceptionModal, { className: className }), document.getElementById("exception_modal_container"));
}
return $pt.exceptionDialog;
},
TITLE: 'Exception Raised...',
Z_INDEX: 9998
},
propTypes: {
className: React.PropTypes.string
},
getDefaultProps: function () {
return {};
},
getInitialState: function () {
return {
visible: false
};
},
/**
* set z-index
*/
fixDocumentPadding: function () {
document.body.style.paddingRight = 0;
},
/**
* did update
* @param prevProps
* @param prevState
*/
componentDidUpdate: function (prevProps, prevState) {
this.fixDocumentPadding();
if (this.state.visible) {
$(document).on('keyup', this.onDocumentKeyUp);
} else {
$(document).off('keyup', this.onDocumentKeyUp);
}
},
/**
* did mount
*/
componentDidMount: function () {
this.fixDocumentPadding();
if (this.state.visible) {
$(document).on('keyup', this.onDocumentKeyUp);
} else {
$(document).off('keyup', this.onDocumentKeyUp);
}
},
componentWillUpdate: function () {
$(document).off('keyup', this.onDocumentKeyUp);
},
componentWillUnmount: function () {
$(document).off('keyup', this.onDocumentKeyUp);
},
/**
* render content
*/
renderContent: function () {
var status = this.state.status;
var statusMessage = $pt.ComponentConstants.Http_Status[status];
var message = this.state.message;
return React.createElement(
"div",
null,
React.createElement(
"h6",
null,
status,
": ",
statusMessage
),
message != null ? React.createElement(
"pre",
null,
message
) : null
);
},
/**
* render
* @returns {*}
*/
render: function () {
if (!this.state.visible) {
return null;
}
var css = {
'n-exception-modal': true,
modal: true,
fade: true,
in: true
};
if (this.props.className) {
css[this.props.className] = true;
}
return React.createElement(
"div",
null,
React.createElement("div", { className: "modal-backdrop fade in", style: { zIndex: NExceptionModal.Z_INDEX } }),
React.createElement(
"div",
{ className: $pt.LayoutHelper.classSet(css),
tabIndex: "-1",
role: "dialog",
ref: "container",
style: { display: 'block', zIndex: NExceptionModal.Z_INDEX + 1 } },
React.createElement(
"div",
{ className: "modal-danger modal-dialog", tabIndex: "0" },
React.createElement(
"div",
{ className: "modal-content", role: "document" },
React.createElement(
"div",
{ className: "modal-header" },
React.createElement(
"button",
{ className: "close",
onClick: this.hide,
"aria-label": "Close",
style: { marginTop: '-2px' } },
React.createElement(
"span",
{ "aria-hidden": "true" },
"\xD7"
)
),
React.createElement(
"h4",
{ className: "modal-title" },
NExceptionModal.TITLE
)
),
React.createElement(
"div",
{ className: "modal-body" },
this.renderContent()
)
)
)
)
);
},
onDocumentKeyUp: function (evt) {
if (evt.keyCode === 27) {
// escape
this.hide();
} else if (evt.keyCode === 9) {
// tab
// evt.preventDefault();
var target = $(evt.target);
var container = $(this.refs.container);
if (target.closest(container).length == 0) {
container.focus();
}
}
},
/**
* hide dialog
*/
hide: function () {
this.setState({ visible: false, status: null, message: null });
},
/**
* show dialog
* @param status http status
* @param message error message
*/
show: function (status, message) {
$(':focus').blur();
this.setState({ visible: true, status: status, message: message });
}
});
$pt.Components.NExceptionModal = NExceptionModal;
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NFile = React.createClass($pt.defineCellComponent({
displayName: 'NFile',
statics: {
// PROPERTY_LIST: {
// ajaxDeleteSettings: null,
// ajaxSettings: null,
// allowedFileExtensions: null,
// allowedFileTypes: null,
// allowedPreviewMimeTypes: null,
// allowedPreviewTypes: null,
// autoReplace: null,
// browseClass: null,
// browseIcon: null,
// browseLabel: null,
// browseOnZoneClick: null,
// buttonLabelClass: null,
// captionClass: null,
// customLayoutTags: null,
// customPreviewTags: null,
// defaultPreviewContent: null,
// deleteExtraData: null,
// deleteUrl: null,
// dropZoneClickTitle: null,
// dropZoneEnabled: null,
// dropZoneTitle: null,
// dropZoneTitleClass: null,
// fileActionSettings: null,
// fileSizeGetter: null,
// fileTypeSettings: null,
// initialCaption: null,
// initialPreview: null,
// initialPreviewAsData: null,
// initialPreviewConfig: null,
// initialPreviewCount: null,
// initialPreviewDelimiter: null,
// initialPreviewFileType: null,
// initialPreviewShowDelete: null,
// initialPreviewThumbTags: null,
// language: null,
// layoutTemplates: null,
// mainClass: null,
// maxFileCount: null,
// maxFileSize: null,
// maxImageHeight: null,
// maxImageWidth: null,
// maxPreviewFileSize: null,
// minFileCount: null,
// minImageHeight: null,
// minImageWidth: null,
// msgCancelled: null,
// msgErrorClass: null,
// msgFileNotFound: null,
// msgFileNotReadable: null,
// msgFilePreviewAborted: null,
// msgFilePreviewError: null,
// msgFileSecured: null,
// msgFilesTooLess: null,
// msgFilesTooMany: null,
// msgFoldersNotAllowed: null,
// msgImageHeightLarge: null,
// msgImageHeightSmall: null,
// msgImageWidthLarge: null,
// msgImageWidthSmall: null,
// msgInvalidFileExtension: null,
// msgInvalidFileType: null,
// msgLoading: null,
// msgNo: null,
// msgProgress: null,
// msgSelected: null,
// msgSizeTooLarge: null,
// msgUploadAborted: null,
// msgValidationError: null,
// msgValidationErrorClass: null,
// msgValidationErrorIcon: null,
// msgZoomModalHeading: null,
// msgZoomTitle: null,
// otherActionButtons: null,
// overwriteInitial: null,
// previewClass: null,
// previewFileExtSettings: null,
// previewFileIcon: null,
// previewFileIconClass: null,
// previewFileIconSettings: null,
// previewFileType: null,
// previewSettings: null,
// previewTemplates: null,
// previewThumbTags: null,
// previewZoomButtonClasses: null,
// previewZoomButtonIcons: null,
// previewZoomButtonTitles: null,
// previewZoomSettings: null,
// progressClass: null,
// progressCompleteClass: null,
// progressErrorClass: null,
// purifyHtml: null,
// removeClass: null,
// resizeDefaultImageType: null,
// removeIcon: null,
// removeLabel: null,
// removeFromPreviewOnError: null,
// removeTitle: null,
// resizeImage: null,
// resizeImageQuality: null,
// resizePreference: null,
// showBrowse: null,
// showAjaxErrorDetails: null,
// showCaption: null,
// showCancel: null,
// showClose: null,
// showPreview: null,
// showRemove: null,
// showUpload: null,
// showUploadedThumbs: null,
// slugCallback: null,
// textEncoding: null,
// theme: null,
// uploadAsync: null,
// uploadClass: null,
// uploadExtraData: null,
// uploadIcon: null,
// uploadLabel: null,
// uploadTitle: null,
// uploadUrl: null,
// validateInitialCount: null,
// zoomIndicator: null
// },
DEFAULT_PROPERTY_VALUES: {
browseLabel: '',
browseIcon: '<i class="fa fa-fw fa-folder-open-o"></i>',
browseClass: 'btn btn-link',
uploadLabel: '',
uploadIcon: '<i class="fa fa-fw fa-upload"></i>',
uploadClass: 'btn btn-link',
removeLabel: '',
removeIcon: '<i class="fa fa-fw fa-trash-o"></i>',
removeClass: 'btn btn-link',
showClose: false,
showPreview: true
},
MOBILE_SETTINGS: {
previewSettings: {
image: { width: "80px", height: "80px" },
html: { width: "80px", height: "80px" },
text: { width: "80px", height: "80px" },
video: { width: "80px", height: "80px" },
audio: { width: "80px", height: "80px" },
flash: { width: "80px", height: "80px" },
object: { width: "80px", height: "80px" },
other: { width: "80px", height: "80px" }
},
browseIcon: '<i class="fa fa-fw fa-plus-square"></i>',
showCaption: true,
layoutTemplates: {
caption: '<span />',
progress: '<span />'
},
showCancel: false,
dropZoneEnabled: false
}
},
getDefaultProps: function () {
return {
defaultOptions: {
mobileEnabled: true,
multiple: true,
inputName: 'fileData'
}
};
},
getInitialState: function () {
return {
monitors: {}
};
},
beforeDidMount: function () {
var input = $(ReactDOM.findDOMNode(this.refs.file));
input.fileinput(this.createDisplayOptions());
// event monitor
var _this = this;
var monitors = this.getEventMonitor();
Object.keys(monitors).forEach(function (eventKey) {
_this.state.monitors[eventKey] = function () {
var args = Array.prototype.slice.call(arguments);
// attach this component to event object
args[0].reactComponent = _this;
monitors[eventKey].apply(this, args);
};
input.on(eventKey, _this.state.monitors[eventKey]);
});
var comp = $(ReactDOM.findDOMNode(this.refs.comp));
comp.find('.kv-fileinput-caption').focus(this.onComponentFocused).blur(this.onComponentBlurred);
comp.find('.input-group-btn>.btn').focus(this.onComponentFocused).blur(this.onComponentBlurred);
},
beforeWillUnmount: function () {
var input = $(ReactDOM.findDOMNode(this.refs.file));
// event monitor
var monitors = this.getEventMonitor();
Object.keys(this.state.monitors).forEach(function (eventKey) {
input.off(eventKey, this.state.monitors[eventKey]);
}.bind(this));
// destroy the component
input.fileinput('destroy');
},
render: function () {
var css = {
'n-disabled': !this.isEnabled()
};
css[this.getComponentCSS('n-file')] = true;
css.mobile = this.isMobileEnabled();
var inputCSS = {
file: true
};
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css), ref: 'comp' },
React.createElement('input', { type: 'file',
className: $pt.LayoutHelper.classSet(inputCSS),
multiple: this.allowMultipleFiles(),
disabled: !this.isEnabled(),
name: this.getComponentOption('inputName'),
ref: 'file' }),
this.renderNormalLine(),
this.renderFocusLine()
);
},
createDisplayOptions: function () {
var _this = this;
// get all component options
var options = this.getLayout().getComponentOption();
Object.keys(options).filter(function (key) {
// the component type should be filterred
return ['type', 'multiple', 'inputName', 'mobileEnabled'].indexOf(key) == -1;
}).forEach(function (key) {
options[key] = _this.getComponentOption(key);
// console.log(key, options[key]);
if (options[key] == null) {
var defaultValue = NFile.DEFAULT_PROPERTY_VALUES[key];
// console.log(defaultValue);
if (defaultValue) {
options[key] = defaultValue;
} else {
delete options[key];
}
}
});
Object.keys(NFile.DEFAULT_PROPERTY_VALUES).forEach(function (key) {
if (options[key] == null) {
options[key] = NFile.DEFAULT_PROPERTY_VALUES[key];
}
});
if (this.isMobileEnabled()) {
Object.keys(NFile.MOBILE_SETTINGS).forEach(function (key) {
var value = NFile.MOBILE_SETTINGS[key];
if (value) {
options[key] = value;
}
});
}
return options;
},
allowMultipleFiles: function () {
return this.getComponentOption('multiple');
},
onComponentFocused: function () {
$(ReactDOM.findDOMNode(this.refs.focusLine)).toggleClass('focus');
$(ReactDOM.findDOMNode(this.refs.normalLine)).toggleClass('focus');
},
onComponentBlurred: function () {
$(ReactDOM.findDOMNode(this.refs.focusLine)).toggleClass('focus');
$(ReactDOM.findDOMNode(this.refs.normalLine)).toggleClass('focus');
},
isMobileEnabled: function () {
return this.isMobile() && this.getComponentOption('mobileEnabled');
}
}));
$pt.Components.NFile = NFile;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.File, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NFile, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NForm = React.createClass({
displayName: 'NForm',
statics: {
LABEL_DIRECTION: 'vertical'
},
getDefaultProps: function () {
return {
next: {
icon: 'angle-double-right',
text: 'Next',
style: 'primary',
labelPosition: 'left'
},
previous: {
icon: 'angle-double-left',
text: 'Previous',
style: 'primary'
}
};
},
getInitialState: function () {
return {
activeCard: null,
next: $.extend({}, this.props.next, {
click: this.onNextClicked
}),
previous: $.extend({}, this.props.previous, {
click: this.onPreviousClicked
}),
expanded: {}
};
},
/**
* will update
* @param nextProps
*/
componentWillUpdate: function (nextProps) {
var _this = this;
this.getLayout().getCards().forEach(function (card) {
if (card.hasBadge()) {
_this.getModel().removeListener(card.getBadgeId(), 'post', 'change', _this.onModelChanged);
}
});
},
/**
* did update
* @param prevProps
* @param prevState
*/
componentDidUpdate: function (prevProps, prevState) {
var _this = this;
this.getLayout().getCards().forEach(function (card) {
if (card.hasBadge()) {
_this.getModel().addListener(card.getBadgeId(), 'post', 'change', _this.onModelChanged);
}
});
},
/**
* did mount
*/
componentDidMount: function () {
var _this = this;
this.getLayout().getCards().forEach(function (card) {
if (card.hasBadge()) {
_this.getModel().addListener(card.getBadgeId(), 'post', 'change', _this.onModelChanged);
}
});
},
/**
* will unmount
*/
componentWillUnmount: function () {
var _this = this;
this.getLayout().getCards().forEach(function (card) {
if (card.hasBadge()) {
_this.getModel().removeListener(card.getBadgeId(), 'post', 'change', _this.onModelChanged);
}
});
},
/**
* render sections
* @param sections {[SectionLayout]}
* @returns {XML}
*/
renderSections: function (sections) {
var layout = {};
var _this = this;
sections.forEach(function (section) {
var cell = {
label: section.getLabel(),
comp: {
type: $pt.ComponentConstants.Panel,
style: section.getStyle(),
expanded: section.isExpanded(),
collapsible: section.isCollapsible(),
visible: section.getVisible(),
expandedLabel: section.getExpandedLabel(),
collapsedLabel: section.getCollapsedLabel()
},
pos: {
width: section.getWidth(),
col: section.getColumnIndex(),
row: section.getRowIndex()
}
};
if (section.hasCheckInTitle()) {
cell.comp.checkInTitle = {
data: section.getCheckInTitleDataId(),
collapsible: section.getCheckInTitleCollapsible(),
label: section.getCheckInTitleLabel()
};
var otherOptions = section.getCheckInTitleOption();
Object.keys(otherOptions).forEach(function (key) {
cell.comp.checkInTitle[key] = otherOptions[key];
});
}
cell.comp.editLayout = section.getCells();
layout[_this.getSectionKey(section)] = cell;
});
var sectionLayout = {
comp: {
type: $pt.ComponentConstants.Panel,
editLayout: layout
},
pos: {
width: 12
}
};
return React.createElement($pt.Components.NPanel, { model: this.getModel(),
layout: $pt.createCellLayout(sections[0].getParentCard().getId() + '-body', sectionLayout),
direction: this.getLabelDirection(),
view: this.isViewMode() });
},
/**
* attach previous button
* @param left
* @param card
*/
attachPreviousButton: function (left, card) {
if (this.getLayout().isCardButtonShown()) {
// add default previous
if (this.isPreviousCardBackable(card.getId())) {
left.splice(0, 0, this.state.previous);
} else {
left.splice(0, 0, $.extend({
enabled: false
}, this.state.previous));
}
}
},
/**
* attach next button
* @param right {{}[]} right buttons definition
*/
attachNextButton: function (right) {
if (this.getLayout().isCardButtonShown()) {
right.push(this.state.next);
}
},
/**
* wrap custom button
* @param button {{successCallback: string, click: function}}
* @returns {{}}
*/
wrapCustomButton: function (button) {
var _this = this;
var newButton = $.extend({}, button);
if (button.successCallback === 'next') {
newButton.click = function (model) {
if (button.click.call(_this, model)) {
_this.onNextClicked();
}
};
} else if (button.successCallback === 'prev') {
newButton.click = function (model) {
if (button.click.call(_this, model)) {
_this.onPreviousClicked();
}
};
} else if (button.successCallback === 'return') {
newButton.click = function (model) {
var cardId = button.click.call(_this, model);
if (typeof cardId === 'string') {
_this.jumpToCard(cardId);
}
};
}
return newButton;
},
/**
* wrap custom buttons
* @param buttons
* @returns {{}[]}
*/
wrapCustomButtons: function (buttons) {
if (buttons == null) {
return null;
} else if (Array.isArray(buttons)) {
var _this = this;
return buttons.map(function (button) {
return _this.wrapCustomButton(button);
});
} else {
return [this.wrapCustomButton(buttons)];
}
},
/**
* render card
* @param card {CardLayout}
* @param isCards {boolean}
* @param index {number}
* @returns {XML}
*/
renderCard: function (card, isCards, index) {
var css = {
'n-card': true
};
var right = [];
right.push.apply(right, this.wrapCustomButtons(card.getRightButtons()));
var left = [];
left.push.apply(left, this.wrapCustomButtons(card.getLeftButtons()));
var footer = null;
if (isCards) {
css['n-card-active'] = card.getId() == this.state.activeCard;
if (index == 0) {
// first card
this.attachNextButton(right);
} else if (index == this.props.layout.getCards().length - 1) {
// last card
this.attachPreviousButton(left, card);
var finishButton = card.getFinishButton();
if (finishButton) {
right.push(finishButton);
}
} else {
// middle cards
this.attachPreviousButton(left, card);
this.attachNextButton(right);
}
} else {
// no cards, render sections directly
css['n-card-active'] = true;
}
if (right.length != 0 || left.length != 0) {
right = right.reverse();
footer = React.createElement($pt.Components.NPanelFooter, { right: right, left: left, model: this.getModel(), view: this.isViewMode() });
}
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css), key: index },
this.renderSections(card.getSections()),
footer
);
},
/**
* render badge
* @param card
* @returns {XML}
*/
renderBadge: function (card) {
if (card.hasBadge()) {
var badgeRender = card.getBadgeRender();
var badge = badgeRender ? badgeRender.call(this, this.getModel().get(card.getBadgeId()), this.getModel()) : this.getModel().get(card.getBadgeId());
return React.createElement(
'span',
{ className: 'badge' },
' ',
badge
);
} else {
return null;
}
},
/**
* render card title
* @returns {XML}
*/
renderWizards: function () {
var css = $pt.LayoutHelper.classSet({
'nav': true,
'nav-justified': true,
'nav-pills': true,
'nav-direction-vertical': false,
'n-cards-nav': true,
'n-cards-free': this.isFreeCard()
});
var _this = this;
return React.createElement(
'ul',
{ className: css, key: 'wizards' },
this.getLayout().getCards().map(function (card, cardIndex) {
var css = {
active: card.getId() == _this.state.activeCard,
before: _this.isBeforeActiveCard(card.getId()),
after: _this.isAfterActiveCard(card.getId())
};
var click = null;
if (_this.isFreeCard()) {
click = function () {
_this.jumpToCard(card.getId());
};
}
var icon = null;
if (card.getIcon() != null) {
var iconCSS = {
fa: true,
'fa-fw': true
};
iconCSS['fa-' + card.getIcon()] = true;
icon = React.createElement('span', { className: $pt.LayoutHelper.classSet(iconCSS) });
}
return React.createElement(
'li',
{ className: $pt.LayoutHelper.classSet(css), key: cardIndex },
React.createElement(
'a',
{ href: 'javascript:void(0);', onClick: click },
icon,
' ',
card.getLabel(),
_this.renderBadge(card)
)
);
})
);
},
/**
* render cards
* @returns {[XML]}
*/
renderCards: function () {
var cards = this.getLayout().getCards();
if (cards.length == 1) {
// no card needs
return this.renderCard(cards[0], false);
} else {
// cards need
var _this = this;
this.initActiveCard();
var nodes = [];
nodes.push(this.renderWizards());
var index = 0;
cards.forEach(function (card) {
nodes.push(_this.renderCard(card, true, index));
index++;
});
return nodes;
}
},
/**
* initialize active card
*/
initActiveCard: function () {
if (this.state.activeCard != null) {
return;
}
var _this = this;
var cards = this.getLayout().getCards();
cards.forEach(function (card) {
if (card.isActive()) {
_this.state.activeCard = card.getId();
}
});
if (!this.state.activeCard) {
// no card active, set first card as active
this.state.activeCard = cards[0].getId();
}
},
/**
* render
* @returns {XML}
*/
render: function () {
var css = {
'n-form': true
};
if (this.props.className) {
css[this.props.className] = true;
}
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css) },
this.renderCards()
);
},
/**
* on model changed
* @param evt
*/
onModelChanged: function (evt) {
this.forceUpdate();
},
/**
* on previous clicked
*/
onPreviousClicked: function () {
var activeIndex = this.getActiveCardIndex();
var prevCard = this.getLayout().getCards()[activeIndex - 1];
if (this.isFreeCard() || prevCard.isBackable()) {
this.setState({
activeCard: prevCard.getId()
});
}
},
/**
* on next clicked
*/
onNextClicked: function () {
var activeIndex = this.getActiveCardIndex();
var nextCard = this.getLayout().getCards()[activeIndex + 1];
this.setState({
activeCard: nextCard.getId()
});
},
/**
* jump to card
* @param cardId
*/
jumpToCard: function (cardId) {
this.setState({
activeCard: cardId
});
},
/**
* get active card index
* @param cardId optional, use activeCard if no parameter
* @return {number}
*/
getActiveCardIndex: function (cardId) {
var activeCardId = cardId ? cardId : this.state.activeCard;
var cards = this.getLayout().getCards();
var activeIndex = 0;
for (var index = 0, count = cards.length; index < count; index++) {
if (cards[index].getId() == activeCardId) {
activeIndex = index;
break;
}
}
return activeIndex;
},
/**
* get section key
* @param section
* @returns {string}
*/
getSectionKey: function (section) {
return section.getParentCard().getId() + '-' + section.getId();
},
isViewMode: function () {
var isViewMode = this.state ? this.state.isViewMode : null;
if (isViewMode == null) {
return this.props.view === true;
} else {
return isViewMode;
}
},
isFreeCard: function () {
return this.isViewMode() || this.getLayout().isFreeCard();
},
/**
* is previous card backable
* @param cardId
* @return {*}
*/
isPreviousCardBackable: function (cardId) {
if (this.isFreeCard()) {
return true;
}
var index = this.getActiveCardIndex(cardId);
var cards = this.getLayout().getCards();
return cards[index - 1].isBackable();
},
/**
* check the given card is before active card or not
* @param cardId
* @returns {boolean}
*/
isAfterActiveCard: function (cardId) {
return this.getActiveCardIndex(cardId) - this.getActiveCardIndex() > 0;
},
/**
* check the given card is after active card or not
* @param cardId
* @returns {boolean}
*/
isBeforeActiveCard: function (cardId) {
return this.getActiveCardIndex(cardId) - this.getActiveCardIndex() < 0;
},
/**
* get model
* @returns {*}
*/
getModel: function () {
return this.props.model;
},
getLabelDirection: function () {
return this.props.direction ? this.props.direction : NForm.LABEL_DIRECTION;
},
/**
* get layout
* @returns {*}
*/
getLayout: function () {
return this.props.layout;
},
/**
* get cell component, react class instance
* @param key
* @return {object}
*/
getCellComponent: function (key) {
var cell = this.refs[key];
if (cell) {
return cell.refs[key];
}
return null;
}
});
$pt.Components.NForm = NForm;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Form, function (model, layout, direction, viewMode) {
var formLayout = $pt.createFormLayout(layout.getComponentOption('editLayout'));
return React.createElement($pt.Components.NForm, $pt.LayoutHelper.transformParameters(model, formLayout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
/**
* Created by brad.wu on 9/10/2015.
*/
(function (window, $, React, ReactDOM, $pt) {
var NFormButtonFooter = React.createClass($pt.defineCellComponent({
displayName: 'NFormButtonFooter',
render: function () {
var buttonLayout = this.getButtonLayout();
return React.createElement($pt.Components.NPanelFooter, { model: this.props.model,
view: this.isViewMode(),
save: buttonLayout.save,
validate: buttonLayout.validate,
cancel: buttonLayout.cancel,
reset: buttonLayout.reset,
left: buttonLayout.left,
right: buttonLayout.right });
},
getButtonLayout: function () {
return this.getComponentOption('buttonLayout');
}
}));
$pt.Components.NFormButtonFooter = NFormButtonFooter;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.ButtonFooter, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NFormButtonFooter, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
/**
* Created by brad.wu on 8/18/2015.
* depends cell components which will be renderred in cell.
*
* the following settings are shared with all form cell components
* layout: {
* dataId: string,
* comp: {
* paintRequired: boolean,
* labelDirection: string,
* labelWidth: number
* },
* css: {
* cell: string,
* label: string
* }
* }
*/
(function (window, $, React, ReactDOM, $pt) {
var NFormCell = React.createClass($pt.defineCellComponent({
displayName: 'NFormCell',
keepRender: true,
statics: {
REQUIRED_ICON: 'asterisk',
TOOLTIP_ICON: 'question-circle',
LABEL_WIDTH: 4,
__componentRenderer: {},
registerComponentRenderer: function (type, func) {
$pt.LayoutHelper.registerComponentRenderer(type, func);
},
getComponentRenderer: function (type) {
return $pt.LayoutHelper.getComponentRenderer(type);
}
},
getDefaultProps: function () {
return {
defaultOptions: {
// paintRequired: true
},
direction: 'vertical'
};
},
beforeWillUpdate: function (nextProps) {
this.destroyPopover();
this.removeRequiredDependencyMonitor();
},
beforeDidUpdate: function (prevProps, prevState) {
this.renderPopover();
this.addRequiredDependencyMonitor();
},
beforeDidMount: function () {
this.renderPopover();
this.addRequiredDependencyMonitor();
},
beforeWillUnmount: function () {
this.destroyPopover();
this.removeRequiredDependencyMonitor();
},
destroyPopover: function () {
var comp = this.refs.comp;
if (comp != null) {
$(ReactDOM.findDOMNode(comp)).popover("destroy");
}
var tooltip = this.refs.tooltip;
if (tooltip != null) {
$(ReactDOM.findDOMNode(tooltip)).popover('destroy');
}
},
/**
* render error popover
*/
renderPopover: function () {
var tooltip = this.getComponentOption('tooltip');
if (tooltip != null) {
if (typeof tooltip === 'string') {
tooltip = {
text: tooltip
};
}
var tooltipPopover = {
title: tooltip.title,
content: tooltip.text,
placement: tooltip.position ? tooltip.position : 'top',
trigger: 'hover',
container: 'body',
html: true,
animation: false
};
$(ReactDOM.findDOMNode(this.refs.tooltip)).popover(tooltipPopover);
}
if ($pt.ComponentConstants.ERROR_POPOVER && this.getLayout().getComponentType().popover !== false && this.getModel().hasError(this.getDataId())) {
var messages = this.getModel().getError(this.getDataId());
var _this = this;
var popover = {
placement: 'top',
trigger: 'hover',
html: true,
content: messages.map(function (msg) {
return "<span style='display:block'>" + msg.format([_this.getLayout().getLabel(_this)]) + "</span>";
}),
container: 'body',
// false is very import, since when destroy popover,
// the really destroy will be invoked by some delay,
// and before really destory invoked,
// the new popover is bind by componentDidUpdate method.
// and finally new popover will be destroyed.
animation: false,
template: '<div class="popover form-cell-error" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
};
var comp = this.refs.comp;
if (comp != null) {
var dom = $(ReactDOM.findDOMNode(comp));
dom.popover(popover);
if (dom.has($(':focus')).length != 0) {
dom.popover('show');
}
}
}
},
/**
* render input component
* @param componentDefinition
*/
renderInputComponent: function (componentDefinition) {
// always pass form model to component,
// since maybe getModel() returns inner model which defined with comp: {model: another}
var direction = this.props.direction ? this.props.direction : 'vertical';
if (componentDefinition.render) {
// user defined component
return componentDefinition.render.call(this, this.getFormModel(), this.getLayout(), direction, this.isViewMode());
}
// pre-defined components
var type = componentDefinition.type;
if (!type) {
type = "text";
}
var innerComponent = $pt.LayoutHelper.getComponentRenderer(type).call(this, this.getFormModel(), this.getLayout(), direction, this.isViewMode());
return React.createElement(
'div',
{ ref: 'comp' },
innerComponent
);
},
isRequiredSignPaint: function () {
if (this.isViewMode()) {
return false;
}
// calculate the 'paintRequired' attribute
var requiredPaint = this.getComponentOption("paintRequired");
if (requiredPaint == null) {
// not given, calculate 'required' rules
requiredPaint = this.getModel().isRequired(this.getDataId());
return requiredPaint ? true : this.isRequiredSignNeeded();
} else if (typeof requiredPaint === 'boolean') {
// boolean type, return directly
return requiredPaint;
} else if (typeof requiredPaint === 'function') {
requiredPaint = requiredPaint.call(this);
if (typeof requiredPaint === 'boolean') {
// boolean type, return directly
return requiredPaint;
}
}
// calculate from model validator
return this.getModel().isRequired(this.getDataId(), requiredPaint);
},
/**
* render label
* @returns {XML}
*/
renderLabel: function () {
var labelIcon = this.getComponentOption('labelIcon');
var iconLabel = labelIcon ? React.createElement('span', { className: 'label-icon fa fa-fw fa-' + labelIcon }) : null;
var requireIconCSS = {
fa: true,
'fa-fw': true,
required: true
};
requireIconCSS['fa-' + NFormCell.REQUIRED_ICON] = true;
var requiredLabel = this.isRequiredSignPaint() ? React.createElement('span', { className: $pt.LayoutHelper.classSet(requireIconCSS) }) : null;
var tooltip = this.getComponentOption('tooltip');
var tooltipIcon = null;
var tooltipCSS = {
fa: true,
'fa-fw': true,
'n-form-cell-tooltip': true
};
if (tooltip != null) {
tooltipCSS['fa-' + NFormCell.TOOLTIP_ICON] = true;
tooltipIcon = React.createElement('span', { className: $pt.LayoutHelper.classSet(tooltipCSS), ref: 'tooltip' });
}
return React.createElement(
'span',
{ className: this.getLayout().getLabelCSS(),
onClick: this.onLabelClicked,
ref: 'label' },
iconLabel,
this.getLayout().getLabel(this),
tooltipIcon,
requiredLabel,
this.getComponentOption('customLabelAddon')
);
},
/**
* render
* @returns {XML}
*/
render: function () {
if (!this.isVisible()) {
return React.createElement('div', { className: this.getCSSClassName() + ' n-form-cell-invisible' });
} else {
var css = this.getCSSClassName();
if (this.getModel().hasError(this.getDataId()) && this.getLayout().getComponentType().renderError !== false) {
css += " has-error";
}
if (!this.isEnabled()) {
css += ' n-form-cell-disabled';
}
// read component definition
var type = this.getLayout().getComponentType();
if (type.label === false) {
return React.createElement(
'div',
{ className: css, ref: 'div' },
this.renderInputComponent(type)
);
} else {
var labelDirection = this.getComponentOption("labelDirection");
if (labelDirection == null) {
labelDirection = this.props.direction ? this.props.direction : 'vertical';
}
if (labelDirection != 'vertical') {
return React.createElement(
'div',
{ className: css + ' horizontal-label', ref: 'div' },
React.createElement(
'div',
{ className: 'row' },
React.createElement(
'div',
{ className: this.getHorizontalLabelCSS() },
this.renderLabel()
),
React.createElement(
'div',
{ className: this.getHorizontalComponentCSS() },
this.renderInputComponent(type)
)
)
);
} else {
return React.createElement(
'div',
{ className: css + ' vertical-label', ref: 'div' },
this.renderLabel(),
this.renderInputComponent(type)
);
}
}
}
},
/**
* on model change
* @param evt
*/
onModelChanged: function (evt) {
var delay = this.getValidationOption('delay', this.getLayout().getComponentType().delay);
if (delay != null && delay > 0) {
if (this.state.delayedValidation) {
window.clearTimeout(this.state.delayedValidation);
}
this.state.delayedValidation = window.setTimeout(function () {
this.validate();
}.bind(this), delay);
} else {
// no delay for validation
this.validate();
}
},
/**
* on label clicked
*/
onLabelClicked: function () {
$(ReactDOM.findDOMNode(this.refs.comp)).focus();
},
/**
* get css class
* @returns {string}
*/
getCSSClassName: function () {
var width = this.getLayout().getWidth();
var css = {
'n-form-cell': true
};
if (typeof width === 'number' || typeof width === 'string') {
css['col-sm-' + width] = true;
css['col-md-' + width] = true;
css['col-lg-' + width] = true;
} else {
Object.keys(width).forEach(function (key) {
css['col-' + key + '-' + width[key]] = true;
});
if (typeof width.sm === 'undefined') {
css['col-sm-' + width.width] = true;
}
if (typeof width.md === 'undefined') {
css['col-md-' + width.width] = true;
}
if (typeof width.lg === 'undefined') {
css['col-lg-' + width.width] = true;
}
// css['col-sm-' + (width.sm ? width.sm : width.width)] = true;
// css['col-md-' + (width.md ? width.md : width.width)] = true;
// css['col-lg-' + (width.lg ? width.lg : width.width)] = true;
}
return this.getLayout().getCellCSS($pt.LayoutHelper.classSet(css));
},
/**
* get label css when horizontal direction
* @returns {string}
*/
getHorizontalLabelCSS: function () {
var width = this.getHorizontalLabelWidth();
return "col-sm-" + width + " col-md-" + width + " col-lg-" + width;
},
/**
* get component css when horizontal direction
* @returns {string}
*/
getHorizontalComponentCSS: function () {
var width = 12 - this.getHorizontalLabelWidth();
return "col-sm-" + width + " col-md-" + width + " col-lg-" + width;
},
getHorizontalLabelWidth: function () {
var width = this.getComponentOption('labelWidth');
return width ? width : NFormCell.LABEL_WIDTH;
},
/**
* register to component central
*/
registerToComponentCentral: function () {
var id = this.getComponentCentralId();
if (id) {
$pt.LayoutHelper.registerComponent(id + '@cell', this);
}
},
/**
* unregsiter from component central
*/
unregisterFromComponentCentral: function () {
var id = this.getComponentCentralId();
if (id) {
$pt.LayoutHelper.unregisterComponent(id + '@cell', this);
}
}
}));
$pt.Components.NFormCell = NFormCell;
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NFormTab = React.createClass($pt.defineCellComponent({
displayName: 'NFormTab',
getDefaultProps: function () {
return {
defaultOptions: {
tabType: 'tab',
justified: false,
titleDirection: 'horizontal'
}
};
},
beforeWillUpdate: function (nextProps) {
this.uninstallBadgeMonitor();
},
beforeDidUpdate: function (prevProps, prevState) {
this.installBadgeMonitor();
},
beforeDidMount: function () {
this.installBadgeMonitor();
},
beforeWillUnmount: function () {
this.uninstallBadgeMonitor();
},
installBadgeMonitor: function () {
this.getTabs().forEach(function (tab) {
if (tab.badgeId) {
this.addDependencyMonitor([tab.badgeId]);
}
}.bind(this));
},
uninstallBadgeMonitor: function () {
this.getTabs().forEach(function (tab) {
if (tab.badgeId) {
this.removeDependencyMonitor([tab.badgeId]);
}
}.bind(this));
},
renderTabContent: function (layout, index) {
var activeIndex = this.getActiveTabIndex();
var css = {
'n-form-tab-card': true,
show: index == activeIndex,
hide: index != activeIndex
};
return React.createElement($pt.Components.NForm, { model: this.getModel(),
layout: layout,
direction: this.props.direction,
view: this.isViewMode(),
className: $pt.LayoutHelper.classSet(css),
key: 'form-' + index });
},
render: function () {
var tabs = this.initializeTabs();
var canActive = this.getComponentOption('canActive');
if (canActive) {
canActive.bind(this);
}
return React.createElement(
'div',
{ className: this.getComponentCSS('n-form-tab') },
React.createElement($pt.Components.NTab, { type: this.getComponentOption('tabType'),
justified: this.getComponentOption('justified'),
direction: this.getComponentOption('titleDirection'),
size: this.getComponentOption('titleIconSize'),
tabClassName: this.getAdditionalCSS('tabs'),
tabs: tabs,
canActive: canActive,
onActive: this.onTabClicked,
ref: 'tabs' }),
React.createElement(
'div',
{ className: 'n-form-tab-content', ref: 'content' },
this.getTabLayouts().map(this.renderTabContent)
)
);
},
getTabs: function () {
return this.getComponentOption('tabs');
},
initializeTabs: function () {
var _this = this;
var tabs = this.getTabs();
tabs.forEach(function (tab) {
if (tab.badgeId) {
tab.badge = _this.getModel().get(tab.badgeId);
if (tab.badgeRender) {
tab.badge = tab.badgeRender.call(_this, tab.badge, _this.getModel());
}
}
});
return tabs;
},
/**
* get tab layouts
* @returns {FormLayout[]}
*/
getTabLayouts: function () {
return this.getTabs().map(function (tab) {
return $pt.createFormLayout(tab.layout || tab.editLayout);
});
},
/**
* on tab clicked
* @param tabValue {string} tab value
* @param index {number}
*/
onTabClicked: function (tabValue, index) {
this.setActiveTabIndex(index);
var onActive = this.getComponentOption('onActive');
if (onActive) {
onActive.call(this, tabValue, index);
}
},
/**
* get active tab index
* @returns {number}
*/
getActiveTabIndex: function () {
var tabs = this.getComponentOption('tabs');
// find the active tab
var activeTabIndex = tabs.findIndex(function (tab, index) {
return tab.active === true;
});
if (activeTabIndex == -1) {
// find the first visible tab if no active tab found
activeTabIndex = tabs.findIndex(function (tab, index) {
var visible = tab.visible !== false;
if (visible) {
tab.active = true;
return true;
}
});
}
return activeTabIndex;
},
/**
* set active tab index
* @param {number}
*/
setActiveTabIndex: function (index) {
this.refs.tabs.setActiveTabIndex(index);
this.forceUpdate();
}
}));
$pt.Components.NFormTab = NFormTab;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Tab, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NFormTab, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
/**
* icon based on font-awesome
*/
(function (window, $, React, ReactDOM, $pt) {
var NIcon = React.createClass({
displayName: 'NIcon',
getDefaultProps: function () {
return {
fixWidth: false,
spin: false
};
},
/**
* get size
* @returns {*}
*/
getSize: function () {
var size = {
"fa-lg": this.props.size === "lg",
"fa-2x": this.props.size === "2x",
"fa-3x": this.props.size === "3x",
"fa-4x": this.props.size === "4x",
"fa-5x": this.props.size === "5x",
"fa-fw": this.props.fixWidth
};
if (this.props.size) {
size['fa-' + this.props.size] = true;
}
return size;
},
/**
* get icon
* @returns {*}
*/
getIcon: function () {
var c = {
"fa": true,
"fa-spin": this.props.spin,
"fa-pulse": this.props.pulse,
"fa-rotate-90": this.props.rotate == 90,
"fa-rotate-180": this.props.rotate == 180,
"fa-rotate-270": this.props.rotate == 270,
"fa-flip-horizontal": this.props.flip === "h",
"fa-flip-vertical": this.props.flip === "v"
};
c["fa-" + this.props.icon] = true;
if (this.props.iconClassName) {
c[this.props.iconClassName] = true;
}
return c;
},
/**
* get background icon
* @returns {*}
*/
getBackIcon: function () {
var c = {
"fa": true,
"fa-spin": this.props.backSpin,
"fa-pulse": this.props.backPulse,
"fa-rotate-90": this.props.backRotate == 90,
"fa-rotate-180": this.props.backRotate == 180,
"fa-rotate-270": this.props.backRotate == 270,
"fa-flip-horizontal": this.props.backFlip === "h",
"fa-flip-vertical": this.props.backFlip === "v"
};
c["fa-" + this.props.backIcon] = true;
if (this.props.backClassName) {
c[this.props.backClassName] = true;
}
return c;
},
/**
* render
* @returns {XML}
*/
render: function () {
var size = this.getSize();
var iconClasses = this.getIcon();
if (this.props.backIcon) {
size["fa-stack"] = true;
iconClasses['fa-stack-1x'] = true;
var backIconClasses = this.getBackIcon();
backIconClasses['fa-stack-2x'] = true;
return React.createElement(
"span",
{ className: $pt.LayoutHelper.classSet(size), title: this.props.tooltip },
React.createElement("i", { className: $pt.LayoutHelper.classSet(iconClasses) }),
React.createElement("i", { className: $pt.LayoutHelper.classSet(backIconClasses) })
);
}
return React.createElement("span", { className: $pt.LayoutHelper.classSet($.extend(iconClasses, size)),
title: this.props.tooltip });
}
});
$pt.Components.NIcon = NIcon;
})(window, jQuery, React, ReactDOM, $pt);
/**
* Jumbortron
*/
(function (window, $, React, ReactDOM, $pt) {
var NJumbortron = React.createClass({
displayName: 'NJumbortron',
renderText: function () {
if (Array.isArray(this.props.highlightText)) {
return this.props.highlightText.map(function (text, textIndex) {
return React.createElement(
"h4",
{ key: textIndex },
text
);
});
} else {
return React.createElement(
"h4",
null,
this.props.highlightText
);
}
},
render: function () {
return React.createElement(
"div",
{ className: "n-jumbotron jumbotron" },
this.renderText()
);
}
});
$pt.Components.NJumbortron = NJumbortron;
})(window, jQuery, React, ReactDOM, $pt);
/**
* Created by brad.wu on 8/21/2015.
*/
(function (window, $, React, ReactDOM, $pt) {
var NLabel = React.createClass($pt.defineCellComponent({
displayName: 'NLabel',
getDefaultProps: function () {
return {
defaultOptions: {
textFromModel: true
}
};
},
render: function () {
var texts = this.getText();
if (!Array.isArray(texts)) {
var convertor = this.getComponentOption('convertor');
if (convertor && typeof convertor === 'function') {
texts = [convertor.call(this, texts)];
} else if (convertor && convertor.view) {
// for NText compatibility
texts = [convertor.view.call(this, texts)];
} else {
var currency = this.getComponentOption('currency');
if (currency && texts != null && !(texts + '').isBlank()) {
var fraction = this.getComponentOption('fraction');
fraction = fraction ? fraction * 1 : 0;
texts = (texts + '').currencyFormat(fraction);
}
if (texts == null || (texts + '').isBlank()) {
texts = this.getComponentOption('replaceBlank') || this.getComponentOption('placeholder');
}
var left = this.getComponentOption('left');
var right = this.getComponentOption('right');
texts = left ? left + texts : texts;
texts = right ? texts + right : texts;
texts = [texts];
}
}
var css = {
'n-disabled': !this.isEnabled()
};
css[this.getComponentCSS('n-label')] = true;
var style = this.getComponentOption('style');
if (style) {
css['n-label-' + style] = true;
}
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css) },
texts.map(function (text, textIndex) {
return React.createElement(
'span',
{ key: textIndex },
text
);
})
);
},
getText: function () {
if (this.isTextFromModel()) {
return this.getValueFromModel();
} else {
return this.getLayout().getLabel(this);
}
},
isTextFromModel: function () {
return this.getComponentOption('textFromModel') !== false;
}
}));
$pt.Components.NLabel = NLabel;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Label, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NLabel, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
/**
* modal confirm dialog
* z-index is 9699 and 9698, less than exception dialog, on request dialog and code search dialog, more than any other.
*
* depends NFormButton
*/
(function (window, $, React, ReactDOM, $pt) {
var NConfirm = React.createClass({
displayName: 'NConfirm',
statics: {
getConfirmModal: function (className) {
if ($pt.confirmDialog === undefined || $pt.confirmDialog === null) {
var confirmContainer = $("#confirm_modal_container");
if (confirmContainer.length == 0) {
$("<div id='confirm_modal_container' />").appendTo($(document.body));
}
$pt.confirmDialog = ReactDOM.render(React.createElement($pt.Components.NConfirm, { className: className }), document.getElementById("confirm_modal_container"));
}
return $pt.confirmDialog;
},
OK_TEXT: 'OK',
OK_ICON: 'check',
CLOSE_TEXT: 'Close',
CLOSE_ICON: 'ban',
CANCEL_TEXT: 'Cancel',
CANCEL_ICON: 'ban',
Z_INDEX: 9698
},
getDefaultProps: function () {
return {};
},
getInitialState: function () {
return {
visible: false
};
},
/**
* set z-index
*/
fixDocumentPadding: function () {
document.body.style.paddingRight = 0;
},
/**
* did update
* @param prevProps
* @param prevState
*/
componentDidUpdate: function (prevProps, prevState) {
this.fixDocumentPadding();
if (this.state.visible) {
$(document).on('keyup', this.onDocumentKeyUp);
} else {
$(document).off('keyup', this.onDocumentKeyUp);
}
},
/**
* did mount
*/
componentDidMount: function () {
this.fixDocumentPadding();
if (this.state.visible) {
$(document).on('keyup', this.onDocumentKeyUp);
} else {
$(document).off('keyup', this.onDocumentKeyUp);
}
},
componentWillUpdate: function () {
$(document).off('keyup', this.onDocumentKeyUp);
},
componentWillUnmount: function () {
$(document).off('keyup', this.onDocumentKeyUp);
},
/**
* render confirm button
* @returns {XML}
*/
renderConfirmButton: function () {
if (this.state.options && this.state.options.disableConfirm) {
return null;
}
var layout = $pt.createCellLayout('pseudo-button', {
label: this.state.options && this.state.options.confirmText ? this.state.options.confirmText : NConfirm.OK_TEXT,
comp: {
type: $pt.ComponentConstants.Button,
icon: NConfirm.OK_ICON,
style: 'primary',
click: this.onConfirmClicked
}
});
return React.createElement($pt.Components.NFormButton, { layout: layout, model: $pt.createModel({}) });
},
/**
* render close button
* @returns {XML}
*/
renderCloseButton: function () {
if (this.state.options && this.state.options.disableClose) {
return null;
}
var layout = $pt.createCellLayout('pseudo-button', {
label: this.state.options && this.state.options.close ? NConfirm.CLOSE_TEXT : NConfirm.CANCEL_TEXT,
comp: {
type: $pt.ComponentConstants.Button,
icon: this.state.options && this.state.options.close ? NConfirm.CLOSE_ICON : NConfirm.CANCEL_ICON,
style: 'danger',
click: this.onCancelClicked
}
});
return React.createElement($pt.Components.NFormButton, { layout: layout, model: $pt.createModel({}) });
},
renderDialogCloseButton: function () {
if (this.isDialogCloseShown()) {
return React.createElement(
"button",
{ className: "close",
onClick: this.onCancelClicked,
"aria-label": "Close",
style: { marginTop: '-2px' } },
React.createElement(
"span",
{ "aria-hidden": "true" },
"\xD7"
)
);
}
return null;
},
/**
* render footer
* @returns {XML}
*/
renderFooter: function () {
if (this.state.options && this.state.options.disableButtons) {
return React.createElement("div", { className: "modal-footer-empty" });
}
return React.createElement(
"div",
{ className: "modal-footer" },
this.renderCloseButton(),
this.renderConfirmButton()
);
},
/**
* render content
*/
renderContent: function () {
var messages = this.state.options;
if (typeof messages === "string") {
messages = [messages];
}
if (!Array.isArray(messages)) {
messages = messages.messages;
if (typeof messages === "string") {
messages = [messages];
}
}
// string array
return messages.map(function (element, index) {
return React.createElement(
"h6",
{ key: index },
element
);
});
},
/**
* render
* @returns {XML}
*/
render: function () {
if (!this.state.visible) {
return null;
}
var css = {
'n-confirm': true,
modal: true,
fade: true,
in: true
};
if (this.props.className) {
css[this.props.className] = true;
}
return React.createElement(
"div",
null,
React.createElement("div", { className: "modal-backdrop fade in", style: { zIndex: NConfirm.Z_INDEX } }),
React.createElement(
"div",
{ className: $pt.LayoutHelper.classSet(css),
tabIndex: "-1",
role: "dialog",
ref: "container",
style: { display: 'block', zIndex: NConfirm.Z_INDEX + 1 } },
React.createElement(
"div",
{ className: "modal-dialog", tabIndex: "0" },
React.createElement(
"div",
{ className: "modal-content", role: "document" },
React.createElement(
"div",
{ className: "modal-header" },
this.renderDialogCloseButton(),
React.createElement(
"h4",
{ className: "modal-title" },
this.state.title
)
),
React.createElement(
"div",
{ className: "modal-body" },
this.renderContent()
),
this.renderFooter()
)
)
)
);
},
onDocumentKeyUp: function (evt) {
if (evt.keyCode === 27) {
// escape
this.onCancelClicked();
} else if (evt.keyCode === 9) {
// tab
// evt.preventDefault();
var target = $(evt.target);
var container = $(this.refs.container);
if (target.closest(container).length == 0) {
container.focus();
}
}
},
/**
* hide dialog
*/
hide: function () {
this.setState({
visible: false,
title: null,
options: null,
onConfirm: null,
onCancel: null
});
},
/**
* on confirm clicked
*/
onConfirmClicked: function () {
if (this.state.onConfirm) {
this.state.onConfirm.call(this);
}
this.hide();
if (this.state.afterClose) {
this.state.afterClose.call(this, 'confirm');
}
},
/**
* on cancel clicked
*/
onCancelClicked: function () {
if (this.state.onCancel) {
this.state.onCancel.call(this);
}
this.hide();
if (this.state.afterClose) {
this.state.afterClose.call(this, 'cancel');
}
},
isDialogCloseShown: function () {
return this.state && this.state.buttons && this.state.buttons.disableDialogClose === true;
},
/**
* show dialog
*
* from 0.0.3
* all parameters should be pass to #show in first as a JSON object
*
* @param title deprecated title of dialog
* @param options string or string array, or object as below.
* {
* disableButtons: true, // hide button bar
* disableConfirm: true, // hide confirm button
* disableClose: true, // hide close button
* messsages: "", // string or string array,
* close: true, // show close button text as "close"
* onConfirm: function,
* onCancel: function,
* afterClose: function,
* title: string
* }
* @param onConfirm deprecated callback function when confirm button clicked
* @param onCancel deprecated callback function when cancel button clicked
*/
show: function (title, options, onConfirm, onCancel) {
$(':focus').blur();
var state;
if (typeof title === 'string') {
state = {
visible: true,
title: title,
options: options,
onConfirm: onConfirm,
onCancel: onCancel,
afterClose: options.afterClose
};
} else {
// for new API
options = title;
state = {
visible: true,
title: options.title,
options: {
disableButtons: options.disableButtons,
disableConfirm: options.disableConfirm,
disableClose: options.disableClose,
disableDialogClose: options.disableDialogClose,
confirmText: options.confirmText,
close: options.close,
messages: options.messages
},
onConfirm: options.onConfirm,
onCancel: options.onCancel,
afterClose: options.afterClose
};
}
this.setState(state);
}
});
$pt.Components.NConfirm = NConfirm;
})(window, jQuery, React, ReactDOM, $pt);
/**
* modal form dialog
*
* depends NPanelFooter, NForm, NConfirm
*/
(function (window, $, React, ReactDOM, $pt) {
var NModalForm = React.createClass({
displayName: 'NModalForm',
statics: {
/**
* create form modal dialog
* @param title
* @param className
* @returns {object}
*/
createFormModal: function (title, className) {
if ($pt.formModalIndex === undefined || $pt.formModalIndex === null) {
$pt.formModalIndex = 1500;
} else {
$pt.formModalIndex += 1;
}
var containerId = "form_modal_container_" + $pt.formModalIndex;
var container = $("#" + containerId);
if (container.length == 0) {
$("<div id='" + containerId + "' />").appendTo($(document.body));
}
var css = {
"n-modal-form": true
};
if (className) {
css[className] = true;
}
return ReactDOM.render(React.createElement($pt.Components.NModalForm, { title: title, className: $pt.LayoutHelper.classSet(css),
zIndex: $pt.formModalIndex }), document.getElementById(containerId));
},
RESET_CONFIRM_TITLE: "Reset Data",
RESET_CONFIRM_MESSAGE: ["Are you sure to reset data?", "All data will be lost and cannot be recovered."],
CANCEL_CONFIRM_TITLE: "Cancel Editing",
CANCEL_CONFIRM_MESSAGE: ["Are you sure to cancel current operating?", "All data will be lost and cannot be recovered."]
},
getInitialState: function () {
return {
visible: false,
expanded: true,
collapsible: false,
draggable: true
};
},
/**
* set z-index
*/
fixDocumentPadding: function () {
document.body.style.paddingRight = 0;
},
setDraggable: function () {
if (!this.isDraggable() || !this.refs.top) {
return;
}
var top = $(ReactDOM.findDOMNode(this.refs.top));
var modal = top.children('.modal');
modal.drags({ handle: '.modal-header' });
modal.css({
overflow: 'visible',
height: 0
});
var dialog = modal.children('.modal-dialog');
dialog.css({
height: 0
});
if (!this.state.modal) {
top.find('.modal-backdrop').hide();
}
// the initial position
if (this.state.pos) {
// dialog content position is relative to dialog.
// dialog has margin.
var dialogPosition = {
top: parseInt(dialog.css('margin-top')),
left: parseInt(dialog.css('margin-left')),
bottom: parseInt(dialog.css('margin-bottom')),
right: parseInt(dialog.css('margin-right'))
};
var content = dialog.children('.modal-content');
var contentPosition = {};
var currentContentTop = parseInt(content.css('top'));
if (isNaN(currentContentTop)) {
if (this.state.pos.bottom != null) {
contentPosition.bottom = dialogPosition.bottom + content.height() - $(window).height();
} else if (this.state.pos.top != null) {
contentPosition.top = this.state.pos.top - dialogPosition.top;
}
} else {
contentPosition.top = currentContentTop;
}
var currentContentLeft = parseInt(content.css('left'));
if (isNaN(currentContentLeft)) {
if (this.state.pos.right != null) {
contentPosition.right = this.state.pos.right - dialogPosition.right;
} else if (this.state.pos.left != null) {
contentPosition.left = this.state.pos.left - dialogPosition.left;
}
} else {
contentPosition.left = currentContentLeft;
}
if (Object.keys(contentPosition).length > 0) {
content.css(contentPosition);
}
}
},
stopDraggable: function () {
if (this.refs.top) {
var top = $(ReactDOM.findDOMNode(this.refs.top));
var modal = top.children('.modal');
modal.stopDrags({ handle: '.modal-header' });
}
},
/**
* did update
* @param prevProps
* @param prevState
*/
componentDidUpdate: function (prevProps, prevState) {
this.fixDocumentPadding();
this.setDraggable();
if (this.isDialogCloseShown()) {
$(document).on('keyup', this.onDocumentKeyUp);
}
if (this.state.visible) {
$(document).on('keydown', this.onDocumentKeyDown);
} else {
$(document).off('keydown', this.onDocumentKeyDown);
}
},
componentWillUpdate: function () {
this.stopDraggable();
if (this.isDialogCloseShown()) {
$(document).off('keyup', this.onDocumentKeyUp);
}
$(document).off('keydown', this.onDocumentKeyDown);
},
/**
* did mount
*/
componentDidMount: function () {
this.fixDocumentPadding();
this.setDraggable();
if (this.isDialogCloseShown()) {
$(document).on('keyup', this.onDocumentKeyUp);
}
if (this.state.visible) {
$(document).on('keydown', this.onDocumentKeyDown);
} else {
$(document).off('keydown', this.onDocumentKeyDown);
}
},
componentWillUnmount: function () {
this.stopDraggable();
if (this.isDialogCloseShown()) {
$(document).off('keyup', this.onDocumentKeyUp);
}
$(document).off('keydown', this.onDocumentKeyDown);
},
/**
* render footer
* @returns {XML}
*/
renderFooter: function () {
if (this.state.footer === false || !this.state.expanded) {
return React.createElement("div", { ref: "footer" });
} else {
return React.createElement(
"div",
{ className: "n-modal-form-footer modal-footer", ref: "footer" },
React.createElement($pt.Components.NPanelFooter, { reset: this.getResetButton(),
validate: this.getValidationButton(),
save: this.getSaveButton(),
cancel: this.getCancelButton(),
left: this.getLeftButton(),
right: this.getRightButton(),
model: this.getModel(),
view: this.isViewMode() })
);
}
},
renderBody: function () {
var css = {
'modal-body': true,
hide: !this.state.expanded
};
return React.createElement(
"div",
{ className: $pt.LayoutHelper.classSet(css) },
React.createElement($pt.Components.NForm, { model: this.getModel(),
layout: this.getLayout(),
direction: this.getDirection(),
view: this.isViewMode(),
ref: "form" })
);
},
renderCloseButton: function () {
if (this.isDialogCloseShown()) {
return React.createElement(
"button",
{ className: "close",
onClick: this.hide,
"aria-label": "Close",
style: { marginTop: '-2px' } },
React.createElement(
"span",
{ "aria-hidden": "true" },
"\xD7"
)
);
}
return null;
},
/**
* render
* @returns {*}
*/
render: function () {
if (!this.state.visible) {
return null;
}
var title = this.state.title ? this.state.title : this.props.title;
if (this.isCollapsible()) {
title = React.createElement(
"a",
{ href: "javascript:void(0);", onClick: this.onTitleClicked },
title
);
}
var css = {
'n-confirm': true,
modal: true,
fade: true,
in: true
};
if (this.props.className) {
css[this.props.className] = true;
}
// tabindex="0"
return React.createElement(
"div",
{ ref: "top" },
React.createElement("div", { className: "modal-backdrop fade in", style: { zIndex: this.props.zIndex * 1 } }),
React.createElement(
"div",
{ className: $pt.LayoutHelper.classSet(css),
role: "dialog",
ref: "container",
tabIndex: "0",
style: { display: 'block', zIndex: this.props.zIndex * 1 + 1 } },
React.createElement(
"div",
{ className: "modal-dialog" },
React.createElement(
"div",
{ className: "modal-content", role: "document" },
React.createElement(
"div",
{ className: "modal-header" },
this.renderCloseButton(),
React.createElement(
"h4",
{ className: "modal-title" },
title
)
),
this.renderBody(),
this.renderFooter()
)
)
)
);
},
onDocumentKeyUp: function (evt) {
if (evt.keyCode === 27) {
// escape
this.hide();
}
},
onDocumentKeyDown: function (evt) {
// console.log(evt);
if (evt.keyCode === 9) {
// tab
// evt.preventDefault();
var target = $(evt.target);
var container = $(this.refs.container);
console.log(target.closest(container).length == 0);
if (target.closest(container).length == 0) {
container.focus();
}
}
},
/**
* on title clicked
*/
onTitleClicked: function () {
// TODO no animotion, tried, weird.
this.setState({ expanded: !this.state.expanded });
},
/**
* on reset clicked
*/
onResetClicked: function () {
var reset = function () {
this.getModel().reset();
this.refs.form.forceUpdate();
};
$pt.Components.NConfirm.getConfirmModal().show(NModalForm.RESET_CONFIRM_TITLE, NModalForm.RESET_CONFIRM_MESSAGE, reset.bind(this));
},
/**
* on validate clicked
*/
onValidateClicked: function () {
this.getModel().validate();
this.forceUpdate();
},
/**
* on cancel clicked
*/
onCancelClicked: function () {
if (this.state.buttons && typeof this.state.buttons.cancel === 'function') {
this.hide();
} else {
$pt.Components.NConfirm.getConfirmModal().show(NModalForm.CANCEL_CONFIRM_TITLE, NModalForm.CANCEL_CONFIRM_MESSAGE, this.hide);
}
},
/**
* get model
* @returns {ModelInterface}
*/
getModel: function () {
return this.state.model;
},
/**
* get layout
* @returns {FormLayout}
*/
getLayout: function () {
return this.state.layout;
},
/**
* get direction
* @returns {string}
*/
getDirection: function () {
return this.state.direction;
},
/**
* get left button configuration
* @returns {{}|{}[]}
*/
getLeftButton: function () {
return this.state.buttons ? this.state.buttons.left : null;
},
/**
* get right button configuration
* @returns {{}|{}[]}
*/
getRightButton: function () {
return this.state.buttons ? this.state.buttons.right : null;
},
/**
* get validation button
* @returns {function}
*/
getValidationButton: function () {
if (this.state.buttons && this.state.buttons.validate === false) {
return null;
} else if (this.isViewMode()) {
return null;
} else {
return this.onValidateClicked;
}
},
/**
* get cancel button
* @returns {function}
*/
getCancelButton: function () {
if (this.state.buttons && this.state.buttons.cancel === false) {
return null;
} else {
return this.onCancelClicked;
}
},
/**
* get reset button
* @returns {function}
*/
getResetButton: function () {
if (this.state.buttons && this.state.buttons.reset === false) {
return null;
} else if (this.isViewMode()) {
return null;
} else {
return this.onResetClicked;
}
},
/**
* get save button configuration
* @returns {{}}
*/
getSaveButton: function () {
return this.state.buttons ? this.state.buttons.save : null;
},
/**
* is dialog close button shown
* @returns boolean
*/
isDialogCloseShown: function () {
return this.state.buttons ? this.state.buttons.dialogCloseShown !== false : true;
},
/**
* is draggable
* @returns boolean
*/
isDraggable: function () {
return this.state.draggable || !this.state.modal;
},
/**
* is collapsible
* @returns boolean
*/
isCollapsible: function () {
return this.state.collapsible;
},
/**
* is expanded
* @returns boolean
*/
isExpanded: function () {
return this.state.expanded;
},
/**
* is view mode
* @returns boolean
*/
isViewMode: function () {
return this.state.view;
},
/**
* validate
* @returns {boolean}
*/
validate: function () {
this.getModel().validate();
this.forceUpdate();
return this.getModel().hasError();
},
/**
* hide dialog
* @return model
*/
hide: function () {
var model = this.state.model;
if (this.state.buttons && typeof this.state.buttons.cancel === 'function') {
this.state.buttons.cancel.call(this, model, function () {
this.setState({
visible: false,
model: null,
layout: null,
buttons: null
});
}.bind(this));
} else {
this.setState({
visible: false,
model: null,
layout: null,
buttons: null
});
}
return model;
},
/**
* show dialog
*
* from 0.0.3, all parameters can be defined in first as a JSON.
* @param model
* @param layout
* @param buttons
* @param direction vertical or horizontal
* @param footer {boolean}
* @param title {string}
*/
show: function (model, layout, buttons, direction, footer, title) {
if (!model.getCurrentModel) {
// test the model is ModelInterface or not
this.setState({
visible: true,
model: model.model,
layout: model.layout,
buttons: model.buttons,
direction: model.direction,
footer: model.footer,
title: model.title,
draggable: model.draggable,
modal: model.modal == null ? model.draggable ? false : true : true,
collapsible: model.collapsible,
expanded: model.expanded == null ? true : model.expanded,
pos: model.pos,
view: model.view === true
});
} else {
window.console.warn("Properties [draggable, expanded, collapsible, pos] are not supported in parameters, use JSON parameter instead.");
this.setState({
visible: true,
model: model,
layout: layout,
buttons: buttons,
direction: direction,
footer: footer,
title: title,
draggable: false,
modal: true,
expanded: true,
collapsible: false,
view: false
});
}
}
});
$pt.Components.NModalForm = NModalForm;
$.fn.drags = function (opt) {
opt = $.extend({ handle: "", cursor: "move" }, opt);
var $el = null;
if (opt.handle === "") {
$el = this;
} else {
$el = this.find(opt.handle);
}
return $el.css('cursor', opt.cursor).on("mousedown", function (e) {
var $drag = null;
if (opt.handle === "") {
$drag = $(this).addClass('draggable');
} else {
$drag = $(this).addClass('active-handle').parent().addClass('draggable');
}
var z_idx = $drag.css('z-index'),
drg_h = $drag.outerHeight(),
drg_w = $drag.outerWidth(),
pos_y = $drag.offset().top + drg_h - e.pageY,
pos_x = $drag.offset().left + drg_w - e.pageX;
// $drag.css('z-index', 1000).parents().on("mousemove", function(e) {
$drag.parents().on("mousemove", function (e) {
$('.draggable').offset({
top: e.pageY + pos_y - drg_h,
left: e.pageX + pos_x - drg_w
}).on("mouseup", function () {
$(this).removeClass('draggable').css('z-index', z_idx);
});
});
e.preventDefault(); // disable selection
}).on("mouseup", function () {
if (opt.handle === "") {
$(this).removeClass('draggable');
} else {
$(this).removeClass('active-handle').parent().removeClass('draggable');
}
});
};
$.fn.stopDrags = function (opt) {
opt = $.extend({ handle: "", cursor: "move" }, opt);
var $el = null;
if (opt.handle === "") {
$el = this;
} else {
$el = this.find(opt.handle);
}
var $drag = null;
if (opt.handle === "") {
$drag = $($el);
} else {
$drag = $($el).parent();
}
$drag.parents().off("mousemove");
return $el.off('mousedown mouseup');
};
})(window, jQuery, React, ReactDOM, $pt);
/**
* Created by brad.wu on 9/2/2015.
*/
(function (window, $, React, ReactDOM, $pt) {
var NNormalLabel = React.createClass({
displayName: 'NNormalLabel',
propTypes: {
text: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.arrayOf(React.PropTypes.string)]),
style: React.PropTypes.string,
className: React.PropTypes.string
},
getDefaultProps: function () {
return {};
},
render: function () {
var texts = this.getText();
if (!Array.isArray(texts)) {
texts = [texts];
}
var css = {
'n-normal-label': true
};
if (this.props.className) {
css[this.props.className] = true;
}
if (this.props.style) {
css['n-label-' + this.props.style] = true;
}
if (this.props.size) {
css['n-label-' + this.props.size] = true;
}
return React.createElement(
'span',
{ className: $pt.LayoutHelper.classSet(css) },
texts.map(function (text, textIndex) {
return React.createElement(
'span',
{ key: textIndex },
text
);
})
);
},
getText: function () {
return this.props.text;
}
});
$pt.Components.NNormalLabel = NNormalLabel;
})(window, jQuery, React, ReactDOM, $pt);
/**
* on request modal dialog.
* z-index is 9899 and 9898, less than exception dialog, more than any other.
*/
(function (window, $, React, ReactDOM, $pt) {
var NOnRequestModal = React.createClass({
displayName: 'NOnRequestModal',
statics: {
getOnRequestModal: function (className) {
if ($pt.onRequestDialog === undefined || $pt.onRequestDialog === null) {
var onRequestContainer = $("#onrequest_modal_container");
if (onRequestContainer.length == 0) {
$("<div id='onrequest_modal_container' />").appendTo($(document.body));
}
$pt.onRequestDialog = ReactDOM.render(React.createElement($pt.Components.NOnRequestModal, { className: className }), document.getElementById("onrequest_modal_container"));
}
return $pt.onRequestDialog;
},
WAITING_MESSAGE: 'Send request to server and waiting for response...',
Z_INDEX: 9898
},
getInitialState: function () {
return {
visible: false
};
},
/**
* set z-index
*/
fixDocumentPadding: function () {
document.body.style.paddingRight = 0;
},
/**
* did update
* @param prevProps
* @param prevState
*/
componentDidUpdate: function (prevProps, prevState) {
this.fixDocumentPadding();
if (this.state.visible) {
$(document).on('keydown', this.onDocumentKeyDown);
} else {
$(document).off('keydown', this.onDocumentKeyDown);
}
},
componentWillUpdate: function () {
$(document).off('keydown', this.onDocumentKeyDown);
},
/**
* did mount
*/
componentDidMount: function () {
this.fixDocumentPadding();
if (this.state.visible) {
$(document).on('keydown', this.onDocumentKeyDown);
} else {
$(document).off('keydown', this.onDocumentKeyDown);
}
},
componentWillUnmount: function () {
$(document).off('keydown', this.onDocumentKeyDown);
},
render: function () {
if (!this.state.visible) {
return null;
}
var css = {
'n-on-request': true,
modal: true,
fade: true,
in: true
};
if (this.props.className) {
css[this.props.className] = true;
}
return React.createElement(
"div",
null,
React.createElement("div", { className: "modal-backdrop fade in", style: { zIndex: NOnRequestModal.Z_INDEX } }),
React.createElement(
"div",
{ className: $pt.LayoutHelper.classSet(css),
tabIndex: "-1",
role: "dialog",
ref: "container",
style: { display: 'block', zIndex: NOnRequestModal.Z_INDEX + 1 } },
React.createElement(
"div",
{ className: "modal-danger modal-dialog", tabIndex: "0" },
React.createElement(
"div",
{ className: "modal-content", role: "document" },
React.createElement(
"div",
{ className: "modal-body", ref: "body" },
React.createElement("span", { className: "fa fa-fw fa-lg fa-spin fa-spinner" }),
" ",
NOnRequestModal.WAITING_MESSAGE
)
)
)
)
);
},
/**
* hide dialog
*/
hide: function () {
this.setState({ visible: false });
},
/**
* show dialog
*/
show: function () {
this.setState({ visible: true });
},
onDocumentKeyDown: function (evt) {
if (evt.keyCode === 9) {
// tab
// evt.preventDefault();
var target = $(evt.target);
var container = $(this.refs.container);
if (target.closest(container).length == 0) {
container.focus();
}
}
}
});
$pt.Components.NOnRequestModal = NOnRequestModal;
})(window, jQuery, React, ReactDOM, $pt);
/**
* page footer.<br>
*/
(function (window, $, React, ReactDOM, $pt) {
var NPageFooter = React.createClass({
displayName: 'NPageFooter',
statics: {
TECH_BASE: 'Parrot',
TECH_URL: 'https://github.com/bradwoo8621/parrot',
COMPANY: 'NEST',
COMPANY_URL: 'https://github.com/bradwoo8621/nest',
LEFT_TEXT: 'For best viewing, we recommend using the latest Chrome version.'
},
getDefaultProps: function () {
return {};
},
renderTech: function () {
if (NPageFooter.TECH_BASE != null && !NPageFooter.TECH_BASE.isBlank()) {
return React.createElement(
'span',
null,
', on ',
React.createElement(
'a',
{ href: NPageFooter.TECH_URL, target: '_blank', tabIndex: '-1' },
NPageFooter.TECH_BASE
)
);
}
return null;
},
renderCompany: function () {
if (NPageFooter.COMPANY != null && !NPageFooter.COMPANY.isBlank()) {
return React.createElement(
'span',
null,
', by ',
React.createElement(
'a',
{ href: NPageFooter.COMPANY_URL, target: '_blank', tabIndex: '-1' },
NPageFooter.COMPANY
)
);
}
return null;
},
render: function () {
return React.createElement(
'footer',
{ className: 'footer' },
React.createElement(
'div',
{ className: 'container' },
React.createElement(
'p',
{ className: 'text-muted', style: { display: 'inline-block' } },
React.createElement(
'span',
null,
NPageFooter.LEFT_TEXT
)
),
React.createElement(
'p',
{ className: 'text-muted pull-right', style: { display: 'inline-block' } },
this.props.name,
this.renderTech(),
this.renderCompany(),
'.'
)
)
);
}
});
$pt.Components.NPageFooter = NPageFooter;
})(window, jQuery, React, ReactDOM, $pt);
/**
* Page Header<br>
*/
(function (window, $, React, ReactDOM, $pt) {
var NPageHeader = React.createClass({
displayName: 'NPageHeader',
statics: {
SEARCH_PLACEHOLDER: 'Search...'
},
getDefaultProps: function () {
return {
side: false
};
},
/**
* get initial state
* @returns {*}
*/
getInitialState: function () {
return {
model: $pt.createModel({
text: null
})
};
},
componentDidMount: function () {
if (this.props.side && this.props.menus) {
this.state.sideMenu = NSideMenu.getSideMenu(this.props.menus, null, null, true);
}
},
/**
* render search box
* @returns {XML}
*/
renderSearchBox: function () {
var layout = $pt.createCellLayout('text', {
comp: {
placeholder: NPageHeader.SEARCH_PLACEHOLDER,
rightAddon: {
icon: 'search',
click: this.onSearchClicked
}
}
});
return React.createElement(
'div',
{ className: 'navbar-form navbar-right', role: 'search' },
React.createElement($pt.Components.NText, { model: this.state.model, layout: layout })
);
},
renderMenuItem: function (item, index, menus, onTopLevel) {
if (item.children !== undefined) {
// render dropdown menu
var _this = this;
return React.createElement(
'li',
{ className: onTopLevel ? "dropdown" : "dropdown-submenu", key: index },
React.createElement(
'a',
{ href: '#', className: 'dropdown-toggle', 'data-toggle': 'dropdown', role: 'button',
'aria-expanded': 'false' },
item.text,
' ',
onTopLevel ? React.createElement('span', { className: 'caret' }) : null
),
React.createElement(
'ul',
{ className: 'dropdown-menu', role: 'menu' },
item.children.map(function (childItem, childIndex, dropdownItems) {
return _this.renderMenuItem(childItem, childIndex, dropdownItems, false);
})
)
);
} else if (item.divider === true) {
// render divider
return React.createElement('li', { className: 'divider', key: index });
} else if (item.func !== undefined) {
// call javascript function
return React.createElement(
'li',
null,
React.createElement(
'a',
{ href: 'javascript:void(0);', onClick: this.onMenuClicked.bind(this, item.func), key: index },
item.text
)
);
} else {
// jump to url
return React.createElement(
'li',
{ key: index },
React.createElement(
'a',
{ href: item.url },
item.text
)
);
}
},
/**
* render menus
* @returns {XML}
*/
renderMenus: function () {
var _this = this;
var css = {
'nav navbar-nav': true
};
if (this.props.side) {
css['nav-side'] = true;
}
return React.createElement(
'ul',
{ className: $pt.LayoutHelper.classSet(css) },
this.props.menus.map(function (item, index, menu) {
return _this.renderMenuItem(item, index, menu, true);
})
);
},
renderBrand: function () {
if (this.props.brandUrl) {
return React.createElement(
'a',
{ href: this.props.brandUrl },
React.createElement(
'span',
{ className: 'navbar-brand' },
this.props.brand
)
);
} else if (this.props.brandFunc || this.props.side) {
return React.createElement(
'a',
{ href: 'javascript:void(0);',
onMouseEnter: this.onBrandMouseEnter,
onMouseLeave: this.onBrandMouseLeave,
onClick: this.onBrandClicked },
React.createElement(
'span',
{ className: 'navbar-brand' },
this.props.brand
)
);
} else {
return React.createElement(
'span',
{ className: 'navbar-brand' },
this.props.brand
);
}
},
onBrandMouseEnter: function () {
if (this.props.side) {
if (this.state.sideMenu) {
this.state.sideMenu.show();
}
}
},
onBrandMouseLeave: function () {
if (this.props.side) {
if (this.state.sideMenu) {
this.state.sideMenu.willHide();
}
}
},
/**
* on brand clicked
*/
onBrandClicked: function () {
if (this.props.brandFunc) {
this.props.brandFunc.call(this);
}
},
/**
* on menu clicked
* @param func
*/
onMenuClicked: function (func) {
func.call(this);
},
/**
* on search clicked
*/
onSearchClicked: function () {
this.props.search.call(this, this.state.model.get('text'));
},
/**
* render component
* @returns {XML}
*/
render: function () {
return React.createElement(
'nav',
{ className: 'navbar navbar-default navbar-fixed-top' },
React.createElement(
'div',
{ className: 'container-fluid' },
React.createElement(
'div',
{ className: 'navbar-header' },
React.createElement(
'button',
{ type: 'button', className: 'navbar-toggle collapsed', 'data-toggle': 'collapse',
'data-target': '#navbar-1' },
React.createElement(
'span',
{ className: 'sr-only' },
'Toggle navigation'
),
React.createElement('span', { className: 'icon-bar' }),
React.createElement('span', { className: 'icon-bar' }),
React.createElement('span', { className: 'icon-bar' })
),
this.renderBrand()
),
React.createElement(
'div',
{ className: 'collapse navbar-collapse', id: 'navbar-1' },
this.props.menus ? this.renderMenus() : null,
this.props.search ? this.renderSearchBox() : null,
this.props.custom ? this.props.custom.call(this) : null
)
)
);
}
});
$pt.Components.NPageHeader = NPageHeader;
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NPagination = React.createClass({
displayName: 'NPagination',
statics: {
PAGE_TEXT: 'Page: '
},
/**
* override react method
* @returns {*}
* @override
*/
getDefaultProps: function () {
return {
maxPageButtons: 5,
pageCount: 1, // page count default 1
currentPageIndex: 1, // page number count from 1
showStatus: true
};
},
/**
* make max page buttons is an odd number and at least 3
*/
getMaxPageButtons: function () {
var maxPageButtons = this.props.maxPageButtons * 1;
if (maxPageButtons % 2 == 0) {
maxPageButtons = maxPageButtons - 1;
}
if (maxPageButtons < 3) {
maxPageButtons = 3;
}
return maxPageButtons;
},
/**
* get buttons range
* @returns {{min: number, max: number}}
*/
getPageButtonsRange: function () {
var currentPageIndex = this.getCurrentPageIndex();
var maxPageButtons = this.getMaxPageButtons();
// calc the steps from currentPageIndex to maxPageIndex(pageCount)
var max = 0;
var availablePageCountFromCurrent = this.getPageCount() - currentPageIndex;
var maxButtonCountFromCurrent = Math.floor(maxPageButtons / 2);
if (availablePageCountFromCurrent >= maxButtonCountFromCurrent) {
//
max = currentPageIndex + maxButtonCountFromCurrent;
} else {
max = currentPageIndex + availablePageCountFromCurrent;
// move to min buttons, since no enough available pages to display
maxButtonCountFromCurrent += maxButtonCountFromCurrent - availablePageCountFromCurrent;
}
// calc the steps from currentPageIndex to first page
var min = 0;
var availablePageCountBeforeCurrent = currentPageIndex - 1;
if (availablePageCountBeforeCurrent >= maxButtonCountFromCurrent) {
min = currentPageIndex - maxButtonCountFromCurrent;
} else {
min = 1;
}
// calc the steps
if (max - min < maxPageButtons) {
// no enough buttons
max = min + maxPageButtons - 1;
max = max > this.getPageCount() ? this.getPageCount() : max;
}
return { min: min, max: max };
},
/**
* render button which jump to first page
* @param buttonsRange
* @returns {XML}
*/
renderFirst: function (buttonsRange) {
return React.createElement(
'li',
null,
React.createElement(
'a',
{ href: 'javascript:void(0);', 'aria-label': 'First', onClick: this.toFirst },
React.createElement('span', { className: 'fa fa-fw fa-fast-backward' })
)
);
},
/**
* render button which jump to previous page section
* @param buttonsRange
* @returns {XML}
*/
renderPreviousSection: function (buttonsRange) {
return React.createElement(
'li',
null,
React.createElement(
'a',
{ href: 'javascript:void(0);', 'aria-label': 'PreviousSection', onClick: this.toPreviousSection },
React.createElement('span', { className: 'fa fa-fw fa-backward' })
)
);
},
/**
* render button which jump to previous page
* @param buttonsRange
* @returns {XML}
*/
renderPrevious: function (buttonsRange) {
return React.createElement(
'li',
null,
React.createElement(
'a',
{ href: 'javascript:void(0);', 'aria-label': 'Previous', onClick: this.toPrevious },
React.createElement('span', { className: 'fa fa-fw fa-chevron-left' })
)
);
},
/**
* render buttons
* @param buttonsRange
* @returns {[XML]}
*/
renderButtons: function (buttonsRange) {
var buttons = [];
for (var index = buttonsRange.min; index <= buttonsRange.max; index++) {
buttons.push(index);
}
var _this = this;
return buttons.map(function (index) {
var css = {
active: index == _this.getCurrentPageIndex()
};
return React.createElement(
'li',
{ key: index },
React.createElement(
'a',
{ href: 'javascript:void(0);',
onClick: _this.toPage,
'data-index': index,
className: $pt.LayoutHelper.classSet(css) },
index
)
);
});
},
/**
* render button which jump to next page
* @param buttonsRange
* @returns {XML}
*/
renderNext: function (buttonsRange) {
return React.createElement(
'li',
null,
React.createElement(
'a',
{ href: 'javascript:void(0);', 'aria-label': 'Next', onClick: this.toNext },
React.createElement('span', { className: 'fa fa-fw fa-chevron-right' })
)
);
},
/**
* render button which jump to next page section
* @param buttonsRange
* @returns {XML}
*/
renderNextSection: function (buttonsRange) {
return React.createElement(
'li',
null,
React.createElement(
'a',
{ href: 'javascript:void(0);', 'aria-label': 'NextSection', onClick: this.toNextSection },
React.createElement('span', { className: 'fa fa-fw fa-forward' })
)
);
},
/**
* render button which jump to last page
* @param buttonsRange
* @returns {XML}
*/
renderLast: function (buttonsRange) {
return React.createElement(
'li',
null,
React.createElement(
'a',
{ href: 'javascript:void(0);', 'aria-label': 'Last', onClick: this.toLast },
React.createElement('span', { className: 'fa fa-fw fa-fast-forward' })
)
);
},
/**
* render status
* @returns {XML}
*/
renderStatus: function () {
if (this.props.showStatus) {
return React.createElement(
'div',
{ className: 'n-pagination-status col-sm-2 col-md-2 col-lg-2' },
React.createElement(
'div',
null,
NPagination.PAGE_TEXT,
this.getCurrentPageIndex(),
' / ',
this.getPageCount()
)
);
} else {
return null;
}
},
/**
* override react method
* @returns {XML}
* @override
*/
render: function () {
var buttonsRange = this.getPageButtonsRange();
var css = {
row: true,
'n-pagination': true
};
if (this.props.className) {
css[this.props.className] = true;
}
var buttonCSS = {
'n-pagination-buttons': true,
'col-sm-10 col-md-10 col-lg-10': this.props.showStatus,
'col-sm-12 col-md-12 col-lg-12': !this.props.showStatus
};
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css) },
this.renderStatus(),
React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(buttonCSS) },
React.createElement(
'ul',
{ className: 'pagination' },
this.renderFirst(buttonsRange),
this.renderPreviousSection(buttonsRange),
this.renderPrevious(buttonsRange),
this.renderButtons(buttonsRange),
this.renderNext(buttonsRange),
this.renderNextSection(buttonsRange),
this.renderLast(buttonsRange)
)
)
);
},
/**
* get current page index
* @param button
* @returns {*|jQuery}
*/
getCurrentPageIndex: function (button) {
if (button) {
return $(button).attr("data-index");
} else {
return this.props.currentPageIndex * 1;
}
},
getPageCount: function () {
return this.props.pageCount * 1;
},
/**
* jump to first page
*/
toFirst: function () {
this.jumpTo(1);
},
/**
* jump to previous page section
*/
toPreviousSection: function () {
var previousIndex = this.getCurrentPageIndex() - this.getMaxPageButtons();
previousIndex = previousIndex < 1 ? 1 : previousIndex;
this.jumpTo(previousIndex);
},
/**
* jump to previous page
*/
toPrevious: function () {
var previousIndex = this.getCurrentPageIndex() - 1;
this.jumpTo(previousIndex < 1 ? 1 : previousIndex);
},
/**
* jump to given page according to event
* @param evt
*/
toPage: function (evt) {
this.jumpTo(this.getCurrentPageIndex(evt.target));
},
/**
* jump to next page
*/
toNext: function () {
var nextIndex = this.getCurrentPageIndex() + 1;
this.jumpTo(nextIndex > this.getPageCount() ? this.getPageCount() : nextIndex);
},
/**
* jump to next page section
*/
toNextSection: function () {
var nextIndex = this.getCurrentPageIndex() + this.getMaxPageButtons();
nextIndex = nextIndex > this.getPageCount() ? this.getPageCount() : nextIndex;
this.jumpTo(nextIndex);
},
/**
* jump to last page
*/
toLast: function () {
this.jumpTo(this.getPageCount());
},
/**
* jump to given page index
* @param pageIndex
*/
jumpTo: function (pageIndex) {
pageIndex = pageIndex * 1;
$(':focus').blur();
if (pageIndex - this.getCurrentPageIndex() == 0) {
return;
}
// this.props.currentPageIndex = pageIndex;
// this.forceUpdate();
if (this.props.toPage) {
this.props.toPage.call(this, pageIndex);
}
}
});
$pt.Components.NPagination = NPagination;
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NPanel = React.createClass($pt.defineCellComponent({
displayName: 'NPanel',
getDefaultProps: function () {
return {
defaultOptions: {
collapsible: false,
expanded: true,
style: 'default'
}
};
},
installMonitors: function () {
if (this.hasCheckInTitle()) {
this.getModel().addPostChangeListener(this.getCheckInTitleDataId(), this.onTitleCheckChanged);
}
},
uninstallMonitors: function () {
if (this.hasCheckInTitle()) {
this.getModel().removePostChangeListener(this.getCheckInTitleDataId(), this.onTitleCheckChanged);
}
},
beforeWillUpdate: function (nextProps) {
this.uninstallMonitors();
},
beforeDidUpdate: function (prevProps, prevState) {
this.installMonitors();
},
beforeDidMount: function () {
this.installMonitors();
},
beforeWillUnmount: function () {
this.uninstallMonitors();
},
getDependencyOptions: function () {
return ['collapsedLabel', 'expandedLabel'];
},
/**
* render check in title
* @returns {XML}
*/
renderCheckInTitle: function () {
if (!this.hasCheckInTitle()) {
return null;
}
var layout = {
label: this.getCheckInTitleLabel(),
dataId: this.getCheckInTitleDataId(),
comp: $.extend({ labelAttached: 'left' }, this.getCheckInTitleOption(), {
type: $pt.ComponentConstants.Check,
labelDirection: 'horizontal'
})
};
return React.createElement(
'div',
null,
'(',
React.createElement($pt.Components.NCheck, { model: this.getModel(), layout: $pt.createCellLayout('check', layout), view: this.isViewMode() }),
')'
);
},
renderHeadingButtons: function () {
var headButtons = this.getComponentOption('headerButtons');
if (headButtons) {
headButtons = Array.isArray(headButtons) ? headButtons : [headButtons];
var _this = this;
return React.createElement(
'div',
{ className: 'btn-toolbar pull-right', role: 'toolbar' },
headButtons.map(function (button, buttonIndex) {
if (_this.isViewMode() && button.view == 'edit') {
return null;
} else if (!_this.isViewMode() && button.view == 'view') {
return null;
}
var layout = {
label: button.text,
comp: button
};
// delete layout.comp.label;
// console.log(layout);
return React.createElement($pt.Components.NFormButton, { model: _this.getModel(),
layout: $pt.createCellLayout('pseudo-button', layout),
key: buttonIndex });
}).filter(function (button) {
return button != null;
})
);
} else {
return null;
}
},
renderCustomHeader: function () {
var customHeader = this.getComponentOption('customHeader');
if (typeof customHeader === 'function') {
return customHeader.call(this);
} else {
return customHeader;
}
},
/**
* render heading
* @returns {XML}
*/
renderHeading: function () {
var label = this.getTitle();
var css = {
'panel-title': true
};
if (this.isCollapsible()) {
css['n-collapsible-title-check'] = this.hasCheckInTitle();
return React.createElement(
'div',
{ className: 'panel-heading' },
React.createElement(
'h4',
{ className: $pt.LayoutHelper.classSet(css) },
React.createElement(
'a',
{ href: 'javascript:void(0);', onClick: this.onTitleClicked, ref: 'head' },
label
),
this.renderCheckInTitle()
),
this.renderCustomHeader(),
this.renderHeadingButtons()
);
} else if (this.hasCheckInTitle()) {
css['n-normal-title-check'] = this.hasCheckInTitle();
return React.createElement(
'div',
{ className: 'panel-heading' },
React.createElement(
'h4',
{ className: $pt.LayoutHelper.classSet(css) },
React.createElement(
'span',
{ ref: 'head' },
label
),
this.renderCheckInTitle()
),
this.renderCustomHeader(),
this.renderHeadingButtons()
);
} else {
return React.createElement(
'div',
{ className: 'panel-heading', ref: 'head' },
label,
this.renderCustomHeader(),
this.renderHeadingButtons()
);
}
},
/**
* render row
* @param row {RowLayout}
*/
renderRow: function (row, rowIndex) {
var _this = this;
var cells = row.getCells().map(function (cell, cellIndex) {
return React.createElement($pt.Components.NFormCell, { layout: cell,
model: _this.getModel(),
ref: cell.getId(),
direction: _this.props.direction,
view: _this.isViewMode(),
key: '' + rowIndex + '-' + cellIndex });
});
return React.createElement(
'div',
{ className: 'row', key: rowIndex },
cells
);
},
/**
* render
* @returns {XML}
*/
render: function () {
var label = this.getLayout().getLabel(this);
if (label == null) {
return React.createElement(
'div',
{ ref: 'panel' },
this.getInnerLayout().getRows().map(this.renderRow)
);
}
var expanded = this.canExpanded() && this.isExpanded();
var css = {
panel: true,
'panel-collapsible': this.isCollapsible(),
'panel-expanded': expanded
};
css['panel-' + this.getStyle()] = true;
css[this.getComponentCSS('n-panel')] = true;
var bodyStyle = {
display: expanded ? 'block' : 'none'
};
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css), ref: 'panel' },
this.renderHeading(),
React.createElement(
'div',
{ className: 'panel-body', style: bodyStyle, ref: 'body' },
this.getInnerLayout().getRows().map(this.renderRow)
)
);
},
/**
* get inner layout
* @returns {SectionLayout}
*/
getInnerLayout: function () {
return $pt.createSectionLayout({ layout: this.getComponentOption('editLayout') });
},
/**
* is collapsible or not
* @returns {boolean}
*/
isCollapsible: function () {
return this.getComponentOption('collapsible');
},
/**
* is expanded
* @returns {boolean}
*/
isExpanded: function () {
if (this.state.expanded == null) {
// first equals 'expanded' definition
this.state.expanded = this.getComponentOption('expanded');
if (this.hasCheckInTitle()) {
// when there is a check-box in title
var value = this.getCheckInTitleValue();
var action = this.getCheckInTitleCollapsible();
if (action === 'same') {
// check behavior same as collapsible
// all expanded, finally expanded
this.state.expanded = this.state.expanded && value === true;
} else if (action === 'reverse') {
// check behavior reversed as collapsible
// all expanded, finally expanded
this.state.expanded = this.state.expanded && value !== true;
}
}
}
return this.state.expanded;
},
/**
* get style
* @returns {string}
*/
getStyle: function () {
return this.getComponentOption('style');
},
getTitle: function () {
var label = this.getLayout().getLabel(this);
if (this.state.expanded) {
var expandedLabel = this.getExpandedLabelRenderer();
if (expandedLabel) {
if (typeof expandedLabel === 'string') {
label = expandedLabel;
} else {
label = expandedLabel.when.call(this, this.getModel());
}
}
} else {
var collapsedLabel = this.getCollapsedLabelRenderer();
if (collapsedLabel) {
if (typeof collapsedLabel === 'string') {
label = collapsedLabel;
} else {
label = collapsedLabel.when.call(this, this.getModel());
}
}
}
return label;
},
getExpandedLabelRenderer: function () {
return this.getComponentOption('expandedLabel');
},
getCollapsedLabelRenderer: function () {
return this.getComponentOption('collapsedLabel');
},
/**
* has check box in title or not
* @returns {boolean}
*/
hasCheckInTitle: function () {
return this.getComponentOption('checkInTitle') != null;
},
/**
* get check box value of panel title
* @returns {boolean}
*/
getCheckInTitleValue: function () {
var id = this.getCheckInTitleDataId();
return id ? this.getModel().get(id) : null;
},
/**
* get check box data id of panel title
* @returns {string}
*/
getCheckInTitleDataId: function () {
var checkInTitle = this.getComponentOption('checkInTitle');
return checkInTitle ? checkInTitle.data : null;
},
/**
* get check box label of panel title
* @returns {string}
*/
getCheckInTitleLabel: function () {
var checkInTitle = this.getComponentOption('checkInTitle');
return checkInTitle ? checkInTitle.label : null;
},
/**
* get check box value and collapsible is related or not
* @returns {same|reverse}
*/
getCheckInTitleCollapsible: function () {
var checkInTitle = this.getComponentOption('checkInTitle');
return checkInTitle ? checkInTitle.collapsible : null;
},
/**
* get other check in title options
* @returns {null}
*/
getCheckInTitleOption: function () {
var checkInTitle = this.getComponentOption('checkInTitle');
if (checkInTitle) {
var options = $.extend({}, checkInTitle);
delete options.data;
delete options.label;
delete options.collapsible;
return options;
} else {
return null;
}
},
/**
* on title clicked
* @param e
*/
onTitleClicked: function (e) {
e.selected = false;
e.preventDefault();
this.toggleExpanded(!this.state.expanded);
},
/**
* on title check-box value changed
* @param evt
*/
onTitleCheckChanged: function (evt) {
var value = evt.new;
var collapsible = this.getCheckInTitleCollapsible();
if (collapsible === 'same') {
this.toggleExpanded(value);
} else if (collapsible === 'reverse') {
this.toggleExpanded(!value);
}
},
/**
* toggle panel expanded
* @param expanded {boolean}
*/
toggleExpanded: function (expanded) {
if (expanded) {
if (this.canExpanded()) {
// panel can be expanded
$(ReactDOM.findDOMNode(this.refs.body)).slideDown(300, function () {
this.setState({ expanded: true });
}.bind(this));
}
} else {
$(ReactDOM.findDOMNode(this.refs.body)).slideUp(300, function () {
this.setState({ expanded: false });
}.bind(this));
}
},
/**
* check the panel can expanded or not
* @returns {boolean}
*/
canExpanded: function () {
if (this.hasCheckInTitle()) {
var value = this.getCheckInTitleValue();
var collapsible = this.getCheckInTitleCollapsible();
if (collapsible === 'same') {
// check behavior same as collapsible
return value === true;
} else if (collapsible === 'reverse') {
// check behavior reversed as collapsible
return value !== true;
}
}
return true;
}
}));
$pt.Components.NPanel = NPanel;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Panel, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NPanel, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
/**
* panel footer which only contains buttons
* depends NFormButton
*/
(function (window, $, React, ReactDOM, $pt) {
var NPanelFooter = React.createClass({
displayName: 'NPanelFooter',
statics: {
RESET_TEXT: "Reset",
RESET_ICON: "reply-all",
RESET_STYLE: "warning",
VALIDATE_TEXT: "Validate",
VALIDATE_ICON: "bug",
VALIDATE_STYLE: "default",
SAVE_TEXT: 'Save',
SAVE_ICON: 'floppy-o',
SAVE_STYLE: 'primary',
CANCEL_TEXT: 'Cancel',
CANCEL_ICON: 'ban',
CANCEL_STYLE: 'danger'
},
// componentWillUpdate: function (nextProps) {
// },
// componentDidUpdate: function (prevProps, prevState) {
// },
// componentDidMount: function () {
// },
// componentWillUnmount: function () {
// },
/**
* render left buttons
*/
renderLeftButtons: function () {
if (this.props.left) {
if (Array.isArray(this.props.left)) {
return this.props.left.map(function (button, buttonIndex) {
return this.renderButton(button, buttonIndex, true);
}.bind(this));
} else {
return this.renderButton(this.props.left, 'left', true);
}
} else {
return null;
}
},
/**
* render right buttons
*/
renderRightButtons: function () {
if (this.props.right) {
if (Array.isArray(this.props.right)) {
return this.props.right.map(function (button, buttonIndex) {
return this.renderButton(button, buttonIndex, false);
}.bind(this));
} else {
return this.renderButton(this.props.right, 'right', false);
}
} else {
return null;
}
},
/**
* render button
*/
renderButton: function (option, buttonIndex, onLeft) {
if (this.isViewMode() && option.view == 'edit') {
return null;
} else if (!this.isViewMode() && option.view == 'view') {
return null;
}
var layout = $.extend(true, {
label: option.text,
comp: { type: $pt.ComponentConstants.Button },
css: { comp: onLeft ? 'on-left' : 'on-right' }
}, {
comp: option
});
delete layout.comp.label;
var model = this.getModel();
return React.createElement($pt.Components.NFormButton, { model: this.getModel(),
layout: $pt.createCellLayout('pseudo-button', layout),
key: buttonIndex });
// }
},
/**
* render
* @returns {XML}
*/
render: function () {
return React.createElement(
"div",
{ className: "row n-panel-footer" },
React.createElement(
"div",
{ className: "col-sm-12 col-md-12 col-lg-12" },
React.createElement(
"div",
{ className: "btn-toolbar", role: "toolbar" },
this.props.reset ? this.renderButton({
icon: NPanelFooter.RESET_ICON,
text: NPanelFooter.RESET_TEXT,
style: NPanelFooter.RESET_STYLE,
click: this.props.reset.click ? this.props.reset.click : this.props.reset,
enabled: this.props.reset.enabled ? this.props.reset.enabled : true,
visible: this.props.reset.visible ? this.props.reset.visible : true
}, 'reset', true) : null,
this.props.validate ? this.renderButton({
icon: NPanelFooter.VALIDATE_ICON,
text: NPanelFooter.VALIDATE_TEXT,
style: NPanelFooter.VALIDATE_STYLE,
click: this.props.validate.click ? this.props.validate.click : this.props.validate,
enabled: this.props.validate.enabled ? this.props.validate.enabled : true,
visible: this.props.validate.visible ? this.props.validate.visible : true
}, 'validate', true) : null,
this.renderLeftButtons(),
this.props.cancel ? this.renderButton({
icon: NPanelFooter.CANCEL_ICON,
text: NPanelFooter.CANCEL_TEXT,
style: NPanelFooter.CANCEL_STYLE,
click: this.props.cancel.click ? this.props.cancel.click : this.props.cancel,
enabled: this.props.cancel.enabled ? this.props.cancel.enabled : true,
visible: this.props.cancel.visible ? this.props.cancel.visible : true
}, 'cancel', false) : null,
this.props.save ? this.renderButton({
icon: NPanelFooter.SAVE_ICON,
text: NPanelFooter.SAVE_TEXT,
style: NPanelFooter.SAVE_STYLE,
click: this.props.save.click ? this.props.save.click : this.props.save,
enabled: this.props.save.enabled ? this.props.save.enabled : true,
visible: this.props.save.visible ? this.props.save.visible : true
}, 'save', false) : null,
this.renderRightButtons()
)
)
);
},
/**
* get model
* @returns {ModelInterface}
*/
getModel: function () {
return this.props.model == null ? $pt.createModel({}) : this.props.model;
},
isViewMode: function () {
return this.props.view;
}
});
$pt.Components.NPanelFooter = NPanelFooter;
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NRadio = React.createClass($pt.defineCellComponent({
displayName: 'NRadio',
getDefaultProps: function () {
return {
defaultOptions: {
direction: 'horizontal',
labelAtLeft: false
}
};
},
/**
* render label
* @param option {{text:string}}
* @param labelInLeft {boolean}
* @returns {XML}
*/
renderLabel: function (option, labelInLeft) {
var css = {
'radio-label': true,
disabled: !this.isEnabled(),
'radio-label-left': labelInLeft
};
return React.createElement(
'span',
{ className: $pt.LayoutHelper.classSet(css),
onClick: this.isEnabled() && !this.isViewMode() ? this.onButtonClicked.bind(this, option) : null },
option.text
);
},
/**
* render radio button, using font awesome instead
* @params option radio option
* @returns {XML}
*/
renderRadio: function (option, optionIndex) {
var checked = this.getValueFromModel() == option.id;
var enabled = this.isEnabled();
var css = {
disabled: !enabled,
checked: checked,
'radio-container': true
};
var labelAtLeft = this.isLabelAtLeft();
return React.createElement(
'div',
{ className: 'n-radio-option', key: optionIndex },
labelAtLeft ? this.renderLabel(option, true) : null,
React.createElement(
'div',
{ className: 'radio-container' },
React.createElement(
'span',
{ className: $pt.LayoutHelper.classSet(css),
onClick: enabled && !this.isViewMode() ? this.onButtonClicked.bind(this, option) : null,
onKeyUp: enabled && !this.isViewMode() ? this.onKeyUp.bind(this, option) : null,
tabIndex: '0',
ref: 'out-' + option.id },
React.createElement('span', { className: 'check', onClick: this.onInnerClicked.bind(this, option) })
)
),
labelAtLeft ? null : this.renderLabel(option, false)
);
},
render: function () {
var css = {
'n-radio': true,
vertical: this.getComponentOption('direction') === 'vertical',
'n-disabled': !this.isEnabled(),
'n-view-mode': this.isViewMode()
};
return React.createElement($pt.Components.NCodeTableWrapper, { codetable: this.getCodeTable(),
className: $pt.LayoutHelper.classSet(css),
model: this.getModel(),
layout: this.getLayout(),
renderer: this.getRealRenderer });
},
getRealRenderer: function () {
var css = {
'n-radio': true,
vertical: this.getComponentOption('direction') === 'vertical',
'n-disabled': !this.isEnabled(),
'n-view-mode': this.isViewMode()
};
return React.createElement(
'div',
{ className: this.getComponentCSS($pt.LayoutHelper.classSet(css)) },
this.getCodeTable().map(this.renderRadio)
);
},
getCodeTable: function () {
return this.getComponentOption("data");
},
/**
* inner span clicked, force focus to outer span
* for fix the outer span cannot gain focus in IE11
* @param option
*/
onInnerClicked: function (option) {
$(ReactDOM.findDOMNode(this.refs['out-' + option.id])).focus();
},
/**
* on button clicked
* @param option
*/
onButtonClicked: function (option) {
this.setValueToModel(option.id);
},
onKeyUp: function (option, evt) {
if (evt.keyCode == '32') {
this.onButtonClicked(option);
}
},
isLabelAtLeft: function () {
return this.getComponentOption('labelAtLeft');
}
}));
$pt.Components.NRadio = NRadio;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Radio, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NRadio, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
/**
* search text
*/
(function (window, $, React, ReactDOM, $pt) {
var NSearchText = React.createClass($pt.defineCellComponent({
displayName: 'NSearchText',
statics: {
ADVANCED_SEARCH_BUTTON_ICON: 'search',
ADVANCED_SEARCH_DIALOG_NAME_LABEL: 'Name',
ADVANCED_SEARCH_DIALOG_BUTTON_TEXT: 'Search',
ADVANCED_SEARCH_DIALOG_CODE_LABEL: 'Code',
ADVANCED_SEARCH_DIALOG_RESULT_TITLE: 'Search Result',
NOT_FOUND: 'Not Found',
SEARCH_PROXY: null,
SEARCH_PROXY_CALLBACK: null,
ADVANCED_SEARCH_PROXY: null,
ADVANCED_SEARCH_PROXY_CALLBACK: null
},
getDefaultProps: function () {
return {
defaultOptions: {}
};
},
getInitialState: function () {
return {
stopRetrieveLabelFromRemote: false
};
},
beforeDidUpdate: function (prevProps, prevState) {
this.initSetValues();
},
beforeDidMount: function () {
// set model value to component
this.initSetValues();
},
/**
* render
* @returns {XML}
*/
render: function () {
if (this.isViewMode()) {
return this.renderInViewMode();
}
var enabled = this.isEnabled();
var css = {
'n-search-text': true
};
if (!enabled) {
css['n-disabled'] = true;
}
var middleSpanStyle = {
width: '0'
};
return React.createElement(
'div',
{ className: this.getComponentCSS($pt.LayoutHelper.classSet(css)) },
React.createElement(
'div',
{ className: 'input-group' },
React.createElement('input', { type: 'text', className: 'form-control search-code', onKeyUp: this.onComponentChange, ref: 'code',
disabled: !enabled, onFocus: this.onComponentFocused, onBlur: this.onComponentBlurred }),
React.createElement('span', { className: 'input-group-btn', style: middleSpanStyle }),
React.createElement('input', { type: 'text', className: 'form-control search-label', onFocus: this.onLabelFocused, ref: 'label',
disabled: !enabled, tabIndex: -1 }),
React.createElement(
'span',
{ className: 'input-group-addon advanced-search-btn',
onClick: enabled ? this.showAdvancedSearchDialog : null },
React.createElement('span', { className: 'fa fa-fw fa-' + NSearchText.ADVANCED_SEARCH_BUTTON_ICON })
),
this.renderNormalLine(),
this.renderFocusLine()
)
);
},
/**
* transfer focus to first text input
*/
onLabelFocused: function () {
this.getComponent().focus();
},
onComponentFocused: function () {
$(ReactDOM.findDOMNode(this.refs.focusLine)).toggleClass('focus');
$(ReactDOM.findDOMNode(this.refs.normalLine)).toggleClass('focus');
},
onComponentBlurred: function () {
$(ReactDOM.findDOMNode(this.refs.focusLine)).toggleClass('focus');
$(ReactDOM.findDOMNode(this.refs.normalLine)).toggleClass('focus');
},
/**
* on component changed
*/
onComponentChange: function (evt) {
var value = evt.target.value;
this.setValueToModel(evt.target.value);
},
/**
* on model change
* @param evt
*/
onModelChanged: function (evt) {
var value = evt.new;
this.getComponent().val(value);
this.retrieveAndSetLabelTextFromRemote(value);
},
/**
* show advanced search dialog
*/
showAdvancedSearchDialog: function () {
if (!this.state.searchDialog) {
this.state.searchDialog = $pt.Components.NModalForm.createFormModal(this.getLayout().getLabel(this), 'advanced-search-dialog');
}
this.state.searchDialog.show({
model: this.getAdvancedSearchDialogModel(),
layout: this.getAdvancedSearchDialogLayout(),
buttons: {
reset: false,
validate: false,
cancel: false
}
});
},
/**
* pickup advanced result item
* @param item
*/
pickupAdvancedResultItem: function (item) {
this.state.stopRetrieveLabelFromRemote = true;
this.getModel().set(this.getDataId(), item.code);
this.setLabelText(item.name);
this.state.stopRetrieveLabelFromRemote = false;
},
initSetValues: function () {
var value = this.getValueFromModel();
if (!this.isViewMode()) {
this.getComponent().val(value);
}
var labelPropertyId = this.getComponentOption('labelPropId');
if (labelPropertyId) {
this.setLabelText(this.getModel().get(labelPropertyId));
} else {
// send ajax request
this.retrieveAndSetLabelTextFromRemote(value);
}
},
setLabelText: function (text) {
if (this.isViewMode()) {
var value = this.getValueFromModel();
if (value == null) {
// $(ReactDOM.findDOMNode(this.refs.viewLabel)).text('');
} else {
var label = value;
if (text == null) {
label += ' - ' + NSearchText.NOT_FOUND;
} else {
label += ' - ' + text;
}
// $(ReactDOM.findDOMNode(this.refs.viewLabel)).text(label);
var def = this.refs.viewLabel.getLayout().getDefinition();
def.label = label;
this.refs.viewLabel.forceUpdate();
// this.refs.viewLabel.getLayout()
// this.setState({viewLabel: label})
}
} else {
$(ReactDOM.findDOMNode(this.refs.label)).val(text);
}
// if label property id defined, and value changed, set to model
var labelPropertyId = this.getComponentOption('labelPropId');
if (labelPropertyId) {
var name = this.getModel().get(labelPropertyId);
if (name != text) {
this.getModel().set(labelPropertyId, name);
}
}
},
/**
* get label text from remote
*/
retrieveAndSetLabelTextFromRemote: function (value) {
if (this.state.search != null) {
clearTimeout(this.state.search);
}
if (this.state.stopRetrieveLabelFromRemote) {
return;
}
var triggerDigits = this.getSearchTriggerDigits();
if (triggerDigits == null) {
throw new $pt.createComponentException($pt.ComponentConstants.Err_Search_Text_Trigger_Digits_Not_Defined, "Trigger digits cannot be null in search text.");
}
if (value == null) {
value = '';
}
if (typeof value !== 'string') {
value = value + '';
}
if (value.isBlank() || value.length != triggerDigits && triggerDigits != -1) {
this.setLabelText(null);
return;
}
var _this = this;
this.state.search = setTimeout(function () {
var postData = {
code: value
};
if (NSearchText.SEARCH_PROXY) {
postData = NSearchText.SEARCH_PROXY.call(_this, postData);
}
$pt.internalDoPost(_this.getSearchUrl(), postData, {
quiet: true
}).done(function (data) {
if (typeof data === 'string') {
data = JSON.parse(data);
}
var name = data.name;
if (NSearchText.SEARCH_PROXY_CALLBACK) {
name = NSearchText.SEARCH_PROXY_CALLBACK.call(_this, data);
}
_this.setLabelText(name);
}).fail(function () {
window.console.error('Error occured when retrieve label from remote in NSearch.');
// arguments.slice(0).forEach(function(argu) {
// window.console.error(argu);
// });
});
}, 300);
},
/**
* get search url
* @returns {string}
*/
getSearchUrl: function () {
return this.getComponentOption("searchUrl");
},
/**
* get advanced search url
* @returns {string}
*/
getAdvancedSearchUrl: function () {
return this.getComponentOption("advancedUrl");
},
/**
* get minimum digits to trigger search
* @returns {number}
*/
getSearchTriggerDigits: function () {
return this.getComponentOption("searchTriggerDigits");
},
getComponent: function () {
return $(ReactDOM.findDOMNode(this.refs.code));
},
// search dialog
getAdvancedSearchDialogModel: function () {
var model = this.getComponentOption('searchDialogModel');
if (model == null) {
model = {
name: null,
countPerPage: 10,
pageIndex: 1,
items: null,
criteria: {
pageIndex: 1,
pageCount: 1,
countPerPage: 10
}
};
}
return $pt.createModel(model);
},
getAdvancedSearchDialogLayout: function () {
var _this = this;
var layout = this.getComponentOption('searchDialogLayout');
if (layout == null) {
var direction = this.props.direction;
if (!direction) {
direction = NForm.LABEL_DIRECTION;
}
var buttonCSS = {
'pull-right': true,
'pull-down': direction == 'vertical'
};
layout = {
name: {
label: NSearchText.ADVANCED_SEARCH_DIALOG_NAME_LABEL,
comp: {
type: $pt.ComponentConstants.Text
},
pos: {
row: 10,
col: 10,
width: 6
}
},
button: {
label: NSearchText.ADVANCED_SEARCH_DIALOG_BUTTON_TEXT,
comp: {
type: $pt.ComponentConstants.Button,
style: 'primary',
click: function (model) {
var currentModel = $.extend({}, model.getCurrentModel());
// remove query result and pagination criteria JSON, only remain the criteria data.
delete currentModel.items;
delete currentModel.criteria;
if (NSearchText.ADVANCED_SEARCH_PROXY) {
currentModel = NSearchText.ADVANCED_SEARCH_PROXY.call(this, currentModel);
}
$pt.internalDoPost(_this.getAdvancedSearchUrl(), currentModel, {
done: function (data) {
if (typeof data === 'string') {
data = JSON.parse(data);
}
if (NSearchText.ADVANCED_SEARCH_PROXY_CALLBACK) {
data = NSearchText.ADVANCED_SEARCH_PROXY_CALLBACK.call(this, data, this.getDataId());
}
model.mergeCurrentModel(data);
model.set('criteria' + $pt.PROPERTY_SEPARATOR + 'url', this.getAdvancedSearchUrl());
window.console.debug(model.getCurrentModel());
this.state.searchDialog.forceUpdate();
}.bind(_this)
});
}
},
css: {
comp: $pt.LayoutHelper.classSet(buttonCSS)
},
pos: {
row: 10,
col: 20,
width: 6
}
},
items: {
label: NSearchText.ADVANCED_SEARCH_DIALOG_RESULT_TITLE,
comp: {
type: $pt.ComponentConstants.Table,
indexable: true,
searchable: false,
rowOperations: {
icon: "check",
click: function (row) {
_this.pickupAdvancedResultItem(row);
_this.state.searchDialog.hide();
}
},
pageable: true,
criteria: "criteria",
columns: [{
title: NSearchText.ADVANCED_SEARCH_DIALOG_CODE_LABEL,
width: 200,
data: "code"
}, {
title: NSearchText.ADVANCED_SEARCH_DIALOG_NAME_LABEL,
width: 400,
data: "name"
}]
},
pos: {
row: 20,
col: 10,
width: 12
}
}
};
} else {
layout = layout.call(this);
}
return $pt.createFormLayout(layout);
},
getTextInViewMode: function () {
var value = this.getValueFromModel();
if (value != null) {
// if (this.state.viewLabel) {
// return this.state.viewLabel;
// }
}
return value;
}
}));
$pt.Components.NSearchText = NSearchText;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Search, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NSearchText, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NSelect = React.createClass($pt.defineCellComponent({
displayName: 'NSelect',
mixins: [$pt.mixins.PopoverMixin],
statics: {
POP_FIX_ON_BOTTOM: false,
PLACEHOLDER: "Please Select...",
NO_OPTION_FOUND: 'No Option Found',
FILTER_PLACEHOLDER: 'Search...',
CLOSE_TEXT: 'Close',
CLEAR_TEXT: 'Clear'
},
getDefaultProps: function () {
return {
defaultOptions: {
allowClear: true,
minimumResultsForSearch: 1,
data: [],
availableWhenNoParentValue: false
// other
/*
parentPropId: parent property id
parentModel: parent model, default is this.props.model is not defined
parentFilter: filter of options according to parent property value,
can be property of self options
or a function with parameters
1: parent value
2: self options array
*/
}
};
},
afterWillUpdate: function (nextProps) {
if (this.hasParent()) {
// add post change listener into parent model
this.getParentModel().removePostChangeListener(this.getParentPropertyId(), this.onParentModelChanged);
}
},
afterDidUpdate: function (prevProps, prevState) {
this.checkLoadingState();
},
afterDidMount: function () {
this.checkLoadingState();
},
checkLoadingState: function () {
if (this.hasParent()) {
// remove post change listener from parent model
this.getParentModel().addPostChangeListener(this.getParentPropertyId(), this.onParentModelChanged);
}
if (this.state.onloading && !this.state.alreadySendRequest) {
if (this.hasParent()) {
var parentValue = this.getParentPropertyValue();
if (parentValue == null) {
// no parent value
if (this.isAvailableWhenNoParentValue()) {
this.getCodeTable().initializeRemote().done(function () {
this.setState({ onloading: false });
}.bind(this));
} else {
this.getCodeTable().setAsRemoteInitialized();
this.setState({ onloading: false });
}
} else {
this.getCodeTable().loadRemoteCodeSegment(parentValue).done(function () {
this.setState({ onloading: false });
}.bind(this));
}
} else {
this.getCodeTable().initializeRemote().done(function () {
this.setState({ onloading: false });
}.bind(this));
}
}
},
afterWillUnmount: function () {
if (this.hasParent()) {
// remove post change listener from parent model
this.getParentModel().removePostChangeListener(this.getParentPropertyId(), this.onParentModelChanged);
}
},
renderClear: function () {
if (!this.isClearAllowed()) {
return null;
}
return React.createElement('span', { className: 'fa fa-fw fa-close clear',
onClick: this.onClearClick });
},
getCurrentDisplayText: function () {
var value = this.getValueFromModel();
var itemText = null;
if (this.hasParent() && this.isOnLoadingWhenHasParent() && value != null) {
this.state.onloading = true;
} else if (this.isOnLoadingWhenNoParent()) {
this.state.onloading = true;
} else if (value != null) {
var item = this.getAvailableOptions().find(function (item) {
return item.id == value;
});
if (item) {
itemText = item.text;
}
}
if (itemText == null) {
itemText = this.state.onloading ? $pt.Components.NCodeTableWrapper.ON_LOADING : this.isViewMode() ? '' : this.getPlaceholder();
}
return itemText;
},
renderText: function () {
var css = {
'input-group': true,
'form-control': true,
'no-clear': !this.isClearAllowed()
};
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css),
onClick: this.onComponentClicked },
React.createElement(
'span',
{ className: 'text' },
this.getCurrentDisplayText()
),
this.renderClear(),
React.createElement('span', { className: 'fa fa-fw fa-sort-down drop' })
);
},
/**
* render
* @returns {XML}
*/
render: function () {
var css = {
'n-disabled': !this.isEnabled(),
'n-view-mode': this.isViewMode()
};
css[this.getComponentCSS('n-select')] = true;
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css),
tabIndex: this.isEnabled() ? '0' : null,
onKeyUp: this.onComponentKeyUp,
'aria-readonly': 'true',
readOnly: 'true',
ref: 'comp' },
this.renderText(),
this.renderNormalLine(),
this.renderFocusLine()
);
},
renderOptions: function (options, filterText) {
if (options == null || options.length == 0) {
return null;
}
if (filterText != null && !filterText.isBlank()) {
options = options == null ? null : options.filter(function (item) {
return item.text.toLowerCase().indexOf(filterText.toLowerCase()) != -1;
});
}
var _this = this;
var value = this.getValueFromModel();
return React.createElement(
'ul',
{ className: 'options',
onTouchStart: this.isMobilePhone() ? this.onOptionTouchStart : null,
onTouchMove: this.isMobilePhone() ? this.onOptionTouchMove : null,
onTouchEnd: this.isMobilePhone() ? this.onOptionTouchEnd : null },
options.map(function (item, itemIndex) {
var css = {
chosen: value == item.id
};
return React.createElement(
'li',
{ onClick: _this.onOptionClick.bind(_this, item),
onMouseEnter: _this.onOptionMouseEnter,
onMouseLeave: _this.onOptionMouseLeave,
onMouseMove: _this.onOptionMouseMove,
className: $pt.LayoutHelper.classSet(css),
key: itemIndex,
'data-id': item.id },
React.createElement(
'span',
null,
item.text
)
);
})
);
},
renderNoOption: function (options) {
if (options == null || options.length == 0) {
return React.createElement(
'div',
{ className: 'no-option' },
React.createElement(
'span',
null,
NSelect.NO_OPTION_FOUND
)
);
}
return null;
},
renderFilterText: function (options, filterText) {
if (options == null || options.length == 0) {
return;
}
if (this.hasFilterText()) {
var model = $pt.createModel({
text: filterText,
// on mobile phone, set as true to disable the soft keyboard
// set as false to enable it when popover render completed
// see #onPopoverRenderComplete
disabled: this.isMobilePhone() ? true : false
});
var layout = $pt.createCellLayout('text', {
comp: {
placeholder: NSelect.FILTER_PLACEHOLDER,
enabled: {
depends: 'disabled',
when: function (model) {
return model.get('disabled') !== true;
}
}
},
evt: {
keyUp: this.onComponentKeyUp
}
});
model.addPostChangeListener('text', this.onFilterTextChange);
this.state.filterModel = model;
return React.createElement($pt.Components.NText, { model: model, layout: layout });
} else {
return null;
}
},
renderPopoverOperations: function () {
if (!this.isMobilePhone()) {
return null;
}
return React.createElement(
'div',
{ className: 'operations row' },
React.createElement(
'div',
null,
React.createElement(
'a',
{ href: 'javascript:void(0);', onClick: this.hidePopover },
React.createElement(
'span',
null,
NSelect.CLOSE_TEXT
)
),
this.getComponentOption('allowClear') ? React.createElement(
'a',
{ href: 'javascript:void(0);', onClick: this.onClearClick },
React.createElement(
'span',
null,
NSelect.CLEAR_TEXT
)
) : null
)
);
},
renderPopoverContent: function (filterText) {
var options = this.getAvailableOptions();
return React.createElement(
'div',
{ className: this.hasFilterText() ? 'has-filter' : '' },
this.renderFilterText(options, filterText),
this.renderNoOption(options),
this.renderOptions(options, filterText),
this.renderPopoverOperations()
);
},
getPopoverContainerCSS: function () {
return 'n-select-popover';
},
beforeShowPopover: function () {
if (this.state.popoverDiv) {
// log the last active option
var activeOption = this.state.popoverDiv.find('ul.options > li.active');
this.state.lastActiveOptionId = activeOption.attr('data-id');
} else {
delete this.state.lastActiveOptionId;
}
},
afterPopoverRenderComplete: function () {
// only recalculate when not mobile phone
if (!this.isMobilePhone()) {
// if there is no active option, set first as active
var options = this.state.popoverDiv.find('ul.options > li');
if (options.length != 0) {
if (this.state.lastActiveOptionId) {
// according to react mechanism, must remove the existed active option first
// since active is not render by react by jquery, react will keep it
// active the last active option if exists
options.removeClass('active').filter(function (index, option) {
return $(option).attr('data-id') == this.state.lastActiveOptionId;
}.bind(this)).addClass('active');
}
if (this.state.popoverDiv.find('ul.options > li.active').length == 0) {
// active the first if no active option
this.state.popoverDiv.find('ul.options > li').first().addClass('active');
}
}
}
var filterText = this.state.popoverDiv.find('div.n-text input[type=text]');
if (filterText.length > 0 && !filterText.is(':focus')) {
if (this.state.filteTextCaret != null) {
filterText.caret(this.state.filteTextCaret);
} else if (filterText.val().length > 0) {
filterText.caret(filterText.val().length);
}
if (!this.isMobile()) {
filterText.focus();
} else {
// filterText.blur();
}
}
// set as false anyway to let search text enabled
// actually only in mobile phone, it is set as true when renderring
// see #renderFilterText
if (this.state.filterModel) {
this.state.filterModel.set('disabled', false);
}
},
isOnLoadingWhenHasParent: function () {
var codetable = this.getCodeTable();
if (codetable == null || Array.isArray(codetable) || !codetable.isRemote()) {
return false;
}
var parentValue = this.getParentPropertyValue();
if (parentValue == null) {
// no parent value
if (this.isAvailableWhenNoParentValue()) {
// still need options
// check code table is remote and not initialized
// is on loading
return codetable.isRemoteButNotInitialized();
} else {
// otherwise not need load options
codetable.setAsRemoteInitialized();
return false;
}
} else {
// has parent value
// check code table segment is loaded or not
return !codetable.isSegmentLoaded(parentValue);
}
},
isOnLoadingWhenNoParent: function () {
// var value = this.getValueFromModel();
var codetable = this.getCodeTable();
// remote and not initialized
// is on loading
return codetable != null && !Array.isArray(codetable) && codetable.isRemoteButNotInitialized();
},
onComponentClicked: function () {
if (!this.isEnabled() || this.isViewMode()) {
// do nothing
return;
}
if (!this.state.popoverDiv || !this.state.popoverDiv.is(':visible')) {
var codetable = this.getCodeTable();
if (this.hasParent() && this.isOnLoadingWhenHasParent()) {
this.setState({
onloading: true,
alreadySendRequest: true
}, function () {
codetable.loadRemoteCodeSegment(this.getParentPropertyValue()).done(function () {
this.showPopover();
}.bind(this)).always(function () {
this.setState({
onloading: false,
alreadySendRequest: false
});
}.bind(this));
}.bind(this));
} else if (this.isOnLoadingWhenNoParent()) {
this.setState({
onloading: true,
alreadySendRequest: true
}, function () {
codetable.initializeRemote().done(function () {
this.showPopover();
}.bind(this)).always(function () {
this.setState({
onloading: false,
alreadySendRequest: false
});
}.bind(this));
}.bind(this));
} else {
this.showPopover();
}
}
},
onComponentKeyUp: function (evt) {
if (evt.keyCode === 40) {
// down arrow
this.onComponentDownArrowKeyUp(evt);
} else if (evt.keyCode === 38) {
// up arrow
this.onComponentUpArrowKeyUp(evt);
} else if (evt.keyCode === 13) {
// enter
this.onComponentEnterKeyUp(evt);
}
},
onComponentEnterKeyUp: function (evt) {
evt.preventDefault();
evt.stopPropagation();
if (!this.isEnabled() || this.isViewMode()) {
// do nothing
return;
}
if (!this.state.popoverDiv || !this.state.popoverDiv.is(':visible')) {
return;
}
var option = this.state.popoverDiv.find('ul.options > li.active');
if (option.length > 0) {
this.setValueToModel(option.attr('data-id'));
this.hidePopover();
$(this.refs.comp).focus();
}
},
onComponentDownArrowKeyUp: function (evt) {
evt.preventDefault();
evt.stopPropagation();
if (!this.isEnabled() || this.isViewMode()) {
// do nothing
return;
}
if (!this.state.popoverDiv || !this.state.popoverDiv.is(':visible')) {
this.onComponentClicked();
} else {
var options = this.state.popoverDiv.find('ul.options > li');
var keystepOption = options.filter('.active');
if (keystepOption.length == 0) {
var first = options.first();
first.addClass('active');
this.scrollIntoView(first);
} else {
var last = options.last();
if (!keystepOption.first().is(last)) {
keystepOption.removeClass('active');
var next = keystepOption.next();
next.addClass('active');
this.scrollIntoView(next);
}
}
}
},
onComponentUpArrowKeyUp: function (evt) {
evt.preventDefault();
evt.stopPropagation();
if (!this.isEnabled() || this.isViewMode()) {
// do nothing
return;
}
if (!this.state.popoverDiv || !this.state.popoverDiv.is(':visible')) {
this.onComponentClicked();
} else {
var options = this.state.popoverDiv.find('ul.options > li');
var keystepOption = options.filter('.active');
if (keystepOption.length == 0) {
var last = options.last();
last.addClass('active');
this.scrollIntoView(last);
} else {
var first = options.first();
if (!keystepOption.first().is(first)) {
keystepOption.removeClass('active');
var previous = keystepOption.prev();
previous.addClass('active');
this.scrollIntoView(previous);
}
}
}
},
scrollIntoView: function (option) {
// for forbid the mouse event
this.state.onKeyEventProcessed = true;
var optionOffset = option.offset();
var optionTop = optionOffset.top;
var optionHeight = option.outerHeight();
var optionBottom = optionTop + optionHeight;
var parent = option.parent();
var parentOffset = parent.offset();
var parentTop = parentOffset.top;
var parentBottom = parentTop + parent.height();
var allOptions = parent.children();
var win = $(window);
var windowTop = win.scrollTop();
var windowBottom = windowTop + win.height();
if (optionTop < parentTop || optionBottom > parentBottom) {
// can not see option in its parent, scroll the parent
var optionIndex = allOptions.index(option);
var height = allOptions.toArray().reduce(function (prev, current, index) {
if (index < optionIndex) {
prev += $(current).outerHeight();
}
return prev;
}, 0);
parent.scrollTop(height);
}
// get option offset again, since it might be changed
// but it is seen in its parent
optionOffset = option.offset();
optionTop = optionOffset.top;
optionBottom = optionTop + optionHeight;
if (optionBottom > windowBottom) {
win.scrollTop(windowTop + optionBottom - windowBottom);
}
// get window scroll top again
windowTop = win.scrollTop();
if (optionTop < windowTop) {
// can not see option in window, even it is seen in its parent
// option is above the window top,
// which means parent top is less than window top, since option already been seen in parent
win.scrollTop(windowTop - (windowTop - optionTop));
}
},
defaultOptionClick: function (item) {
this.setValueToModel(item.id);
this.hidePopover();
$(this.refs.comp).focus();
},
onOptionClick: function (item, evt) {
evt.stopPropagation();
evt.preventDefault();
var customOptionClick = this.getComponentOption('optionClick');
if (customOptionClick) {
var ret = customOptionClick.call(this, item);
if (ret && ret.done) {
// have return value, must be a jquery deferred
ret.done(function () {
this.defaultOptionClick(item);
}.bind(this));
} else {
this.defaultOptionClick(item);
}
} else {
this.defaultOptionClick(item);
}
},
onOptionMouseEnter: function (evt) {},
onOptionMouseLeave: function (evt) {
// if (this.state.onKeyEventProcessed === true) {
// }
},
onOptionMouseMove: function (evt) {
// when handled the arrow up/down event, highlight the option
// in chrome, the mouse event will be triggered after call #scrollIntoView
// so use state onKeyEventProcessed to flag this operation
// if the flag is true, ignore the mouse event,
// and reset the flag, let the following mouse event processed
// cannot use mouse enter event, since there is no second enter event triggered
// must use mouse move event to handle, seems no performance issue here.
if (this.state.onKeyEventProcessed === true) {
// console.log('onOptionMouseEnter #1', this.state.onKeyEventProcessed);
delete this.state.onKeyEventProcessed;
} else {
// console.log('onOptionMouseEnter #2', this.state.onKeyEventProcessed);
$(evt.target).addClass('active').siblings().removeClass('active');
}
},
onClearClick: function () {
if (!this.isEnabled() || this.isViewMode()) {
return;
}
this.setValueToModel(null);
// clear highlight
//if (this.state.popoverDiv) {
//this.state.popoverDiv.find('ul.options > li').filter('.chosen').removeClass('chosen');
//}
// for mobile
this.hidePopover();
},
onFilterTextChange: function (evt) {
if (this.state.popoverDiv.is(':visible')) {
var filterText = this.state.popoverDiv.find('div.n-text input[type=text]');
this.state.filteTextCaret = filterText.caret();
} else {
this.state.filteTextCaret = null;
}
// console.log('caret', this.state.filteTextCaret);
if (this.isMobilePhone()) {
var optionContainer = this.state.popoverDiv.find('ul.options');
if (this.getOptionContainerOffsetY(optionContainer) !== 0) {
optionContainer.css({
'transform': '',
'transition-timing-function': '',
'transition-duration': ''
});
}
}
this.showPopover(evt.new);
},
getOptionTouchEventContainer: function (evt) {
var target = $(evt.target);
if (target[0].tagName != 'UL') {
target = target.closest('ul');
}
return target;
},
getOptionContainerOffsetY: function (container) {
var transform = container.css('transform').split(',');
if (transform.length > 5) {
return parseFloat(transform[5]);
} else {
return 0;
}
},
calcOptionContainerOffsetY: function (target, offsetY) {
if (offsetY >= 0) {
offsetY = 0;
} else {
var optionsHeight = target.height();
var totalHeight = target.parent().height();
if (optionsHeight <= totalHeight) {
return 0;
}
if (offsetY < totalHeight - optionsHeight) {
offsetY = totalHeight - optionsHeight;
}
}
return offsetY;
},
onOptionTouchStart: function (evt) {
this.state.touchStartClientY = evt.touches[0].clientY;
var target = this.getOptionTouchEventContainer(evt);
this.state.touchStartRelatedY = this.getOptionContainerOffsetY(target);
this.state.touchStartTime = moment();
},
onOptionTouchMove: function (evt) {
var touches = evt.touches;
var length = touches.length;
if (length > 0) {
var target = this.getOptionTouchEventContainer(evt);
// calculate the distance of touch moving
// make sure the first and last option are in viewport
var distance = touches[length - 1].clientY - this.state.touchStartClientY;
var offsetY = this.calcOptionContainerOffsetY(target, this.state.touchStartRelatedY + distance);
target.css('transform', 'translateY(' + offsetY + 'px)');
this.state.touchLastClientY = touches[length - 1].clientY;
}
},
onOptionTouchEnd: function (evt) {
// continue scrolling
// calculate the speed
var timeUsed = moment().diff(this.state.touchStartTime, 'ms');
// alert(timeUsed);
if (timeUsed <= 300 && this.state.touchLastClientY != null) {
var distance = this.state.touchLastClientY - this.state.touchStartClientY;
var speed = distance / timeUsed * 10; // pixels per 10 ms
var target = this.getOptionTouchEventContainer(evt);
var startOffsetY = this.getOptionContainerOffsetY(target);
var targetOffsetY = this.calcOptionContainerOffsetY(target, startOffsetY + speed * 100 / 2);
target.one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function () {
target.css({
'transition-timing-function': '',
'transition-duration': ''
});
});
target.css({
'transition-timing-function': 'cubic-bezier(0.1, 0.57, 0.1, 1)',
'transition-duration': '500ms',
'transform': 'translateY(' + targetOffsetY + 'px)'
});
}
delete this.state.touchStartClientY;
delete this.state.touchStartRelatedY;
delete this.state.touchStartTime;
delete this.state.touchLastClientY;
},
/**
* on parent model change
* @param evt
*/
onParentModelChanged: function (evt) {
this.setValueToModel(null);
},
/**
* get parent model
* @returns {*}
*/
getParentModel: function () {
var parentModel = this.getComponentOption("parentModel");
return parentModel == null ? this.getModel() : parentModel;
},
/**
* get parent property value
* @returns {*}
*/
getParentPropertyValue: function () {
return this.getParentModel().get(this.getParentPropertyId());
},
/**
* get parent property id
* @returns {string}
*/
getParentPropertyId: function () {
return this.getComponentOption("parentPropId");
},
/**
* has parent or not
* @returns {boolean}
*/
hasParent: function () {
return this.getParentPropertyId() != null;
},
hasFilterText: function () {
var minimumResultsForSearch = this.getComponentOption('minimumResultsForSearch');
return minimumResultsForSearch >= 0 && minimumResultsForSearch != Infinity;
},
/**
* convert data options, options can be CodeTable object or an array
* @param options
* @returns {*}
*/
convertDataOptions: function (options) {
return Array.isArray(options) ? options : options.list();
},
getPlaceholder: function () {
return this.getComponentOption('placeholder', NSelect.PLACEHOLDER);
},
isClearAllowed: function () {
return this.getComponentOption('allowClear') && !this.isMobilePhone();
},
getCodeTable: function () {
return this.getComponentOption('data');
},
/**
* get available options.
* if no parent assigned, return all data options
* @returns {[*]}
*/
getAvailableOptions: function () {
if (!this.hasParent()) {
return this.convertDataOptions(this.getCodeTable());
} else {
var parentValue = this.getParentPropertyValue();
if (parentValue == null) {
return this.isAvailableWhenNoParentValue() ? this.convertDataOptions(this.getCodeTable()) : [];
} else {
var filter = this.getComponentOption("parentFilter");
if (typeof filter === 'object') {
// call code table filter
return this.convertDataOptions(this.getCodeTable().filter($.extend({}, filter, { value: parentValue })));
} else {
// call local filter
var data = this.convertDataOptions(this.getCodeTable());
if (typeof filter === "function") {
return filter.call(this, parentValue, data);
} else {
return data.filter(function (item) {
return item[filter] == parentValue;
});
}
}
}
}
},
/**
* is available when no parent value.
* if no parent assigned, always return true.
* @returns {boolean}
*/
isAvailableWhenNoParentValue: function () {
// when has parent, return availableWhenNoParentValue
// or return true
return this.hasParent() ? this.getComponentOption("availableWhenNoParentValue") : true;
},
getComponent: function () {
return $(ReactDOM.findDOMNode(this.refs.comp));
}
}));
$pt.Components.NSelect = NSelect;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Select, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NSelect, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
/**
* popover will be closed on
* 2.1 mouse down on others in document
* 2.2 press escape or tab
* 2.3 mouse wheel
* 2.4 window resize
*/
(function (window, $, React, ReactDOM, $pt) {
var NSelectTree = React.createClass($pt.defineCellComponent({
displayName: 'NSelectTree',
mixins: [$pt.mixins.PopoverMixin],
statics: {
POP_FIX_ON_BOTTOM: false,
PLACEHOLDER: "Please Select...",
CLOSE_TEXT: 'Close'
},
getDefaultProps: function () {
return {
defaultOptions: {
hideChildWhenParentChecked: false
},
treeLayout: {
comp: {
root: false,
check: true,
multiple: true,
hierarchyCheck: false
}
}
};
},
afterWillUpdate: function (nextProps) {
if (this.hasParent()) {
// add post change listener into parent model
this.getParentModel().removePostChangeListener(this.getParentPropertyId(), this.onParentModelChanged);
}
},
afterDidUpdate: function (prevProps, prevState) {
if (this.hasParent()) {
// add post change listener into parent model
this.getParentModel().addPostChangeListener(this.getParentPropertyId(), this.onParentModelChanged);
}
if (this.state.popoverDiv && this.state.popoverDiv.is(':visible')) {
this.showPopover();
}
},
afterDidMount: function () {
if (this.hasParent()) {
// add post change listener into parent model
this.getParentModel().addPostChangeListener(this.getParentPropertyId(), this.onParentModelChanged);
}
if (this.state.onloading) {
this.getCodeTable().initializeRemote().done(function () {
this.setState({ onloading: false });
}.bind(this));
}
this.state.mounted = true;
},
beforeWillUnmount: function () {
this.destroyPopover();
},
afterWillUnmount: function () {
if (this.hasParent()) {
// add post change listener into parent model
this.getParentModel().removePostChangeListener(this.getParentPropertyId(), this.onParentModelChanged);
}
},
renderTree: function () {
var layout = $pt.createCellLayout('values', this.getTreeLayout());
var model = $pt.createModel({ values: this.getValueFromModel() });
model.addPostChangeListener('values', this.onTreeValueChanged);
return React.createElement($pt.Components.NTree, { model: model, layout: layout, key: 'tree' });
},
renderSelectionItem: function (codeItem, nodeId) {
if (this.isMobilePhone()) {
return React.createElement(
'li',
{ key: nodeId },
codeItem.text
);
} else {
return React.createElement(
'li',
{ key: nodeId },
React.createElement('span', { className: 'fa fa-fw fa-remove', onClick: this.onSelectionItemRemove.bind(this, nodeId) }),
codeItem.text
);
}
},
renderSelectionWhenValueAsArray: function (values) {
var _this = this;
var codes = null;
if (this.isHideChildWhenParentChecked()) {
// only render parent selections
codes = this.getAvailableTreeModel().list();
var isChecked = function (code) {
return -1 != values.findIndex(function (value) {
return value == code.id;
});
};
var traverse = function (codes) {
return codes.map(function (code) {
if (isChecked(code)) {
return _this.renderSelectionItem(code, code.id);
} else if (code.children) {
return traverse(code.children);
}
});
};
return traverse(codes);
} else {
// render all selections
codes = this.getAvailableTreeModel().listAllChildren();
return Object.keys(codes).map(function (id) {
var value = values.find(function (value) {
return value == id;
});
if (value != null) {
return _this.renderSelectionItem(codes[value], value);
}
});
}
},
renderSelectionWhenValueAsJSON: function (values) {
var _this = this;
var codes = this.getAvailableTreeModel().listWithHierarchyKeys({ separator: NTree.NODE_SEPARATOR, rootId: NTree.ROOT_ID });
if (this.isHideChildWhenParentChecked()) {
var paintedNodes = [];
var isPainted = function (nodeId) {
// if nodeId starts with paintedNodeId, do not paint again
return -1 != paintedNodes.findIndex(function (paintedNodeId) {
return nodeId.startsWith(paintedNodeId);
});
};
return Object.keys(codes).map(function (nodeId) {
if (!isPainted(nodeId)) {
var valueId = nodeId.split(NTree.NODE_SEPARATOR).slice(1).join($pt.PROPERTY_SEPARATOR) + $pt.PROPERTY_SEPARATOR + 'selected';
var checked = $pt.getValueFromJSON(values, valueId);
if (checked) {
paintedNodes.push(nodeId + NTree.NODE_SEPARATOR);
return _this.renderSelectionItem(codes[nodeId], nodeId);
}
}
});
} else {
var render = function (node, currentId, parentId) {
var nodeId = parentId + NTree.NODE_SEPARATOR + currentId;
var spans = [];
if (node.selected) {
spans.push(_this.renderSelectionItem(codes[nodeId], nodeId));
}
spans.push.apply(spans, Object.keys(node).filter(function (key) {
return key != 'selected';
}).map(function (key) {
return render(node[key], key, nodeId);
}));
return spans;
};
return Object.keys(values).filter(function (key) {
return key != 'selected';
}).map(function (key) {
return render(values[key], key, NTree.ROOT_ID);
});
}
},
renderSelection: function () {
var values = this.getValueFromModel();
if (values == null) {
// no selection
return null;
} else if (this.getTreeLayout().comp.valueAsArray) {
// value as an array
return this.renderSelectionWhenValueAsArray(values);
} else {
// value as a hierarchy json object
return this.renderSelectionWhenValueAsJSON(values);
}
},
renderText: function () {
var renderContent = function () {
if (this.isOnLoading() && !this.state.mounted) {
this.state.onloading = true;
return React.createElement(
'span',
{ className: 'text' },
$pt.Components.NCodeTableWrapper.ON_LOADING
);
} else {
this.state.onloading = false;
var value = this.getValueFromModel();
if (value == null || Array.isArray(value) && value.length == 0 || typeof value === 'object' && Object.keys(value).length == 0) {
if (this.isViewMode()) {
return React.createElement('span', { className: 'text' });
} else {
return React.createElement(
'span',
{ className: 'text' },
this.getComponentOption('placeholder', NSelectTree.PLACEHOLDER)
);
}
} else {
return React.createElement(
'ul',
{ className: 'selection' },
this.renderSelection()
);
}
}
}.bind(this);
return React.createElement(
'div',
{ className: 'input-group form-control', onClick: this.onComponentClicked, ref: 'comp' },
renderContent(),
React.createElement('span', { className: 'fa fa-fw fa-sort-down pull-right' })
);
},
render: function () {
var css = {
'n-disabled': !this.isEnabled(),
'n-view-mode': this.isViewMode()
};
css[this.getComponentCSS('n-select-tree')] = true;
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css),
'aria-readonly': 'true',
readOnly: 'true',
tabIndex: this.isEnabled() ? '0' : null },
this.renderText(),
this.renderNormalLine(),
this.renderFocusLine()
);
},
renderPopoverOperations: function () {
if (!this.isMobilePhone()) {
return null;
}
return React.createElement(
'div',
{ className: 'operations', key: 'operations' },
React.createElement(
'div',
null,
React.createElement(
'a',
{ href: 'javascript:void(0);', onClick: this.hidePopover },
React.createElement(
'span',
null,
NSelectTree.CLOSE_TEXT
)
)
)
);
},
getPopoverContainerCSS: function () {
return 'n-select-tree-popover';
},
renderPopoverContent: function () {
return [this.renderTree(), this.renderPopoverOperations()];
},
afterPopoverRenderComplete: function () {
if (this.isMobilePhone()) {
var tree = this.state.popoverDiv.find('div.n-tree > ul');
tree.on('touchstart', this.onTreeTouchStart).on('touchmove', this.onTreeTouchMove).on('touchend', this.onTreeTouchEnd);
}
},
afterDestoryPopover: function () {
if (this.state.popoverDiv) {
if (this.isMobilePhone()) {
var tree = this.state.popoverDiv.find('div.n-tree > ul');
tree.off('touchstart', this.onTreeTouchStart).off('touchmove', this.onTreeTouchMove).off('touchend', this.onTreeTouchEnd);
}
}
},
isOnLoading: function () {
// var value = this.getValueFromModel();
var codetable = this.getCodeTable();
// remote and not initialized
// is on loading
return codetable.isRemoteButNotInitialized();
},
onComponentClicked: function () {
if (!this.isEnabled() || this.isViewMode()) {
// do nothing
return;
}
if (this.isOnLoading()) {
this.getCodeTable().initializeRemote().done(function () {
this.setState({ onloading: false });
this.showPopover();
}.bind(this));
} else {
this.showPopover();
}
},
/**
* on parent model changed
*/
onParentModelChanged: function () {
var parentChanged = this.getComponentOption('parentChanged');
if (parentChanged) {
this.setValueToModel(parentChanged.call(this, this.getModel(), this.getParentPropertyValue()));
} else {
// clear values
this.setValueToModel(null);
}
this.forceUpdate();
},
/**
* on tree value changed
*/
onTreeValueChanged: function (evt) {
var values = evt.new;
if (values == null) {
this.setValueToModel(values);
} else if (Array.isArray(values)) {
this.setValueToModel(values.slice(0));
} else {
this.setValueToModel($.extend(true, {}, values));
}
},
onSelectionItemRemove: function (nodeId) {
if (!this.isEnabled()) {
// do nothing
return;
}
var values = this.getValueFromModel();
var hierarchyCheck = this.getTreeLayout().comp.hierarchyCheck;
if (values == null) {
// do nothing
} else if (this.getTreeLayout().comp.valueAsArray) {
if (hierarchyCheck) {
var codes = this.getAvailableTreeModel().listWithHierarchyKeys({ separator: NTree.NODE_SEPARATOR, rootId: NTree.ROOT_ID });
var codeHierarchyIds = Object.keys(codes);
// find all children
var childrenIds = codeHierarchyIds.filter(function (key) {
return key.indexOf(nodeId + NTree.NODE_SEPARATOR) != -1;
}).map(function (id) {
return id.split(NTree.NODE_SEPARATOR).pop();
});
var hierarchyId = codeHierarchyIds.find(function (id) {
return id.endsWith(NTree.NODE_SEPARATOR + nodeId);
});
// find itself and its ancestor ids
var ancestorIds = codeHierarchyIds.filter(function (id) {
return hierarchyId.startsWith(id);
}).map(function (id) {
return id.split(NTree.NODE_SEPARATOR).pop();
});
// combine
var ids = childrenIds.concat(ancestorIds);
// filter found ids
this.setValueToModel(values.filter(function (id) {
return -1 == ids.findIndex(function (idNeedRemove) {
return id == idNeedRemove;
});
}));
} else {
// remove itself
this.setValueToModel(values.filter(function (id) {
return id != nodeId;
}));
}
} else {
var effectiveNodes = nodeId.split(NTree.NODE_SEPARATOR).slice(1);
var node = $pt.getValueFromJSON(values, effectiveNodes.join($pt.PROPERTY_SEPARATOR));
if (hierarchyCheck) {
// set itself and its children to unselected
Object.keys(node).forEach(function (key) {
delete node[key];
});
// set its ancestors to unselected
effectiveNodes.splice(effectiveNodes.length - 1, 1);
effectiveNodes.forEach(function (id, index, array) {
$pt.setValueIntoJSON(values, array.slice(0, index + 1).join($pt.PROPERTY_SEPARATOR) + $pt.PROPERTY_SEPARATOR + 'selected', false);
});
} else {
// set itself to unselected
delete node.selected;
}
this.getModel().firePostChangeEvent(this.getDataId(), values, values);
}
},
isNodeCheckClicked: function (evt) {
return $(evt.target).closest('.n-checkbox').length != 0;
},
getNodeTouchEventContainer: function (evt) {
return $(evt.target).closest('.n-tree').children('ul').first();
},
getNodeContainerOffsetY: function (container) {
var transform = container.css('transform').split(',');
if (transform.length > 5) {
return parseFloat(transform[5]);
} else {
return 0;
}
},
calcNodeContainerOffsetY: function (target, offsetY) {
if (offsetY >= 0) {
offsetY = 0;
} else {
var treeHeight = target.height();
var totalHeight = target.parent().height();
if (treeHeight <= totalHeight) {
return 0;
}
if (offsetY < totalHeight - treeHeight) {
offsetY = totalHeight - treeHeight;
}
}
return offsetY;
},
unwrapTouchEvent: function (evt) {
return evt.touches ? evt : evt.originalEvent;
},
onTreeTouchStart: function (evt) {
if (this.isNodeCheckClicked(evt)) {
return;
}
this.state.touchStartClientY = this.unwrapTouchEvent(evt).touches[0].clientY;
var target = this.getNodeTouchEventContainer(evt);
this.state.touchStartRelatedY = this.getNodeContainerOffsetY(target);
this.state.touchStartTime = moment();
},
onTreeTouchMove: function (evt) {
if (this.isNodeCheckClicked(evt)) {
return;
}
var touches = this.unwrapTouchEvent(evt).touches;
var length = touches.length;
if (length > 0) {
var target = this.getNodeTouchEventContainer(evt);
// calculate the distance of touch moving
// make sure the first and last option are in viewport
var distance = touches[length - 1].clientY - this.state.touchStartClientY;
var offsetY = this.calcNodeContainerOffsetY(target, this.state.touchStartRelatedY + distance);
target.css('transform', 'translateY(' + offsetY + 'px)');
this.state.touchLastClientY = touches[length - 1].clientY;
}
},
onTreeTouchEnd: function (evt) {
if (this.isNodeCheckClicked(evt)) {
return;
}
// continue scrolling
// calculate the speed
var timeUsed = moment().diff(this.state.touchStartTime, 'ms');
// alert(timeUsed);
if (timeUsed <= 300 && this.state.touchLastClientY != null) {
var distance = this.state.touchLastClientY - this.state.touchStartClientY;
var speed = distance / timeUsed * 10; // pixels per 10 ms
var target = this.getNodeTouchEventContainer(evt);
var startOffsetY = this.getNodeContainerOffsetY(target);
var targetOffsetY = this.calcNodeContainerOffsetY(target, startOffsetY + speed * 100 / 2);
target.one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function () {
target.css({
'transition-timing-function': '',
'transition-duration': ''
});
});
target.css({
'transition-timing-function': 'cubic-bezier(0.1, 0.57, 0.1, 1)',
'transition-duration': '500ms',
'transform': 'translateY(' + targetOffsetY + 'px)'
});
}
delete this.state.touchStartClientY;
delete this.state.touchStartRelatedY;
delete this.state.touchStartTime;
delete this.state.touchLastClientY;
},
getComponent: function () {
return $(ReactDOM.findDOMNode(this.refs.comp));
},
/**
* get tree model
* @returns {CodeTable}
*/
getCodeTable: function () {
return this.getComponentOption('data');
},
/**
* get available tree model
* @returns {CodeTable}
*/
getAvailableTreeModel: function () {
var filter = this.getComponentOption('parentFilter');
var tree = this.getCodeTable();
// fetch data from remote is not supported now
if (filter) {
return filter.call(this, tree, this.getParentPropertyValue());
} else {
return tree;
}
},
getTreeLayout: function () {
var treeLayout = this.getComponentOption('treeLayout');
if (treeLayout) {
treeLayout = $.extend(true, {}, this.props.treeLayout, treeLayout);
} else {
treeLayout = $.extend(true, {}, this.props.treeLayout);
}
treeLayout.comp.data = this.getAvailableTreeModel();
treeLayout.comp.valueAsArray = treeLayout.comp.valueAsArray ? treeLayout.comp.valueAsArray : false;
treeLayout.evt = treeLayout.evt ? treeLayout.evt : {};
treeLayout.evt.expand = treeLayout.evt.expand ? function (evt) {
treeLayout.evt.expand.call(this, evt);
this.onPopoverRenderComplete.call(this);
} : this.onPopoverRenderComplete;
treeLayout.evt.collapse = treeLayout.evt.collapse ? function (evt) {
treeLayout.evt.collapse.call(this, evt);
this.onPopoverRenderComplete.call(this);
} : this.onPopoverRenderComplete;
return treeLayout;
},
isHideChildWhenParentChecked: function () {
var hierarchyCheck = this.getTreeLayout().comp.hierarchyCheck;
if (hierarchyCheck) {
return this.getComponentOption('hideChildWhenParentChecked');
} else {
return false;
}
},
/**
* has parent or not
* @returns {boolean}
*/
hasParent: function () {
return this.getParentPropertyId() != null;
},
/**
* get parent property id
* @returns {string}
*/
getParentPropertyId: function () {
return this.getComponentOption("parentPropId");
},
/**
* get parent model
* @returns {ModelInterface}
*/
getParentModel: function () {
var parentModel = this.getComponentOption("parentModel");
return parentModel == null ? this.getModel() : parentModel;
},
/**
* get parent property value
* @returns {*}
*/
getParentPropertyValue: function () {
return this.getParentModel().get(this.getParentPropertyId());
}
}));
$pt.Components.NSelectTree = NSelectTree;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.SelectTree, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NSelectTree, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NSideMenu = React.createClass({
displayName: 'NSideMenu',
statics: {
/**
* get side menu
* @param menus
* @param containerId optional, default is 'side_menu_container'
* @param className
* @param hover
* @returns {react element}
*/
getSideMenu: function (menus, containerId, className, hover) {
if (!containerId) {
containerId = "side_menu_container";
}
if ($pt.sideMenu == null) {
$pt.sideMenu = {};
}
if ($pt.sideMenu[containerId] == null) {
// must initial here. since the function will execute immediately after load,
// and NExceptionModal doesn't defined in that time
var sideMenuContainer = $("#" + containerId);
if (sideMenuContainer.length == 0) {
$("<div id='" + containerId + "' />").appendTo($(document.body));
}
$pt.sideMenu[containerId] = ReactDOM.render(React.createElement($pt.Components.NSideMenu, { menus: menus,
className: className,
hover: hover ? true : false }), document.getElementById(containerId));
}
return $pt.sideMenu[containerId];
}
},
getDefaultProps: function () {
return {
hover: false
};
},
getInitialState: function () {
return {};
},
componentDidMount: function () {
$(ReactDOM.findDOMNode(this.refs.menus)).hide();
},
renderMenuItem: function (item, index, menus, onTopLevel) {
if (item.children !== undefined) {
// render dropdown menu
var _this = this;
var id = 'item_' + index;
return React.createElement(
"li",
{ ref: id, key: index },
React.createElement(
"a",
{ href: "javascript:void(0);",
onClick: this.onParentMenuClicked.bind(this, id), ref: id + '_link' },
item.text,
React.createElement("span", { className: "fa fa-fw fa-angle-double-down n-side-menu-ul", ref: id + '_icon' })
),
React.createElement(
"ul",
{ ref: id + '_child', style: { display: 'none' } },
item.children.map(function (childItem, childIndex, dropdownItems) {
return _this.renderMenuItem(childItem, index + '_' + childIndex, dropdownItems, false);
})
)
);
} else if (item.func !== undefined) {
// call javascript function
return React.createElement(
"li",
{ key: index },
React.createElement(
"a",
{ href: "javascript:void(0);",
onClick: this.onMenuClicked.bind(this, item.func, item.value) },
item.text
)
);
} else if (item.divider === true) {
return null;
} else {
// jump to url
return React.createElement(
"li",
{ key: index },
React.createElement(
"a",
{ href: item.url },
item.text
)
);
}
},
render: function () {
var _this = this;
return React.createElement(
"div",
{ className: "n-side-menu", ref: "menus",
onMouseEnter: this.onMouseEnter, onMouseLeave: this.onMouseLeave },
React.createElement(
"ul",
{ className: "nav navbar-nav" },
this.props.menus.map(function (item, index, menu) {
return _this.renderMenuItem(item, index, menu, true);
}),
React.createElement(
"li",
{ className: "n-side-menu-close" },
React.createElement(
"a",
{ href: "javascript:void(0);", onClick: this.onCloseClicked },
React.createElement("span", { className: "fa fa-fw fa-arrow-circle-left" })
)
)
)
);
},
onMouseEnter: function () {
if (this.props.hover) {
this.show();
}
},
onMouseLeave: function () {
if (this.props.hover) {
this.willHide();
}
},
/**
* on menu clicked
* @param func
* @param value
*/
onMenuClicked: function (func, value) {
func.call(this, value);
},
onParentMenuClicked: function (id) {
$(ReactDOM.findDOMNode(this.refs[id + '_link'])).blur();
var ul = $(ReactDOM.findDOMNode(this.refs[id + '_child']));
ul.toggle('fade', function () {
// if close, then close all sub menus
if (ul.not(':visible')) {
ul.find('ul').hide();
}
});
$(ReactDOM.findDOMNode(this.refs[id + '_icon'])).toggleClass('fa-angle-double-down fa-angle-double-up');
this.collapseMenus(id);
},
/**
* collapse menus
* @param id {string} menu id which keep expanding
*/
collapseMenus: function (id) {
var _this = this;
Object.keys(this.refs).forEach(function (key) {
if (key.endsWith('_link')) {
var linkId = key.substr(0, key.length - 5);
if (!id || !id.startsWith(linkId)) {
var ul = $(ReactDOM.findDOMNode(_this.refs[linkId + '_child']));
ul.hide('fade', function () {
ul.find('ul').hide();
});
}
}
});
$(ReactDOM.findDOMNode(this.refs[id + '_icon'])).toggleClass('fa-angle-double-down fa-angle-double-up');
},
/**
* on close button clicked
*/
onCloseClicked: function () {
this.hide();
},
/**
* show side menu
*/
show: function () {
if (this.state.willHide) {
clearTimeout(this.state.willHide);
this.state.willHide = null;
}
$(ReactDOM.findDOMNode(this.refs.menus)).show('fade');
},
/**
* hide side menu
*/
hide: function () {
var _this = this;
$(ReactDOM.findDOMNode(this.refs.menus)).hide('fade', function () {
_this.collapseMenus();
});
},
willHide: function () {
var _this = this;
this.state.willHide = setTimeout(function () {
_this.hide();
}, 300);
}
});
$pt.Components.NSideMenu = NSideMenu;
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NTab = React.createClass({
displayName: 'NTab',
getDefaultProps: function () {
return {
type: 'tab',
justified: false,
removable: false
};
},
getInitialState: function () {
return {};
},
componentDidUpdate: function () {
this.renderRelatedDOM();
},
componentDidMount: function () {
this.renderRelatedDOM();
},
renderRelatedDOM: function () {
var activeTabIndex = this.getActiveTabIndex();
this.props.tabs.forEach(function (tab, index) {
if (activeTabIndex == index) {
$('#' + tab.innerId).show();
} else {
$('#' + tab.innerId).hide();
}
});
},
/**
* render icon
* @param icon {string|XML}
* @param size {string}
* @returns {XML}
*/
renderIcon: function (icon, size) {
if (typeof icon === 'string') {
var css = {
'fa': true,
'fa-fw': true
};
css['fa-' + icon] = true;
if (size) {
css['fa-' + size] = true;
}
return React.createElement('span', { className: $pt.LayoutHelper.classSet(css) });
} else {
return icon;
}
},
/**
* render label
* @param label {string}
* @returns {XML}
*/
renderLabel: function (label) {
if (label) {
return React.createElement(
'span',
null,
' ' + label
);
} else {
return null;
}
},
renderBadge: function (badge) {
if (badge) {
return React.createElement(
'span',
{ className: 'badge' },
badge
);
} else {
return null;
}
},
/**
* render tab
* @param tab {{active:boolean, label:string, icon:string, badge:string, removable: boolean, visible:boolean}}
* @param index
* @returns {XML}
*/
renderTab: function (tab, index) {
var css = {
active: index == this.getActiveTabIndex(),
hide: tab.visible === false
};
if (tab.css) {
css[tab.css] = true;
}
var removeButton = React.createElement(
'a',
{ href: 'javascript:void(0);', className: 'n-tab-delete',
onClick: this.onRemoveClicked },
React.createElement('span', { className: 'fa fa-fw fa-times' })
);
return React.createElement(
'li',
{ role: 'presentation', className: $pt.LayoutHelper.classSet(css), key: index },
React.createElement(
'a',
{ href: 'javascript:void(0);', onClick: this.onClicked },
this.renderIcon(tab.icon, this.props.size),
this.renderLabel(tab.label),
this.renderBadge(tab.badge)
),
this.canRemove(tab) ? removeButton : null
);
},
/**
* render
* @returns {XML}
*/
render: function () {
var css = {
'nav': true,
'nav-justified': this.props.justified === true,
'nav-tabs': this.props.type === 'tab',
'nav-pills': this.props.type === 'pill',
'nav-direction-vertical': this.props.direction === 'vertical'
};
if (this.props.tabClassName) {
css[this.props.tabClassName] = true;
}
return React.createElement(
'div',
{ className: 'n-tab' },
React.createElement(
'ul',
{ className: $pt.LayoutHelper.classSet(css), ref: 'tabs' },
this.props.tabs.map(this.renderTab)
)
);
},
/**
* check the given tab can be removed or not
* @param tab {{removable: boolean}}
* @returns {boolean}
*/
canRemove: function (tab) {
if (tab.removable != null) {
return tab.removable === true;
} else {
return this.props.removable;
}
},
/**
* get active tab index
* @returns {number}
*/
getActiveTabIndex: function () {
// find the active tab
var activeTabIndex = this.props.tabs.findIndex(function (tab, index) {
return tab.active === true;
});
if (activeTabIndex == -1) {
// find the first visible tab if no active tab found
activeTabIndex = this.props.tabs.findIndex(function (tab, index) {
var visible = tab.visible !== false;
if (visible) {
tab.active = true;
return true;
}
});
}
return activeTabIndex;
},
/**
* set active tab index
* @param {number}
*/
setActiveTabIndex: function (index) {
if (index < 0 || index >= this.props.tabs.length) {
window.console.warn('Tab index[' + index + '] out of bound.');
}
this.props.tabs.forEach(function (tab, tabIndex) {
tab.active = tabIndex == index;
});
this.forceUpdate();
return this;
},
/**
* on tab clicked
* @param evt
*/
onClicked: function (evt) {
var newTab = $(evt.target).closest('li');
var newTabIndex = newTab.index();
var activeTabIndex = this.getActiveTabIndex();
var canActive = this.props.canActive;
if (canActive) {
var activeTab = this.props.tabs[activeTabIndex];
var ret = canActive.call(this, this.props.tabs[newTabIndex].value, newTabIndex, activeTab.value, activeTabIndex);
if (ret === false) {
$(':focus').blur();
return;
}
}
this.setActiveTabIndex(newTabIndex);
var onActive = this.props.onActive;
if (onActive) {
onActive.call(this, this.props.tabs[newTabIndex].value, newTabIndex);
}
},
/**
* on tab remove clicked
* @param evt
*/
onRemoveClicked: function (evt) {
var selectedTab = $(evt.target).closest('li');
selectedTab.addClass('active');
selectedTab.parent().children('li').not(selectedTab).removeClass('active');
var activeIndex = selectedTab.index();
var activeTab = this.props.tabs[activeIndex];
var activeValue = activeTab.value;
// trigger can remove event, check it can remove or not
var canRemove = this.props.canRemove;
if (canRemove) {
var ret = canRemove.call(this, activeValue, activeIndex);
if (ret === false) {
return;
}
}
// remove tab
this.props.tabs[activeIndex].visible = false;
// find the visible tab
// if tab index more than removed one, stop finding
// or return the last visible tab which before removed tab
var activeTabIndex = -1;
this.props.tabs.some(function (tab, index) {
if (tab.visible !== false) {
activeTabIndex = index;
return index > activeIndex;
}
});
this.setActiveTabIndex(activeTabIndex);
// trigger on remove event
var onRemove = this.props.onRemove;
if (onRemove) {
onRemove.call(this, activeValue, activeIndex);
}
}
});
$pt.Components.NTab = NTab;
})(window, jQuery, React, ReactDOM, $pt);
/**
* table
*
* depends NIcon, NText, NModalForm, NConfirm, NPagination
*/
(function (window, $, React, ReactDOM, $pt) {
var NTable = React.createClass($pt.defineCellComponent({
displayName: 'NTable',
mixins: [$pt.mixins.ArrayComponentMixin],
statics: {
__operationButtonWidth: 31,
__minOperationButtonWidth: 40,
ROW_HEIGHT: 32,
TOOLTIP_EDIT: null,
TOOLTIP_REMOVE: null,
TOOLTIP_MORE: 'More Operations...',
/**
* set operation button width
* @param width {number}
*/
setOperationButtonWidth: function (width) {
NTable.__operationButtonWidth = width;
},
ADD_BUTTON_ICON: "plus",
ADD_BUTTON_TEXT: "",
DOWNLOADABLE: false,
DOWNLOAD_BUTTON_ICON: "cloud-download",
DOWNLOAD_BUTTON_TEXT: "",
NO_DATA_DOWNLOAD_TITLE: 'Downloading...',
NO_DATA_DOWNLOAD: "No data needs to be downloaded...",
SEARCH_PLACE_HOLDER: "Search...",
ROW_EDIT_BUTTON_ICON: "pencil",
ROW_REMOVE_BUTTON_ICON: "trash-o",
ROW_MORE_BUTTON_ICON: 'sort-down',
EDIT_DIALOG_SAVE_BUTTON_TEXT: "Save",
EDIT_DIALOG_SAVE_BUTTON_ICON: 'floppy-o',
SORT_ICON: "sort",
SORT_ASC_ICON: "sort-amount-asc",
SORT_DESC_ICON: "sort-amount-desc",
NO_DATA_LABEL: "No Data",
INDEX_HEADER_TEXT: '#',
INDEX_HEADER_WIDTH: 40,
DETAIL_ERROR_MESSAGE: "Detail error please open item and do validate.",
REMOVE_CONFIRM_TITLE: "Delete data?",
REMOVE_CONFIRM_MESSAGE: ["Are you sure you want to delete data?", "Deleted data cannot be recovered."],
BOOLEAN_TRUE_DISPLAY_TEXT: 'Y',
BOOLEAN_FALSE_DISPLAY_TEXT: 'N',
PAGE_JUMPING_PROXY: null,
PAGE_JUMPING_PROXY_CALLBACK: null,
registerInlineEditor: function (type, definition) {
if (NTable.__inlineEditors[type] != null) {
window.console.warn("Inline editor[" + type + "] is repalced.");
window.console.warn("From:");
window.console.warn(NTable.__inlineEditors[type]);
window.console.warn("To:");
window.console.warn(definition);
}
NTable.__inlineEditors[type] = definition;
},
getInlineEditor: function (type) {
var editor = NTable.__inlineEditors[type];
if (editor == null) {
editor = NTable['__' + type];
}
if (editor == null) {
throw $pt.createComponentException($pt.ComponentConstants.Err_Unsupported_Component, "Inline component type[" + type + "] is not supported yet.");
}
return editor;
},
__inlineEditors: {},
__text: {
comp: {
type: { type: $pt.ComponentConstants.Text, label: false }
}
},
__check: {
comp: {
type: { type: $pt.ComponentConstants.Check, label: false }
}
},
__date: {
comp: {
type: { type: $pt.ComponentConstants.Date, label: false }
}
},
__select: {
comp: {
type: { type: $pt.ComponentConstants.Select, label: false }
}
},
__radio: {
comp: {
type: { type: $pt.ComponentConstants.Radio, label: false }
}
}
},
getDefaultProps: function () {
return {
defaultOptions: {
header: true,
scrollY: false,
scrollX: false,
fixedRightColumns: 0,
fixedLeftColumns: 0,
addable: false,
searchable: true,
// downloadable: false,
operationFixed: false,
editable: false,
removable: false,
indexable: false,
indexFixed: false,
rowSelectFixed: false,
sortable: true,
pageable: false,
countPerPage: 20,
dialogResetVisible: false,
dialogValidateVisible: false,
collapsible: false,
expanded: true
}
};
},
/**
* get initial state
* @returns {*}
*/
getInitialState: function () {
//var _this = this;
return {
sortColumn: null,
sortWay: null, // asc|desc
countPerPage: 20,
pageCount: 1,
currentPageIndex: 1,
searchText: null,
searchModel: $pt.createModel({
text: null
}),
searchLayout: $pt.createCellLayout('text', {
comp: {
placeholder: NTable.SEARCH_PLACE_HOLDER
},
css: {
comp: 'n-table-search-box'
}
})
};
},
/**
* attach listeners
*/
attachListeners: function () {
var _this = this;
this.getScrollBodyComponent().on("scroll", function (e) {
var $this = $(this);
_this.getScrollHeaderComponent().scrollLeft($this.scrollLeft());
_this.getFixedLeftBodyComponent().scrollTop($this.scrollTop());
_this.getFixedRightBodyComponent().scrollTop($this.scrollTop());
});
this.getDivComponent().on("mouseenter", "tbody tr", function () {
//$(this).addClass("hover");
var index = $(this).parent().children().index($(this));
_this.getDivComponent().find("tbody tr:nth-child(" + (index + 1) + ")").addClass("hover");
}).on("mouseleave", "tbody tr", function () {
var index = $(this).parent().children().index($(this));
_this.getDivComponent().find("tbody tr:nth-child(" + (index + 1) + ")").removeClass("hover");
});
// this.renderIfIE8();
this.renderHeaderPopover();
this.addPostChangeListener(this.onModelChanged);
this.state.searchModel.addPostChangeListener('text', this.onSearchBoxChanged);
this.addPostRemoveListener(this.onModelChanged);
this.addPostAddListener(this.onModelChanged);
this.addPostValidateListener(this.onModelValidateChanged);
this.addVisibleDependencyMonitor();
},
/**
* detach listeners
*/
detachListeners: function () {
this.getScrollBodyComponent().off("scroll");
this.getDivComponent().off("mouseenter", "tbody tr").off("mouseleave", "tbody tr");
$(ReactDOM.findDOMNode(this.refs[this.getHeaderLabelId()])).popover("destroy");
this.removePostChangeListener(this.onModelChanged);
this.state.searchModel.removePostChangeListener('text', this.onSearchBoxChanged);
this.removePostRemoveListener(this.onModelChanged);
this.removePostAddListener(this.onModelChanged);
this.removePostValidateListener(this.onModelValidateChanged);
this.removeVisibleDependencyMonitor();
},
/**
* will update
* @param nextProps
*/
componentWillUpdate: function (nextProps) {
this.detachListeners();
if (nextProps != this.props) {
// clear definition
this.state.columns = null;
}
this.unregisterFromComponentCentral();
},
/**
* did update
* @param prevProps
* @param prevState
*/
componentDidUpdate: function (prevProps, prevState) {
this.attachListeners();
this.registerToComponentCentral();
},
/**
* did mount
*/
componentDidMount: function () {
this.attachListeners();
this.registerToComponentCentral();
},
/**
* will unmount
*/
componentWillUnmount: function () {
this.detachListeners();
this.unregisterFromComponentCentral();
this.destroyPopover();
},
/**
* render when IE8, fixed the height of table since IE8 doesn't support max-height
*/
// renderIfIE8: function () {
// if (!this.isIE8() || !this.hasVerticalScrollBar()) {
// return;
// }
// var mainTable = this.getComponent();
// var leftFixedDiv = this.getFixedLeftBodyComponent();
// var rightFixedDiv = this.getFixedRightBodyComponent();
// var trs = mainTable.find("tr");
// var rowCount = trs.length;
// var height = rowCount * NTable.ROW_HEIGHT; // 32 is defined in css, if value in css is changed, it must be changed together
// if (height > this.getComponentOption("scrollY")) {
// height = this.getComponentOption("scrollY");
// }
// // calculate height of body if ie8 and scrollY
// mainTable.closest("div").css({
// height: height + 17
// });
// leftFixedDiv.css({
// height: height
// });
// rightFixedDiv.css({
// height: height
// });
// },
// isIE: function () {
// return $pt.browser.msie;
// },
/**
* check browser is IE8 or not
* @returns {boolean}
*/
isIE8: function () {
return $pt.browser.msie && $pt.browser.version == 8;
},
/**
* check browser is firefox or not
* @returns {boolean}
*/
isFirefox: function () {
return $pt.browser.mozilla;
},
/**
* prepare display options
*/
prepareDisplayOptions: function () {
if (this.state.columns != null) {
// already initialized, do nothing and return
return;
}
var _this = this;
// this.state.searchModel.addListener('text', 'post', 'change', this.onSearchBoxChanged);
// copy from this.props.columns
this.state.columns = this.getComponentOption("columns");
// is it is json array, construct to TableColumnLayout object
if (Array.isArray(this.state.columns)) {
this.state.columns = $pt.createTableColumnLayout(this.state.columns);
} else {
// get original columns definition can create new object
this.state.columns = $pt.createTableColumnLayout(this.state.columns.columns());
}
this.fixedRightColumns = this.getComponentOption("fixedRightColumns");
this.fixedLeftColumns = this.getComponentOption("fixedLeftColumns");
var config = null;
// if editable or removable, auto add last column to render the buttons
var editable = this.isEditable();
var removable = this.isRemovable();
var rowOperations = this.getComponentOption("rowOperations");
if (rowOperations == null) {
rowOperations = [];
} else if (!Array.isArray(rowOperations)) {
rowOperations = [rowOperations];
}
rowOperations = rowOperations.filter(function (operation) {
if (_this.isViewMode()) {
// in view mode, filter the buttons only in editing
return operation.view != 'edit';
} else if (!_this.isViewMode()) {
// no in view mode, filter the buttons only in view mode
return operation.view != 'view';
}
});
if (editable || removable || rowOperations.length != 0) {
config = {
editable: editable,
removable: removable,
rowOperations: rowOperations,
title: "",
width: this.calcOperationColumnWidth(editable, removable, rowOperations)
};
this.state.columns.push(config);
if (this.fixedRightColumns > 0 || this.getComponentOption("operationFixed") === true) {
this.fixedRightColumns++;
}
}
// if row selectable, auto add first column to render the row select checkbox
var rowSelectable = this.isRowSelectable();
if (rowSelectable) {
config = {
rowSelectable: rowSelectable,
width: 40,
title: ''
};
this.state.columns.splice(0, 0, config);
if (this.fixedLeftColumns > 0 || this.getComponentOption('rowSelectFixed') === true) {
this.fixedLeftColumns++;
}
}
// if indexable, auto add first column to render the row index
var indexable = this.isIndexable();
if (indexable) {
config = {
indexable: true,
width: NTable.INDEX_HEADER_WIDTH,
title: NTable.INDEX_HEADER_TEXT
};
this.state.columns.splice(0, 0, config);
if (this.fixedLeftColumns > 0 || this.getComponentOption("indexFixed") === true) {
this.fixedLeftColumns++;
}
}
},
calcOperationColumnWidth: function (editable, removable, rowOperations) {
var width = this.getComponentOption('operationColumnWidth');
if (width != null) {
return width;
}
var maxButtonCount = this.getComponentOption('maxOperationButtonCount');
if (maxButtonCount) {
var actualButtonCount = (editable ? 1 : 0) + (removable ? 1 : 0) + rowOperations.length;
if (maxButtonCount > actualButtonCount) {
// no button in popover
width = (editable ? NTable.__operationButtonWidth : 0) + (removable ? NTable.__operationButtonWidth : 0);
if (rowOperations.length != 0) {
width += NTable.__operationButtonWidth * rowOperations.length;
}
} else {
// still some buttons in popover
width = (maxButtonCount + 1) * NTable.__operationButtonWidth;
}
} else {
width = (editable ? NTable.__operationButtonWidth : 0) + (removable ? NTable.__operationButtonWidth : 0);
if (rowOperations.length != 0) {
width += NTable.__operationButtonWidth * rowOperations.length;
}
}
return width < NTable.__minOperationButtonWidth ? NTable.__minOperationButtonWidth : width;
},
/**
* render search box
* @returns {XML}
*/
renderSearchBox: function () {
if (this.isSearchable()) {
return React.createElement($pt.Components.NText, { model: this.state.searchModel, layout: this.state.searchLayout });
} else {
return null;
}
},
/**
* render heading buttons
* @returns {*}
*/
renderHeadingButtons: function () {
var style = { display: this.state.expanded ? 'block' : 'none' };
var buttons = [];
if (this.isAddable()) {
buttons.push(React.createElement(
'a',
{ href: 'javascript:void(0);',
onClick: this.onAddClicked,
className: 'n-table-heading-buttons pull-right',
ref: 'add-button',
style: style,
key: 'add-button' },
React.createElement($pt.Components.NIcon, { icon: NTable.ADD_BUTTON_ICON }),
this.getComponentOption('addText') || NTable.ADD_BUTTON_TEXT
));
}
if (this.isDownloadable()) {
buttons.push(React.createElement(
'a',
{ href: 'javascript:void(0);',
onClick: this.onDownloadClicked,
className: 'n-table-heading-buttons pull-right',
ref: 'download-button',
style: style,
key: 'download-button' },
React.createElement($pt.Components.NIcon, { icon: NTable.DOWNLOAD_BUTTON_ICON }),
NTable.DOWNLOAD_BUTTON_TEXT
));
}
return buttons;
},
/**
* render panel heading label
* @returns {XML}
*/
renderPanelHeadingLabel: function () {
var css = "col-sm-3 col-md-3 col-lg-3";
if (this.getModel().hasError(this.getDataId())) {
css += " has-error";
}
var spanCSS = {
'n-table-heading-label': true
};
if (this.isCollapsible()) {
spanCSS['n-table-heading-label-collapsible'] = true;
}
return React.createElement(
'div',
{ className: css },
React.createElement(
'span',
{ className: this.getAdditionalCSS("headingLabel", $pt.LayoutHelper.classSet(spanCSS)),
ref: this.getHeaderLabelId(), onClick: this.isCollapsible() ? this.onTitleClicked : null },
this.getLayout().getLabel(this)
)
);
},
/**
* render header popover
*/
renderHeaderPopover: function () {
if ($pt.ComponentConstants.ERROR_POPOVER && this.getModel().hasError(this.getDataId())) {
var messages = this.getModel().getError(this.getDataId());
var _this = this;
var content = messages.map(function (msg) {
if (typeof msg === "string") {
return "<span style='display:block'>" + msg.format([_this.getLayout().getLabel(this)]) + "</span>";
} else {
return "<span style='display:block'>" + NTable.DETAIL_ERROR_MESSAGE + "</span>";
}
});
$(ReactDOM.findDOMNode(this.refs[this.getHeaderLabelId()])).popover({
placement: 'top',
trigger: 'hover',
html: true,
content: content,
// false is very import, since when destroy popover,
// the really destroy will be invoked by some delay,
// and before really destory invoked,
// the new popover is bind by componentDidUpdate method.
// and finally new popover will be destroyed.
animation: false
});
}
},
/**
* render panel heading
* @returns {XML}
*/
renderPanelHeading: function () {
if (!this.isHeading()) {
return null;
}
return React.createElement(
'div',
{ className: this.getAdditionalCSS("heading", "panel-heading n-table-heading") },
React.createElement(
'div',
{ className: 'row' },
this.renderPanelHeadingLabel(),
React.createElement(
'div',
{ className: 'col-sm-9 col-md-9 col-lg-9' },
this.renderHeadingButtons(),
this.renderSearchBox()
)
)
);
},
/**
* render sort button
* @param column
* @returns {XML}
*/
renderTableHeaderSortButton: function (column) {
if (this.isSortable(column)) {
var icon = NTable.SORT_ICON;
var sortClass = this.getAdditionalCSS("sort", "pull-right n-table-sort");
if (this.state.sortColumn == column) {
sortClass += " " + this.getAdditionalCSS("sorted", "n-table-sorted");
if (this.state.sortWay == "asc") {
icon = NTable.SORT_ASC_ICON;
} else {
icon = NTable.SORT_DESC_ICON;
}
}
return React.createElement(
'a',
{ href: 'javascript:void(0);', className: sortClass,
onClick: this.onSortClicked.bind(this, column) },
React.createElement($pt.Components.NIcon, { icon: icon })
);
}
},
/**
* render checkbox
* @param column
* @returns {XML}
*/
renderTableHeaderCheckBox: function (column) {
var data = this.getDataToDisplay();
var range = this.computePagination(data);
var allSelected = this.isCurrentPageAllSelected(column, data, range);
var model = $pt.createModel({
allCheck: allSelected
});
var layout = $pt.createCellLayout('allCheck', {
comp: {
type: $pt.ComponentConstants.Check
}
});
var _this = this;
model.addListener('allCheck', 'post', 'change', function (evt) {
var selected = evt.new;
if (data != null) {
var rowIndex = 1;
data.forEach(function (row) {
if (rowIndex >= range.min && rowIndex <= range.max) {
$pt.setValueIntoJSON(row, column.rowSelectable, selected);
}
rowIndex++;
});
_this.forceUpdate(_this.onCheckAllChanged.bind(_this, selected));
}
});
return React.createElement($pt.Components.NCheck, { model: model, layout: layout });
},
/**
* render heading content.
* at least and only one parameter can be true.
* if more than one parameter is true, priority as all > leftFixed > rightFixed
* @param all {boolean} render all columns?
* @param leftFixed {boolean} render left fixed columns?
* @param rightFixed {boolean} render right fixed columns?
* @returns {XML}
* @see #getRenderColumnIndexRange
*/
renderTableHeading: function (all, leftFixed, rightFixed) {
var indexToRender = this.getRenderColumnIndexRange(all, leftFixed, rightFixed);
var columnIndex = 0;
var _this = this;
return React.createElement(
'thead',
null,
React.createElement(
'tr',
null,
this.state.columns.map(function (column) {
if (columnIndex >= indexToRender.min && columnIndex <= indexToRender.max) {
// column is fixed.
columnIndex++;
var style = {};
style.width = column.width;
if (column.headerAlign) {
style.textAlign = column.headerAlign;
}
if (!(column.visible === undefined || column.visible === true)) {
style.display = "none";
}
if (column.rowSelectable) {
return React.createElement(
'td',
{ style: style, key: columnIndex },
_this.renderTableHeaderCheckBox(column)
);
} else {
return React.createElement(
'td',
{ style: style, key: columnIndex },
column.title,
_this.renderTableHeaderSortButton(column)
);
}
} else {
columnIndex++;
}
})
)
);
},
renderRowEditButton: function (rowModel) {
var layout = $pt.createCellLayout('editButton', {
comp: {
style: 'link',
icon: NTable.ROW_EDIT_BUTTON_ICON,
enabled: this.getRowEditButtonEnabled(),
click: this.onEditClicked.bind(this, rowModel.getCurrentModel()),
tooltip: NTable.TOOLTIP_EDIT
},
css: {
comp: 'n-table-op-btn'
}
});
return React.createElement($pt.Components.NFormButton, { model: rowModel, layout: layout });
},
renderRowRemoveButton: function (rowModel) {
var layout = $pt.createCellLayout('removeButton', {
comp: {
style: 'link',
icon: NTable.ROW_REMOVE_BUTTON_ICON,
enabled: this.getRowRemoveButtonEnabled(),
click: this.onRemoveClicked.bind(this, rowModel.getCurrentModel()),
tooltip: NTable.TOOLTIP_REMOVE
},
css: {
comp: 'n-table-op-btn'
}
});
return React.createElement($pt.Components.NFormButton, { model: rowModel, layout: layout });
},
isRowOperationVisible: function (operation, rowModel) {
var visible = operation.visible;
if (visible) {
return this.getRuleValue(visible, true, rowModel);
} else {
return true;
}
},
renderRowOperationButton: function (operation, rowModel, operationIndex) {
var layout = $pt.createCellLayout('rowButton', {
label: operation.icon ? null : operation.tooltip,
comp: Object.keys(operation).reduce(function (options, key) {
if (key != 'click') {
options[key] = operation[key];
}
return options;
}, {
style: 'link',
click: this.onRowOperationClicked.bind(this, operation.click, rowModel)
}),
// {
// style: 'link',
// icon: operation.icon,
// enabled: operation.enabled,
// visible: operation.visible,
// click: this.onRowOperationClicked.bind(this, operation.click, rowModel),
// tooltip: operation.tooltip
// },
css: {
comp: 'n-table-op-btn'
}
});
return React.createElement($pt.Components.NFormButton, { model: rowModel, layout: layout, key: operationIndex });
},
getRowOperations: function (column) {
var rowOperations = column.rowOperations;
if (rowOperations === undefined || rowOperations === null) {
rowOperations = [];
}
return rowOperations;
},
/**
* render flat operation cell, all operation button renderred as a line.
*/
renderFlatOperationCell: function (column, rowModel) {
var editButton = column.editable ? this.renderRowEditButton(rowModel) : null;
var removeButton = column.removable ? this.renderRowRemoveButton(rowModel) : null;
var rowOperations = this.getRowOperations(column);
var _this = this;
// rowOperations = rowOperations.filter(function(rowOperation) {
// return _this.isRowOperationVisible(rowOperation, rowModel);
// });
return React.createElement(
'div',
{ className: 'btn-group n-table-op-btn-group', role: 'group' },
rowOperations.map(function (operation, operationIndex) {
return _this.renderRowOperationButton(operation, rowModel, operationIndex);
}),
editButton,
removeButton
);
},
renderPopoverContainer: function () {
if (this.state.popoverDiv == null) {
this.state.popoverDiv = $('<div>');
this.state.popoverDiv.appendTo($('body'));
$(document).on('click', this.onDocumentClicked).on('keyup', this.onDocumentKeyUp);
}
this.state.popoverDiv.hide();
},
/**
* check all row operation buttons in more popover are renderred as icon and tooltip or menu?
* if operation with no icon declared, return false (render as menu)
*/
isRenderMoreOperationButtonsAsIcon: function (moreOperations) {
if (this.getComponentOption('moreAsMenu')) {
return true;
} else {
return !moreOperations.some(function (operation) {
return operation.icon == null;
});
}
},
renderPopoverAsMenu: function (moreOperations, rowModel) {
var hasIcon = moreOperations.some(function (operation) {
return operation.icon != null;
});
var _this = this;
var renderOperation = function (operation, operationIndex) {
var layout = $pt.createCellLayout('rowButton', {
label: operation.tooltip,
comp: {
style: 'link',
icon: hasIcon ? operation.icon ? operation.icon : 'placeholder' : null,
enabled: operation.enabled,
click: _this.onRowOperationClicked.bind(_this, operation.click, rowModel)
},
css: {
comp: 'n-table-op-btn'
}
});
return React.createElement(
'li',
{ key: operationIndex },
React.createElement($pt.Components.NFormButton, { model: rowModel, layout: layout })
);
};
return React.createElement(
'ul',
{ className: 'nav' },
moreOperations.map(renderOperation)
);
},
renderPopoverAsIcon: function (moreOperations, rowModel) {
var _this = this;
return moreOperations.map(function (operation, operationIndex) {
return _this.renderRowOperationButton(operation, rowModel, operationIndex);
});
},
renderPopover: function (moreOperations, rowModel, eventTarget) {
var styles = { display: 'block' };
var target = $(eventTarget).closest('a');
var offset = target.offset();
styles.top = offset.top + target.outerHeight() - 5;
styles.left = offset.left;
//var _this = this;
ReactDOM.render(React.createElement(
'div',
{ role: 'tooltip', className: 'n-table-op-btn-popover popover bottom in', style: styles },
React.createElement('div', { className: 'arrow' }),
React.createElement(
'div',
{ className: 'popover-content' },
this.isRenderMoreOperationButtonsAsIcon(moreOperations) ? this.renderPopoverAsIcon(moreOperations, rowModel) : this.renderPopoverAsMenu(moreOperations, rowModel)
)
), this.state.popoverDiv.get(0));
},
showPopover: function (moreOperations, rowModel, eventTarget) {
this.renderPopoverContainer();
this.renderPopover(moreOperations, rowModel, eventTarget);
this.state.popoverDiv.show();
// reset position
var styles = {};
var target = $(eventTarget).closest('a');
var offset = target.offset();
var popover = this.state.popoverDiv.children('.popover');
var popWidth = popover.outerWidth();
styles.left = offset.left + target.outerWidth() - popWidth + 10;
popover.css(styles);
},
hidePopover: function () {
if (this.state.popoverDiv && this.state.popoverDiv.is(':visible')) {
this.state.popoverDiv.hide();
ReactDOM.render(React.createElement('noscript', null), this.state.popoverDiv.get(0));
}
},
destroyPopover: function () {
if (this.state.popoverDiv) {
$(document).off('click', this.onDocumentClicked).off('keyup', this.onDocumentKeyUp);
this.state.popoverDiv.remove();
delete this.state.popoverDiv;
}
},
onDocumentClicked: function (evt) {
var target = $(evt.target);
if (target.closest(this.state.popoverDiv).length != 0) {
// click in popover
} else if (target.closest($('.n-table-op-btn.more')).length != 0) {
// click in more button
if (target.closest($(this.refs.div)).length == 0) {
// in other table's more button
this.hidePopover();
}
} else {
// neither popover nor more button
this.hidePopover();
}
},
onDocumentKeyUp: function (evt) {
if (evt.keyCode === 27) {
this.hidePopover();
}
},
onRowOperationMoreClicked: function (moreOperations, rowModel, eventTarget) {
this.showPopover(moreOperations, rowModel, eventTarget);
},
/**
* render more operations buttons
*/
renderRowOperationMoreButton: function (moreOperations, rowModel) {
var layout = $pt.createCellLayout('rowButton', {
comp: {
style: 'link',
icon: NTable.ROW_MORE_BUTTON_ICON,
click: this.onRowOperationMoreClicked.bind(this, moreOperations),
tooltip: NTable.TOOLTIP_MORE
},
css: {
comp: 'n-table-op-btn more'
}
});
return React.createElement($pt.Components.NFormButton, { model: rowModel, layout: layout, key: 'more-op' });
},
/**
* render dropdown operation cell, only buttons which before maxButtonCount are renderred as a line,
* a dropdown button is renderred in last, other buttons are renderred in popover of dropdown button.
*/
renderDropDownOperationCell: function (column, rowModel, maxButtonCount) {
var _this = this;
var rowOperations = this.getRowOperations(column);
if (column.editable) {
rowOperations.push({ editButton: true });
}
if (column.removable) {
rowOperations.push({ removeButton: true });
}
// filter invisible operations, will not monitor the attributes in depends property
rowOperations = rowOperations.filter(function (rowOperation) {
return _this.isRowOperationVisible(rowOperation, rowModel);
});
var used = -1;
var buttons = [];
rowOperations.some(function (operation, operationIndex) {
if (operation.editButton) {
buttons.push(_this.renderRowEditButton(rowModel));
} else if (operation.removeButton) {
buttons.push(_this.renderRowRemoveButton(rowModel));
} else {
buttons.push(_this.renderRowOperationButton(operation, rowModel, operationIndex));
}
used++;
return maxButtonCount - used == 1;
});
var hasDropdown = rowOperations.length - used > 1;
var dropdown = null;
if (hasDropdown) {
buttons.push(this.renderRowOperationMoreButton(rowOperations.slice(used + 1), rowModel));
}
return React.createElement(
'div',
{ className: 'btn-group n-table-op-btn-group', role: 'group' },
buttons,
dropdown
);
},
/**
* render operation cell
* @param column
* @param rowModel {ModelInterface} row model
* @returns {XML}
*/
renderOperationCell: function (column, rowModel) {
var needPopover = false;
var maxButtonCount = this.getComponentOption('maxOperationButtonCount');
if (maxButtonCount) {
var actualButtonCount = (column.editable ? 1 : 0) + (column.removable ? 1 : 0) + column.rowOperations.length;
if (actualButtonCount > maxButtonCount) {
needPopover = true;
}
}
if (!needPopover) {
return this.renderFlatOperationCell(column, rowModel);
} else {
return this.renderDropDownOperationCell(column, rowModel, maxButtonCount);
}
},
/**
* render row select cell
* @param column
* @param model
* @returns {XML}
*/
renderRowSelectCell: function (column, model) {
var _this = this;
model.addListener(column.rowSelectable, 'post', 'change', function (evt) {
_this.forceUpdate();
});
var layout = $pt.createCellLayout(column.rowSelectable, {
comp: {
type: $pt.ComponentConstants.Check
}
});
return React.createElement($pt.Components.NCheck, { model: model, layout: layout });
},
/**
* render table body rows
* @param row {*} data of row, json object
* @param rowIndex {number}
* @param all {boolean}
* @param leftFixed {boolean}
* @param rightFixed {boolean}
* @returns {XML}
*/
renderTableBodyRow: function (row, rowIndex, all, leftFixed, rightFixed) {
var indexToRender = this.getRenderColumnIndexRange(all, leftFixed, rightFixed);
var columnIndex = 0;
var _this = this;
var className = rowIndex % 2 == 0 ? "even" : "odd";
if (this.getModel().hasError(this.getDataId())) {
var rowError = null;
var errors = this.getModel().getError(this.getDataId());
for (var index = 0, count = errors.length; index < count; index++) {
if (typeof errors[index] !== "string") {
rowError = errors[index].getError(row);
}
}
if (rowError != null) {
className += " has-error";
}
}
var inlineModel = this.createRowModel(row, true);
this.addRowListener(inlineModel);
return React.createElement(
'tr',
{ className: className, key: rowIndex },
this.state.columns.map(function (column) {
if (columnIndex >= indexToRender.min && columnIndex <= indexToRender.max) {
// column is fixed.
columnIndex++;
var style = {
width: column.width
};
if (column.styles) {
Object.keys(column.styles).forEach(function (key) {
style[key] = column.styles[key];
});
}
if (!(column.visible === undefined || column.visible === true)) {
style.display = "none";
}
var data;
if (column.editable || column.removable || column.rowOperations != null) {
// operation column
data = _this.renderOperationCell(column, inlineModel);
style.textAlign = "left";
} else if (column.indexable) {
// index column
data = rowIndex;
} else if (column.rowSelectable) {
data = _this.renderRowSelectCell(column, inlineModel);
} else if (column.inline) {
// inline editor or something, can be pre-defined or just declare as be constructed as a form layout
if (typeof column.inline === 'string') {
var layout = NTable.getInlineEditor(column.inline);
layout.pos = { width: 12 };
if (layout.css) {
layout.css.cell = 'inline-editor' + layout.css.cell ? ' ' + layout.css.cell : '';
} else {
layout.css = { cell: 'inline-editor' };
}
layout.label = column.title;
if (column.inline === 'select' || column.inline === 'radio') {
// set code table
// if (column.codes) {
layout = $.extend(true, {}, { comp: { data: column.codes } }, layout);
// }
} else {
layout = $.extend(true, {}, layout);
}
// pre-defined, use with data together
data = React.createElement($pt.Components.NFormCell, { model: inlineModel,
layout: $pt.createCellLayout(column.data, layout),
direction: 'horizontal',
view: _this.isViewMode() });
} else if (column.inline.inlineType == 'cell') {
column.inline.pos = { width: 12 };
if (column.inline.css) {
column.inline.css.cell = 'inline-editor' + (column.inline.css.cell ? ' ' + column.inline.css.cell : '');
} else {
column.inline.css = { cell: 'inline-editor' };
}
column.inline.label = column.inline.label ? column.inline.label : column.title;
data = React.createElement($pt.Components.NFormCell, { model: inlineModel,
layout: $pt.createCellLayout(column.data, column.inline),
direction: 'horizontal',
view: _this.isViewMode(),
className: column.inline.__className });
} else {
// any other, treat as form layout
// column.data is not necessary
data = React.createElement($pt.Components.NForm, { model: inlineModel,
layout: $pt.createFormLayout(column.inline),
direction: 'horizontal',
view: _this.isViewMode() });
}
} else {
// data is property name
data = _this.getDisplayTextOfColumn(column, row);
}
return React.createElement(
'td',
{ style: style, key: columnIndex, className: column.css },
data
);
} else {
columnIndex++;
}
})
);
},
/**
* render table body
* at least and only one parameter can be true.
* if more than one parameter is true, priority as all > leftFixed > rightFixed
* @param all
* @param leftFixed
* @param rightFixed
* @returns {XML}
*/
renderTableBody: function (all, leftFixed, rightFixed) {
var data = this.getDataToDisplay();
if (data == null || data.length == 0) {
// no data
return null;
}
var rowIndex = 1;
var _this = this;
var range = this.computePagination(data);
return React.createElement(
'tbody',
null,
data.map(function (element) {
if (rowIndex >= range.min && rowIndex <= range.max) {
return _this.renderTableBodyRow(element, rowIndex++, all, leftFixed, rightFixed);
} else {
rowIndex++;
return null;
}
})
);
},
/**
* render table with no scroll Y
* @returns {XML}
*/
renderTableNoScrollY: function () {
return React.createElement(
'div',
{ className: this.getAdditionalCSS("panelBody", "n-table-panel-body") },
React.createElement(
'table',
{ cellSpacing: '0', className: this.getAdditionalCSS("table", "n-table cell-border"),
style: this.computeTableStyle(),
ref: 'table' },
this.renderTableHeading(true),
this.renderTableBody(true)
)
);
},
/**
* render table with scroll Y
* @returns {XML}
*/
renderTableScrollY: function () {
var style = this.computeTableStyle();
var scrolledHeaderDivStyle = {
overflowY: "scroll"
};
var scrolledBodyDivStyle = {
maxHeight: this.getComponentOption("scrollY"),
overflowY: "scroll"
};
return React.createElement(
'div',
{ className: this.getAdditionalCSS("panelBody", "n-table-panel-body") },
React.createElement(
'div',
{ className: 'n-table-scroll-head', ref: this.getScrolledHeaderDivId(), style: scrolledHeaderDivStyle },
React.createElement(
'div',
{ className: 'n-table-scroll-head-inner', style: style },
React.createElement(
'table',
{ cellSpacing: '0', className: this.getAdditionalCSS("table", "n-table cell-border"),
style: style },
this.renderTableHeading(true)
)
)
),
React.createElement(
'div',
{ className: 'n-table-scroll-body', style: scrolledBodyDivStyle, ref: this.getScrolledBodyDivId() },
React.createElement(
'table',
{ cellSpacing: '0', className: this.getAdditionalCSS("table", "n-table cell-border"),
style: style,
ref: 'table' },
this.renderTableBody(true)
)
)
);
},
/**
* render table
* @returns {XML}
*/
renderTable: function () {
if (this.hasVerticalScrollBar() && this.hasDataToDisplay()) {
return this.renderTableScrollY();
} else {
return this.renderTableNoScrollY();
}
},
/**
* render fixed left columns with scroll Y
* @returns {XML}
*/
renderFixedLeftColumnsScrollY: function () {
var divStyle = {
width: this.computeFixedLeftColumnsWidth()
};
var bodyDivStyle = {
width: "100%",
overflow: "hidden"
};
if (this.hasHorizontalScrollBar()) {
// for IE8 box model
bodyDivStyle.maxHeight = this.getComponentOption("scrollY") - (this.isIE8() ? 0 : 18);
}
var tableStyle = {
width: "100%"
};
return React.createElement(
'div',
{ className: 'n-table-fix-left', style: divStyle },
React.createElement(
'table',
{ cellSpacing: '0', style: tableStyle,
className: this.getAdditionalCSS("table", "n-table cell-border") },
this.renderTableHeading(false, true)
),
React.createElement(
'div',
{ ref: this.getFixedLeftBodyDivId(), style: bodyDivStyle },
React.createElement(
'table',
{ cellSpacing: '0', className: this.getAdditionalCSS("table", "n-table cell-border"),
style: tableStyle },
this.renderTableBody(false, true)
)
)
);
},
/**
* render fixed left columns with no scroll Y
* @returns {XML}
*/
renderFixedLeftColumnsNoScrollY: function () {
var divStyle = {
width: this.computeFixedLeftColumnsWidth()
};
var tableStyle = {
width: "100%"
};
return React.createElement(
'div',
{ className: 'n-table-fix-left', style: divStyle },
React.createElement(
'table',
{ cellSpacing: '0', className: this.getAdditionalCSS("table", "n-table cell-border"),
style: tableStyle },
this.renderTableHeading(false, true),
this.renderTableBody(false, true)
)
);
},
/**
* render fixed left columns
* @returns {XML}
*/
renderFixedLeftColumns: function () {
if (!this.hasFixedLeftColumns() && this.hasDataToDisplay()) {
return null;
}
if (this.hasVerticalScrollBar()) {
return this.renderFixedLeftColumnsScrollY();
} else {
return this.renderFixedLeftColumnsNoScrollY();
}
},
/**
* render fixed right columns with no scroll Y
* @returns {XML}
*/
renderFixedRightColumnsNoScrollY: function () {
var divStyle = {
width: this.computeFixedRightColumnsWidth()
};
var tableStyle = {
width: "100%"
};
return React.createElement(
'div',
{ className: 'n-table-fix-right', style: divStyle },
React.createElement(
'table',
{ cellSpacing: '0', className: this.getAdditionalCSS("table", "n-table cell-border"),
style: tableStyle },
this.renderTableHeading(false, false, true),
this.renderTableBody(false, false, true)
)
);
},
/**
* render fixed right columns with scroll Y
* @returns {XML}
*/
renderFixedRightColumnsScrollY: function () {
var divStyle = {
width: this.computeFixedRightColumnsWidth(),
right: "16px"
};
var bodyDivStyle = {
width: "100%",
overflow: "hidden"
};
if (this.hasHorizontalScrollBar()) {
// ie8 box mode, scrollbar is not in height.
// ie>8 or chrome, scrollbar is in height.
bodyDivStyle.maxHeight = this.getComponentOption("scrollY") - (this.isIE8() ? 0 : 18);
}
var tableStyle = {
width: "100%"
};
return React.createElement(
'div',
{ className: 'n-table-fix-right', style: divStyle },
React.createElement(
'div',
{ className: 'n-table-fix-right-head-wrapper' },
React.createElement('div', { className: 'n-table-fix-right-top-corner' }),
React.createElement(
'table',
{ cellSpacing: '0', style: tableStyle,
className: this.getAdditionalCSS("table", "n-table cell-border") },
this.renderTableHeading(false, false, true)
)
),
React.createElement(
'div',
{ ref: this.getFixedRightBodyDivId(), style: bodyDivStyle },
React.createElement(
'table',
{ cellSpacing: '0', className: this.getAdditionalCSS("table", "n-table cell-border") },
this.renderTableBody(false, false, true)
)
)
);
},
/**
* render fixed right columns
* @returns {XML}
*/
renderFixedRightColumns: function () {
if (!this.hasFixedRightColumns() && this.hasDataToDisplay()) {
return null;
}
if (this.hasVerticalScrollBar()) {
return this.renderFixedRightColumnsScrollY();
} else {
return this.renderFixedRightColumnsNoScrollY();
}
},
/**
* render not data reminder label
* @returns {XML}
*/
renderNoDataReminder: function () {
if (this.hasDataToDisplay()) {
return null;
} else {
return React.createElement(
'div',
{ className: 'n-table-no-data' },
React.createElement(
'span',
null,
NTable.NO_DATA_LABEL
)
);
}
},
/**
* render pagination
* @returns {XML}
*/
renderPagination: function () {
if (this.isPageable() && this.hasDataToDisplay()) {
// only show when pageable and has data to display
return React.createElement($pt.Components.NPagination, { className: 'n-table-pagination', pageCount: this.state.pageCount,
currentPageIndex: this.state.currentPageIndex,
toPage: this.toPage });
} else {
return null;
}
},
renderRightTopCorner: function () {
var rightCorner = null;
if (this.hasVerticalScrollBar() && !this.hasFixedRightColumns()) {
var divStyle = {
width: '16px',
right: "16px"
};
var bodyDivStyle = {
width: "100%",
overflow: "hidden"
};
if (this.hasHorizontalScrollBar()) {
// ie8 box mode, scrollbar is not in height.
// ie>8 or chrome, scrollbar is in height.
bodyDivStyle.maxHeight = this.getComponentOption("scrollY") - (this.isIE8() ? 0 : 18);
}
rightCorner = React.createElement(
'div',
{ className: 'n-table-fix-right', style: divStyle },
React.createElement(
'div',
{ className: 'n-table-fix-right-head-wrapper' },
React.createElement('div', { className: 'n-table-fix-right-top-corner' })
)
);
}
return rightCorner;
},
/**
* render
* @returns {XML}
*/
render: function () {
this.prepareDisplayOptions();
var css = {
'n-table-container panel': true
};
var style = this.getComponentOption('style');
if (style) {
css['panel-' + style] = true;
} else {
css['panel-default'] = true;
}
if (!this.isHeading()) {
css['n-table-no-header'] = true;
}
var expandedStyle = {
display: this.isExpanded() ? 'block' : 'none'
};
return React.createElement(
'div',
{ className: this.getComponentCSS($pt.LayoutHelper.classSet(css)), ref: 'div' },
this.renderPanelHeading(),
React.createElement(
'div',
{ ref: 'table-panel-body', style: expandedStyle },
React.createElement(
'div',
{ className: this.getAdditionalCSS("body", "n-table-body-container panel-body") },
this.renderTable(),
this.renderFixedLeftColumns(),
this.renderFixedRightColumns(),
this.renderRightTopCorner()
),
this.renderNoDataReminder(),
this.renderPagination()
)
);
},
/**
* has vertical scroll bar
* @returns {boolean}
*/
hasVerticalScrollBar: function () {
var scrollY = this.getComponentOption("scrollY");
return scrollY !== false;
},
/**
* has horizontal scroll bar
* @returns {boolean}
*/
hasHorizontalScrollBar: function () {
var hasVerticalBar = this.hasVerticalScrollBar();
if (hasVerticalBar) {
// if scrollY is set, force set scrollX to true, since the table will be
// splitted to head table and body table.
// for make sure the cell is aligned, width of columns must be set.
return true;
}
var scrollX = this.getComponentOption("scrollX");
return scrollX === true;
},
/**
* compute table style
* @returns {{width: number, maxWidth: number}}
*/
computeTableStyle: function () {
var width = 0;
if (this.hasHorizontalScrollBar()) {
width = 0;
// calculate width
this.state.columns.forEach(function (column) {
if (column.visible === undefined || column.visible === true) {
width += column.width ? column.width * 1 : 0;
}
});
} else {
width = "100%";
}
return {
width: width,
maxWidth: width
};
},
/**
* compute fixed left columns width
* @returns {number}
*/
computeFixedLeftColumnsWidth: function () {
var width = 0;
var fixedLeftColumns = this.getMaxFixedLeftColumnIndex();
var columnIndex = 0;
this.state.columns.forEach(function (element) {
if (columnIndex <= fixedLeftColumns && (element.visible === undefined || element.visible === true)) {
// column is fixed.
width += element.width ? element.width * 1 : 0;
}
columnIndex++;
});
return width + 1;
},
/**
* compute fixed right columns width
* @returns {number}
*/
computeFixedRightColumnsWidth: function () {
var width = 0;
var fixedRightColumns = this.getMinFixedRightColumnIndex();
var columnIndex = 0;
this.state.columns.forEach(function (element) {
if (columnIndex >= fixedRightColumns && (element.visible === undefined || element.visible === true)) {
// column is fixed
width += element.width;
}
columnIndex++;
});
return width + (this.isFirefox() ? 3 : 1);
},
/**
* get column index range for rendering
* at least and only one parameter can be true.
* if more than one parameter is true, priority as all > leftFixed > rightFixed
* @param all
* @param leftFixed
* @param rightFixed
* @returns {{min, max}}
*/
getRenderColumnIndexRange: function (all, leftFixed, rightFixed) {
var index = {};
if (all) {
index.min = 0;
index.max = 10000;
} else if (leftFixed) {
index.min = 0;
index.max = this.getMaxFixedLeftColumnIndex();
} else if (rightFixed) {
index.min = this.getMinFixedRightColumnIndex();
index.max = 10000;
}
return index;
},
/**
* get max fixed left column index. if no column is fixed in left, return -1
* @returns {number}
*/
getMaxFixedLeftColumnIndex: function () {
return this.fixedLeftColumns - 1;
},
/**
* get min fixed right column index. if no column is fixed in right, return
* max column index.
* eg. there are 3 columns in table, if no fixed right column, return 3. if
* 1 fixed right column, return 2.
* @returns {number}
*/
getMinFixedRightColumnIndex: function () {
return this.state.columns.length() - this.fixedRightColumns;
},
/**
* get query settings
* @returns {*}
*/
getQuerySettings: function () {
return this.getComponentOption("criteria");
},
/**
* compute pagination
* @param data array of data
* @returns {{min, max}}
*/
computePagination: function (data) {
var minRowIndex = 0;
var maxRowIndex = 999999;
if (this.isPageable()) {
var queryCriteria = this.getQuerySettings();
if (queryCriteria === null) {
// no query criteria
this.state.countPerPage = this.getComponentOption("countPerPage");
var pageCount = data.length == 0 ? 1 : data.length / this.state.countPerPage;
this.state.pageCount = Math.floor(pageCount) == pageCount ? pageCount : Math.floor(pageCount) + 1;
this.state.currentPageIndex = this.state.currentPageIndex > this.state.pageCount ? this.state.pageCount : this.state.currentPageIndex;
this.state.currentPageIndex = this.state.currentPageIndex <= 0 ? 1 : this.state.currentPageIndex;
minRowIndex = (this.state.currentPageIndex - 1) * this.state.countPerPage + 1;
maxRowIndex = minRowIndex + this.state.countPerPage - 1;
} else {
var criteria = this.getModel().get(queryCriteria);
this.state.countPerPage = criteria.countPerPage;
this.state.pageCount = criteria.pageCount;
this.state.currentPageIndex = criteria.pageIndex;
minRowIndex = 1;
maxRowIndex = this.state.countPerPage;
}
}
return {
min: minRowIndex,
max: maxRowIndex
};
},
/**
* has fixed left columns
* @returns {boolean}
*/
hasFixedLeftColumns: function () {
return this.fixedLeftColumns > 0;
},
/**
* has fixed right columns or not
* @returns {boolean}
*/
hasFixedRightColumns: function () {
return this.fixedRightColumns > 0;
},
/**
* check the table is addable or not
* @returns {boolean}
*/
isAddable: function () {
return this.getComponentOption("addable") && !this.isViewMode();
},
/**
* check the table is editable or not
* @returns {boolean}
*/
isEditable: function () {
return this.getComponentOption("editable");
},
getRowEditButtonEnabled: function () {
return this.getComponentOption('rowEditEnabled');
},
/**
* check the table is removable or not
* @returns {boolean}
*/
isRemovable: function () {
return this.getComponentOption("removable") && !this.isViewMode();
},
getRowRemoveButtonEnabled: function () {
return this.getComponentOption('rowRemoveEnabled');
},
isDownloadable: function () {
var downloadable = this.getComponentOption('downloadable');
if (downloadable != null) {
return downloadable;
} else {
return NTable.DOWNLOADABLE;
}
},
/**
* check the table is searchable or not
* @returns {boolean}
*/
isSearchable: function () {
return this.getComponentOption("searchable");
},
/**
* check the table is indexable or not
* @returns {boolean}
*/
isIndexable: function () {
return this.getComponentOption("indexable");
},
/**
* check the row can be selectable or not
* @returns {boolean}
*/
isRowSelectable: function () {
if (this.isViewMode()) {
return false;
}
return this.getComponentOption('rowSelectable');
},
/**
* check the table is pageable or not
* @returns {boolean}
*/
isPageable: function () {
return this.getComponentOption("pageable");
},
/**
* check the table heading is displayed or not
* @returns {*}
*/
isHeading: function () {
return this.getComponentOption('header');
},
isCollapsible: function () {
return this.getComponentOption('collapsible');
},
isExpanded: function () {
if (this.state.expanded === undefined) {
this.state.expanded = this.getComponentOption('expanded');
}
return this.state.expanded;
},
/**
* check the column is sortable or not
* @param column if no passed, check the table
* @returns {boolean}
*/
isSortable: function (column) {
if (column === undefined) {
return this.getComponentOption("sortable");
} else if (this.isSortable()) {
// check the table option
if (column.sort === false) {
// if column is defined as not sortable, return false
return false;
} else {
return !(column.editable || column.removable || column.indexable || column.rowSelectable || column.rowOperations != null);
}
} else {
// even table is not sortable, the special column can be sortable
return column.sort;
}
},
/**
* get sorter
* @returns {function}
*/
getSorter: function () {
return this.getComponentOption('sorter');
},
/**
* check has data to display or not
* @returns {boolean}
*/
hasDataToDisplay: function () {
var data = this.getDataToDisplay();
return data != null && data.length > 0;
},
/**
* get display text of given column configuration
* @param column column configuration
* @param data row data
* @return display text
*/
getDisplayTextOfColumn: function (column, data) {
var text = null;
if (column.render) {
text = column.render(data);
} else if (column.codes) {
text = column.codes.getText($pt.getValueFromJSON(data, column.data));
} else {
text = $pt.getValueFromJSON(data, column.data);
}
if (typeof text === "boolean") {
if (text === true) {
return NTable.BOOLEAN_TRUE_DISPLAY_TEXT;
} else if (text === false) {
return NTable.BOOLEAN_FALSE_DISPLAY_TEXT;
}
}
return text == null ? null : text.toString();
},
/**
* filter data to display
* @param row
* @param rowIndex
* @param all
* @returns {boolean} true means data of row can match the search text
*/
filterData: function (row, rowIndex, all) {
var text = this.state.searchText.toUpperCase();
// do not use this.column, it maybe add index or operation columns
var columns = this.getComponentOption("columns");
var _this = this;
return columns.some(function (column) {
var data = _this.getDisplayTextOfColumn(column, row);
if (data == null) {
return false;
}
return data.toString().toUpperCase().indexOf(text) != -1;
});
},
/**
* get data to display
* @returns {[*]}
*/
getDataToDisplay: function () {
var data = this.getValueFromModel();
if (data == null) {
return data;
}
return this.isSearching() ? data.filter(this.filterData) : data;
},
/**
* is searching
* @returns {boolean}
*/
isSearching: function () {
return this.isSearchable() && this.state.searchText != null && this.state.searchText.length != 0;
},
/**
* is current page all selected
* @param column {{rowSelectable: string}}
* @param data {{}[]}
* @param range {{min: number, max: number}}
* @returns {boolean}
*/
isCurrentPageAllSelected: function (column, data, range) {
var rowIndex = 1;
return data == null ? false : !data.some(function (row) {
if (rowIndex >= range.min && rowIndex <= range.max) {
rowIndex++;
return row[column.rowSelectable] !== true;
} else {
rowIndex++;
return false;
}
});
},
onTitleClicked: function () {
this.state.expanded = !this.state.expanded;
if (this.state.expanded) {
$(ReactDOM.findDOMNode(this.refs['table-panel-body'])).slideDown(300);
$(ReactDOM.findDOMNode(this.refs['add-button'])).show();
} else {
$(ReactDOM.findDOMNode(this.refs['table-panel-body'])).slideUp(300);
$(ReactDOM.findDOMNode(this.refs['add-button'])).hide();
}
},
onCheckAllChanged: function (selected) {
var monitor = this.getEventMonitor('checkAll');
if (monitor) {
monitor.call(this, selected);
}
},
/**
* on add button clicked
*/
onAddClicked: function () {
var data = $pt.cloneJSON(this.getComponentOption("modelTemplate"));
var itemModel = this.createEditingModel(data);
var layout = this.getComponentOption("editLayout");
var addClick = this.getComponentOption('addClick');
if (addClick) {
addClick.call(this, this.getModel(), itemModel, layout);
} else {
this.getEditDialog().show({
model: itemModel,
layout: $pt.createFormLayout(layout),
buttons: {
right: [{
icon: NTable.EDIT_DIALOG_SAVE_BUTTON_ICON,
text: NTable.EDIT_DIALOG_SAVE_BUTTON_TEXT,
style: "primary",
click: this.onAddCompleted
}],
reset: this.getComponentOption('dialogResetVisible'),
validate: this.getComponentOption('dialogValidateVisible')
}
});
}
},
/**
* on add completed
*/
onAddCompleted: function () {
var hasError = false;
var editDialog = this.getEditDialog();
if (this.getComponentOption('onAddSave')) {
hasError = this.getComponentOption('onAddSave').call(this, editDialog.getModel(), editDialog) === false;
} else {
hasError = editDialog.validate();
}
if (!hasError) {
var data = editDialog.hide();
this.getModel().add(this.getDataId(), data.getCurrentModel());
}
},
/**
* on edit button clicked
* @param data {*} data of row
*/
onEditClicked: function (data) {
var itemModel = this.createEditingModel(data);
var layout = this.getComponentOption("editLayout");
var editClick = this.getComponentOption('editClick');
if (editClick) {
editClick.call(this, this.getModel(), itemModel, layout);
} else {
this.getEditDialog().show({
model: itemModel,
layout: $pt.createFormLayout(layout),
buttons: {
right: [{
icon: NTable.EDIT_DIALOG_SAVE_BUTTON_ICON,
text: NTable.EDIT_DIALOG_SAVE_BUTTON_TEXT,
style: "primary",
click: this.onEditCompleted,
// show save when editing
view: 'edit'
}],
reset: this.getComponentOption('dialogResetVisible'),
validate: this.getComponentOption('dialogValidateVisible'),
// use default cancel behavior when editing
// simply hide dialog when in view mode
cancel: this.isViewMode() ? function (model, hide) {
hide();
} : true
},
view: this.isViewMode()
});
}
},
/**
* on edit completed
*/
onEditCompleted: function () {
var hasError = false;
var editDialog = this.getEditDialog();
if (this.getComponentOption('onEditSave')) {
hasError = this.getComponentOption('onEditSave').call(this, editDialog.getModel(), editDialog) === false;
} else {
hasError = editDialog.validate();
}
if (!hasError) {
var data = this.getEditDialog().hide();
var original = data.getOriginalModel();
var current = data.getCurrentModel();
this.getModel().update(this.getDataId(), original, current);
}
},
/**
* on remove button clicked
* @param data {*} data of row
*/
onRemoveClicked: function (data) {
var removeRow = function (data) {
var canRemove = this.getComponentOption('canRemove');
if (!canRemove || canRemove.call(this, this.getModel(), data)) {
this.getModel().remove(this.getDataId(), data);
}
$pt.Components.NConfirm.getConfirmModal().hide();
};
$pt.Components.NConfirm.getConfirmModal().show(NTable.REMOVE_CONFIRM_TITLE, NTable.REMOVE_CONFIRM_MESSAGE, removeRow.bind(this, data));
},
/**
* on row user defined operation clicked
* @param callback
* @param rowModel
*/
onRowOperationClicked: function (callback, rowModel) {
callback.call(this, rowModel.getCurrentModel(), rowModel);
},
/**
* on download clicked
*/
onDownloadClicked: function () {
var data = null;
var queryCriteria = this.getQuerySettings();
if (queryCriteria === null) {
// no query criteria, all data is on local
data = this.getValueFromModel();
this.exposeDownloading(data);
} else {
var model = this.getModel();
var criteria = model.get(queryCriteria);
criteria = $.extend({}, criteria);
var url = criteria.url;
delete criteria.url;
delete criteria.pageCount;
criteria.pageIndex = -1;
if (NTable.PAGE_JUMPING_PROXY) {
criteria = NTable.PAGE_JUMPING_PROXY.call(this, criteria);
}
var downloadListener = this.getEventMonitor('download');
if (downloadListener) {
this.notifyEvent({
type: 'download',
criteria: criteria,
target: this
});
} else {
var _this = this;
$pt.internalDoPost(url, criteria).done(function (data) {
if (typeof data === 'string') {
data = JSON.parse(data);
}
if (NTable.PAGE_JUMPING_PROXY_CALLBACK) {
data = NTable.PAGE_JUMPING_PROXY_CALLBACK.call(_this, data, _this.getDataId());
}
var rows = null;
if (data && data[_this.getDataId()]) {
rows = data[_this.getDataId()];
} else {
rows = [];
}
_this.exposeDownloading(rows);
});
}
// todo how to handle failure?
}
},
exposeDownloading: function (data) {
if (data == null || data.length == 0) {
NConfirm.getConfirmModal().show({
title: NTable.NO_DATA_DOWNLOAD_TITLE,
messages: NTable.NO_DATA_DOWNLOAD,
disableConfirm: true,
close: true
});
} else {
this.tableToExcel(data);
}
},
tableToExcel: function (data) {
//creating a temporary HTML link element (they support setting file names)
var a = document.createElement('a');
//getting data from our div that contains the HTML table
var dataType = 'data:application/vnd.ms-excel';
var tableHeaderHtml = this.generateTableExcelHeader();
var tableBodyHtml = '<tbody>' + data.map(this.generateTableExcelBodyRow).join('') + '</tbody>';
var tableHtml = '<table>' + tableHeaderHtml + tableBodyHtml + '</table>';
tableHtml = tableHtml.replace(/ /g, '%20');
a.href = dataType + ', ' + tableHtml;
//setting the file name
a.download = 'exported_data.xls';
//triggering the function
a.click();
},
generateTableExcelHeader: function () {
var columns = this.state.columns.map(function (column) {
if (!(column.visible === undefined || column.visible === true)) {
return '';
}
if (column.editable || column.removable || column.rowOperations != null) {
return '';
} else if (column.indexable) {
return '';
} else if (column.rowSelectable) {
return '';
} else {
return '<td>' + column.title + '</td>';
}
});
return '<thead><tr>' + columns.join('') + '</tr></thead>';
},
generateTableExcelBodyRow: function (row) {
var _this = this;
var columns = this.state.columns.map(function (column) {
if (!(column.visible === undefined || column.visible === true)) {
return '';
}
if (column.editable || column.removable || column.rowOperations != null) {
return '';
} else if (column.indexable) {
return '';
} else if (column.rowSelectable) {
return '';
} else {
// data is property name
var data = _this.getDisplayTextOfColumn(column, row);
return '<td>' + (data == null ? '' : data) + '</td>';
}
});
return '<tr>' + columns.join('') + '</tr>';
},
/**
* on search box changed
*/
onSearchBoxChanged: function () {
var value = this.state.searchModel.get('text');
// window.console.debug('Searching [text=' + value + '].');
if (value == null || value == "") {
this.setState({
searchText: null
});
} else {
this.setState({
searchText: value
});
}
},
/**
* on sort icon clicked
* @param column
*/
onSortClicked: function (column) {
var valueArray = this.getValueFromModel();
if (valueArray == null) {
return;
}
var sortWay = "asc";
if (this.state.sortColumn == column) {
// the column is sorted, so set sortWay as another
sortWay = this.state.sortWay == "asc" ? "desc" : "asc";
}
// if global sorter defined
var sorter = this.getSorter();
if (sorter) {
sorter.call(this, {
mode: sortWay,
column: column,
data: valueArray,
criteria: this.getQuerySettings()
});
this.setState({
sortColumn: column,
sortWay: sortWay
});
return;
}
var isNumberValue = false;
// specific sort
if (column.sort !== undefined && column.sort != null) {
if (typeof column.sort === "function") {
sorter = column.sort;
} else if (column.sort === "number") {
isNumberValue = true;
} else if (typeof column.sort === 'boolean') {
// do nothing, use default sorter
} else {
throw $pt.createComponentException($pt.ComponentConstants.Err_Unuspported_Column_Sort, "Column sort [" + column.sort + "] is not supported yet.");
}
}
var _this = this;
// if no sorter specific in column
sorter = sorter == null ? function (a, b) {
var v1 = _this.getDisplayTextOfColumn(column, a);
var v2 = _this.getDisplayTextOfColumn(column, b);
if (v1 == null) {
return v2 == null ? 0 : -1;
} else if (v2 == null) {
return 1;
} else {
if (isNumberValue) {
// parse to number value
v1 *= 1;
v2 *= 1;
}
if (v1 > v2) {
return 1;
} else if (v1 < v2) {
return -1;
} else {
return 0;
}
}
} : sorter;
if (sortWay == "asc") {
valueArray.sort(sorter);
} else {
valueArray.sort(function (a, b) {
return 0 - sorter(a, b);
});
}
this.setState({
sortColumn: column,
sortWay: sortWay
});
},
/**
* create edit dialog
* @returns {*}
*/
getEditDialog: function () {
if (this.state.editDialog === undefined || this.state.editDialog === null) {
this.state.editDialog = $pt.Components.NModalForm.createFormModal(this.getLayout().getLabel(this));
}
return this.state.editDialog;
},
/**
* create editing model
* @param item
*/
createEditingModel: function (item) {
// var modelValidator = this.getModel().getValidator();
// var tableValidator = modelValidator ? modelValidator.getConfig()[this.getDataId()] : null;
// var itemValidator = tableValidator ? $pt.createModelValidator(tableValidator.table) : null;
// var editModel = $pt.createModel(item, itemValidator);
// editModel.parent(this.getModel());
// return editModel;
return this.createRowModel(item, false);
},
/**
* on model change
* @param evt
*/
onModelChanged: function (evt) {
if (evt.type == "add") {
this.computePagination(this.getDataToDisplay());
this.state.currentPageIndex = this.state.pageCount;
} else if (evt.type == "remove") {
// do nothing
} else if (evt.type == "change") {
// do nothing
// window.console.log('Table[' + this.getDataId() + '] data changed.');
}
if (this.getModel().getValidator() != null) {
this.getModel().validate(this.getDataId());
} else {
this.forceUpdate();
}
},
/**
* jump to page by given page index
* @param pageIndex
*/
toPage: function (pageIndex) {
if (this.state.currentPageIndex == pageIndex) {
// do nothing
return;
}
var queryCriteria = this.getQuerySettings();
if (queryCriteria === null) {
// no query criteria
this.setState({
currentPageIndex: pageIndex
});
} else {
var _this = this;
var model = this.getModel();
var criteria = model.get(queryCriteria);
criteria = $.extend({}, criteria);
var url = criteria.url;
delete criteria.url;
delete criteria.pageCount;
criteria.pageIndex = pageIndex;
if (NTable.PAGE_JUMPING_PROXY) {
criteria = NTable.PAGE_JUMPING_PROXY.call(this, criteria);
}
var pageChangeListener = this.getEventMonitor('pageChange');
if (pageChangeListener) {
this.notifyEvent({
type: 'pageChange',
criteria: criteria,
target: this
});
} else {
$pt.internalDoPost(url, criteria).done(function (data) {
if (typeof data === 'string') {
data = JSON.parse(data);
}
if (NTable.PAGE_JUMPING_PROXY_CALLBACK) {
data = NTable.PAGE_JUMPING_PROXY_CALLBACK.call(_this, data, _this.getDataId());
}
model.mergeCurrentModel(data);
// refresh
_this.forceUpdate();
// _this.setState({
// currentPageIndex: pageIndex
// });
});
}
// todo how to handle failure?
}
},
getDivComponent: function () {
return $(ReactDOM.findDOMNode(this.refs.div));
},
getComponent: function () {
return $(ReactDOM.findDOMNode(this.refs.table));
},
/**
* get header label id
* @returns {string}
*/
getHeaderLabelId: function () {
return "n-table-header-label";
},
getScrollHeaderComponent: function () {
if (this.refs[this.getScrolledHeaderDivId()]) {
return $(ReactDOM.findDOMNode(this.refs[this.getScrolledHeaderDivId()]));
} else {
return $("#___");
}
},
/**
* get scrolled header div id
* @returns {string}
*/
getScrolledHeaderDivId: function () {
return "n-table-scrolled-head";
},
getScrollBodyComponent: function () {
if (this.refs[this.getScrolledBodyDivId()]) {
return $(ReactDOM.findDOMNode(this.refs[this.getScrolledBodyDivId()]));
} else {
return $("#___");
}
},
/**
* get scrolled body div id
* @returns {string}
*/
getScrolledBodyDivId: function () {
return "n-table-scrolled-body";
},
getFixedLeftBodyComponent: function () {
if (this.refs[this.getFixedLeftBodyDivId()]) {
return $(ReactDOM.findDOMNode(this.refs[this.getFixedLeftBodyDivId()]));
} else {
return $("#___");
}
},
/**
* get scrolled fixed left body div id
* @returns {string}
*/
getFixedLeftBodyDivId: function () {
return "n-table-scrolled-left-body";
},
getFixedRightBodyComponent: function () {
if (this.refs[this.getFixedRightBodyDivId()]) {
return $(ReactDOM.findDOMNode(this.refs[this.getFixedRightBodyDivId()]));
} else {
return $("#___");
}
},
/**
* get scrolled fixed right body div id
* @returns {string}
*/
getFixedRightBodyDivId: function () {
return "n-table-scrolled-right-body";
},
/**
* clear columns definition
*/
clearColumnsDefinition: function () {
this.state.columns = null;
this.forceUpdate();
}
}));
$pt.Components.NTable = NTable;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Table, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NTable, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NText = React.createClass($pt.defineCellComponent({
displayName: 'NText',
statics: {
NUMBER_FORMAT: function (value) {
var parts = (value + '').split('.');
var integral = parts[0];
var fraction = parts.length > 1 ? '.' + parts[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(integral)) {
integral = integral.replace(rgx, '$1' + ',' + '$2');
}
return integral + fraction;
},
PERCENTAGE: {
model: function (value) {
return isNaN(value) || (value + '').isBlank() ? value : (value + '').movePointLeft(2);
},
view: function (value) {
return isNaN(value) || (value + '').isBlank() ? value : (value + '').movePointRight(2);
}
},
TRIM: false,
DELAY: 300
},
getDefaultProps: function () {
return {
defaultOptions: {}
};
},
afterWillUpdate: function (nextProps) {
this.getComponent().off('change', this.onChange);
this.uninstallOtherDOMListeners();
},
/**
* did update
* @param prevProps
* @param prevState
*/
beforeDidUpdate: function (prevProps, prevState) {
var formattedValue = this.getValueFromModel();
if (!$(ReactDOM.findDOMNode(this.refs.focusLine)).hasClass('focus')) {
formattedValue = this.getFormattedValue(formattedValue);
}
if (this.getComponent().val() != formattedValue) {
this.getComponent().val(formattedValue);
}
},
afterDidUpdate: function () {
this.getComponent().on('change', this.onChange);
this.installOtherDOMListeners();
},
beforeDidMount: function () {
// set model value to component
this.getComponent().val(this.getFormattedValue(this.getValueFromModel()));
},
afterDidMount: function () {
this.getComponent().on('change', this.onChange);
this.installOtherDOMListeners();
},
afterWillUnmount: function () {
this.getComponent().off('change', this.onChange);
this.uninstallOtherDOMListeners();
},
installOtherDOMListeners: function () {
var listeners = this.getEventMonitor();
Object.keys(listeners).filter(function (name) {
return ['change', 'blur', 'focus', 'keyPress', 'keyUp'].indexOf(name) < 0;
}).forEach(function (name) {
this.getComponent().on(name, listeners[name].bind(this));
}.bind(this));
},
uninstallOtherDOMListeners: function () {
var listeners = this.getEventMonitor();
Object.keys(listeners).filter(function (name) {
return ['change', 'blur', 'focus', 'keyPress', 'keyUp'].indexOf(name) < 0;
}).forEach(function (name) {
this.getComponent().off(name);
}.bind(this));
},
/**
* render left add-on
* @returns {XML}
*/
renderLeftAddon: function () {
return this.renderAddon(this.getComponentOption('leftAddon'));
},
/**
* render text
* @returns {XML}
*/
renderText: function () {
// TODO needs to handle the control keys
var css = {
'form-control': true
};
var textType = 'text';
var isPassword = this.getComponentOption('pwd', false);
if (isPassword) {
textType = 'password';
}
var specialType = this.getComponentOption('textType');
if (specialType) {
textType = specialType;
}
return React.createElement('input', { type: textType,
className: $pt.LayoutHelper.classSet(css),
disabled: !this.isEnabled(),
placeholder: this.getComponentOption('placeholder'),
maxLength: this.getComponentOption('maxlength'),
onKeyPress: this.onKeyPress,
onChange: this.onChange,
onFocus: this.onComponentFocused,
onBlur: this.onComponentBlurred,
onKeyUp: this.onKeyUp,
ref: 'txt' });
},
/**
* render right add-on
* @returns {XML}
*/
renderRightAddon: function () {
return this.renderAddon(this.getComponentOption('rightAddon'));
},
/**
* render add-on
* @param addon {{
* icon: string,
* text: string,
* iconFirst: boolean,
* click: function(model: object, value: object)
* }}
* @returns {XML}
*/
renderAddon: function (addon) {
if (addon == null) {
return null;
}
var spanCss = {
'input-group-addon': true,
link: addon.click != null,
disabled: !this.isEnabled()
};
var iconCss = {
fa: true,
'fa-fw': true
};
var icon = addon.icon;
if (icon != null) {
iconCss['fa-' + icon] = true;
}
var iconPart = icon == null ? null : React.createElement('span', { className: $pt.LayoutHelper.classSet(iconCss), key: 'iconPart' });
var textPart = addon.text;
var innerParts = addon.iconFirst === false ? [textPart, iconPart] : [iconPart, textPart];
return React.createElement(
'span',
{ className: $pt.LayoutHelper.classSet(spanCss),
onClick: this.onAddonClicked.bind(this, addon.click) },
innerParts.map(function (part) {
return part;
})
);
},
/**
* render
* @returns {XML}
*/
render: function () {
if (this.isViewMode()) {
return this.renderInViewMode();
}
var css = {
'n-disabled': !this.isEnabled()
};
css[this.getComponentCSS('n-text')] = true;
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css) },
React.createElement(
'div',
{ className: 'input-group' },
this.renderLeftAddon(),
this.renderText(),
this.renderRightAddon()
),
this.renderNormalLine(),
this.renderFocusLine()
);
},
onComponentFocused: function (evt) {
$(ReactDOM.findDOMNode(this.refs.focusLine)).toggleClass('focus');
$(ReactDOM.findDOMNode(this.refs.normalLine)).toggleClass('focus');
var value = this.getValueFromModel();
if (value == this.getComponent().val()) {} else {
this.getComponent().val(value);
}
// window.console.log("focused: " + this.getValueFromModel());
this.notifyEvent(evt);
},
onComponentBlurred: function (evt) {
$(ReactDOM.findDOMNode(this.refs.focusLine)).toggleClass('focus');
$(ReactDOM.findDOMNode(this.refs.normalLine)).toggleClass('focus');
// if (this.state.componentChanged) {
// clearTimeout(this.state.componentChanged);
// }
var value = evt.target.value;
if (this.getComponentOption('trim', NText.TRIM)) {
value = value == null ? null : (value + '').trim();
}
if (value && !value.isBlank()) {
var formattedValue = this.getFormattedValue(value);
if (formattedValue != value) {
// window.console.debug('Change component display formatted value when onBlur.');
this.getComponent().val(formattedValue);
}
}
if (!this.textEquals(value, this.getValueFromModel())) {
this.setValueToModel(value);
}
this.notifyEvent(evt);
},
hasText: function (value) {
return value != null && !(value + '').isEmpty();
},
textEquals: function (v1, v2) {
var hasText1 = this.hasText(v1);
var hasText2 = this.hasText(v2);
if (hasText1) {
var strV1 = v1 + '';
var strV2 = v2 + '';
return strV1 === strV2;
} else {
return !hasText2;
}
//return hasText1 ? ((v1 + '') === (v2 + '')) : !hasText2;
},
textChanged: function (newValue) {
var oldValue = this.getValueFromModel();
if (!this.textEquals(newValue, oldValue)) {
this.setValueToModel(newValue);
}
},
onChange: function (evt) {
this.onComponentChanged(evt);
this.notifyEvent(evt);
},
/**
* on component change
* @param evt
*/
onComponentChanged: function (evt) {
// console.debug('Text component changed[modelValue=' + this.getValueFromModel() + ', compValue=' + evt.target.value + '].');
if (NText.DELAY === 0) {
this.textChanged(evt.target.value);
} else {
if (this.state.textChangeHandler) {
clearTimeout(this.state.textChangeHandler);
delete this.state.textChangeHandler;
}
var newValue = evt.target.value;
this.state.textChangeHandler = setTimeout(function () {
this.textChanged(newValue);
}.bind(this), NText.DELAY);
}
},
onKeyPress: function (evt) {
this.onComponentChanged(evt);
this.notifyEvent(evt);
},
onKeyUp: function (evt) {
this.notifyEvent(evt);
},
/**
* on addon clicked
* @param userDefinedClickFunc
*/
onAddonClicked: function (userDefinedClickFunc) {
if (this.isAddonClickable(userDefinedClickFunc)) {
userDefinedClickFunc.call(this, this.getModel(), this.getValueFromModel());
}
},
/**
* get component
* @returns {jQuery}
* @override
*/
getComponent: function () {
return $(ReactDOM.findDOMNode(this.refs.txt));
},
/**
* is add-on clickable
* @param userDefinedClickFunc
* @returns {*}
*/
isAddonClickable: function (userDefinedClickFunc) {
return this.isEnabled() && userDefinedClickFunc;
},
getTextFormat: function () {
return this.getComponentOption('format');
},
getFormattedValue: function (value) {
if (value) {
if (typeof value === 'number') {
value = value + '';
}
if (!value.isBlank()) {
var format = this.getTextFormat();
if (format) {
var formatValue = value;
if (format == 'currency') {
var fraction = this.getComponentOption('fraction');
fraction = fraction ? fraction * 1 : 0;
formatValue = value.currencyFormat(fraction);
} else {
formatValue = format.call(this, value);
}
return formatValue;
}
}
}
return value;
},
getTextConvertor: function () {
return this.getComponentOption('transformer') || this.getComponentOption('convertor');
},
getValueFromModel: function () {
var value = this.getModel().get(this.getDataId());
var convertor = this.getTextConvertor();
if (convertor && convertor.view) {
return convertor.view.call(this, value);
} else {
return value;
}
},
setValueToModel: function (value) {
var convertor = this.getTextConvertor();
if (convertor && convertor.model) {
this.getModel().set(this.getDataId(), convertor.model.call(this, value));
} else {
this.getModel().set(this.getDataId(), value);
}
},
getTextInViewMode: function () {
return this.getModel().get(this.getDataId());
}
}));
$pt.Components.NText = NText;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Text, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NText, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NTextArea = React.createClass($pt.defineCellComponent({
displayName: 'NTextArea',
getDefaultProps: function () {
return {
defaultOptions: {
lines: 1
}
};
},
afterWillUpdate: function (nextProps) {
this.getComponent().off('change', this.onComponentChanged);
},
beforeDidUpdate: function (prevProps, prevState) {
if (this.getComponent().val() != this.getValueFromModel()) {
this.getComponent().val(this.getValueFromModel());
}
},
afterDidUpdate: function () {
this.getComponent().on('change', this.onComponentChanged);
},
/**
* did mount
*/
beforeDidMount: function () {
// set model value to component
this.getComponent().val(this.getValueFromModel());
},
afterDidMount: function () {
this.getComponent().on('change', this.onComponentChanged);
},
/**
* will unmount
*/
afterWillUnmount: function () {
this.getComponent().off('change', this.onComponentChanged);
},
/**
* render text
* @returns {XML}
*/
renderText: function () {
var css = {
'form-control': true
};
css['l' + this.getComponentOption('lines')] = true;
return React.createElement('textarea', { className: $pt.LayoutHelper.classSet(css),
disabled: !this.isEnabled(),
placeholder: this.getComponentOption('placeholder'),
onKeyPress: this.onComponentChanged,
onChange: this.onComponentChanged,
onFocus: this.onComponentFocused,
onBlur: this.onComponentBlurred,
ref: 'txt' });
},
/**
* render
* @returns {XML}
*/
render: function () {
if (this.isViewMode()) {
return this.renderInViewMode();
}
var css = {
'n-disabled': !this.isEnabled()
};
css[this.getComponentCSS('n-textarea')] = true;
return React.createElement(
'div',
{ className: $pt.LayoutHelper.classSet(css) },
this.renderText(),
this.renderNormalLine(),
this.renderFocusLine()
);
},
onComponentFocused: function () {
$(ReactDOM.findDOMNode(this.refs.focusLine)).toggleClass('focus');
$(ReactDOM.findDOMNode(this.refs.normalLine)).toggleClass('focus');
},
onComponentBlurred: function () {
$(ReactDOM.findDOMNode(this.refs.focusLine)).toggleClass('focus');
$(ReactDOM.findDOMNode(this.refs.normalLine)).toggleClass('focus');
},
/**
* on component change
* @param evt
*/
onComponentChanged: function (evt) {
this.setValueToModel(evt.target.value);
},
/**
* on addon clicked
* @param userDefinedClickFunc
*/
onAddonClicked: function (userDefinedClickFunc) {
if (userDefinedClickFunc) {
userDefinedClickFunc.call(this, this.getModel(), this.getValueFromModel());
}
},
/**
* get component
* @returns {jQuery}
* @override
*/
getComponent: function () {
return $(ReactDOM.findDOMNode(this.refs.txt));
},
getTextInViewMode: function () {
var value = this.getValueFromModel();
if (value != null) {
value = value.split(/\r|\n/);
}
return value;
}
}));
$pt.Components.NTextArea = NTextArea;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.TextArea, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NTextArea, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
/**
* Created by brad.wu on 8/21/2015.
*/
(function (window, $, React, ReactDOM, $pt) {
var NToggle = React.createClass($pt.defineCellComponent({
displayName: 'NToggle',
beforeDidUpdate: function (prevProps, prevState) {
// set model value to component
this.getComponent().prop("checked", this.getValueFromModel());
},
beforeDidMount: function () {
// set model value to component
this.getComponent().prop("checked", this.getValueFromModel());
},
/**
* render label
* @returns {XML}
*/
renderLabel: function (label, className) {
var css = {
'toggle-label': true,
disabled: !this.isEnabled()
};
css[className] = true;
return React.createElement(
"span",
{ className: $pt.LayoutHelper.classSet(css) },
label
);
},
renderLeftLabel: function () {
var labelAttached = this.getComponentOption('labelAttached');
if (labelAttached && labelAttached.left) {
return this.renderLabel(labelAttached.left, 'toggle-label-left');
}
},
renderRightLabel: function () {
var labelAttached = this.getComponentOption('labelAttached');
if (labelAttached && labelAttached.right) {
return this.renderLabel(labelAttached.right, 'toggle-label-right');
}
},
/**
* render check box, using font awesome instead
* @returns {XML}
*/
renderToggleButton: function () {
var checked = this.isChecked();
var css = {
disabled: !this.isEnabled(),
checked: checked,
unchecked: !checked,
'toggle-container': true
};
return React.createElement(
"div",
{ className: $pt.LayoutHelper.classSet(css) },
React.createElement("span", { className: "n-toggle-line" }),
React.createElement("span", { className: "n-toggle-true",
tabIndex: this.isEnabled() ? '-1' : null,
onClick: this.onButtonClicked.bind(this, true) }),
React.createElement("span", { className: "n-toggle-false",
tabIndex: this.isEnabled() ? '-1' : null,
onClick: this.onButtonClicked.bind(this, false) })
);
},
/**
* render
* @returns {XML}
*/
render: function () {
var css = {
'n-disabled': !this.isEnabled(),
'n-view-mode': this.isViewMode()
};
css[this.getComponentCSS('n-toggle')] = true;
return React.createElement(
"div",
{ className: $pt.LayoutHelper.classSet(css) },
React.createElement("input", { type: "checkbox", style: { display: "none" },
onChange: this.onComponentChanged, ref: "txt" }),
this.renderLeftLabel(),
this.renderToggleButton(),
this.renderRightLabel()
);
},
/**
* handle button clicked event
*/
onButtonClicked: function (value) {
if (this.isEnabled() && !this.isViewMode()) {
this.setValueToModel(value);
}
},
/**
* on component change
* @param evt
*/
onComponentChanged: function (evt) {
// synchronize value to model
this.setValueToModel(evt.target.checked);
},
/**
* on model change
* @param evt
*/
onModelChanged: function (evt) {
this.getComponent().prop("checked", evt.new === true);
this.forceUpdate();
},
/**
* is checked or not
* @returns {boolean}
*/
isChecked: function () {
return this.getValueFromModel() === true;
},
/**
* get component
* @returns {jQuery}
*/
getComponent: function () {
return $(ReactDOM.findDOMNode(this.refs.txt));
}
}));
$pt.Components.NToggle = NToggle;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Toggle, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NToggle, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
(function (window, $, React, ReactDOM, $pt) {
var NTree = React.createClass($pt.defineCellComponent({
displayName: 'NTree',
statics: {
ROOT_LABEL: 'Root',
FOLDER_ICON: 'folder-o',
FOLDER_OPEN_ICON: 'folder-open-o',
FILE_ICON: 'file-o',
OP_FOLDER_LEAF_ICON: 'angle-right',
OP_FOLDER_ICON: 'angle-double-right',
OP_FOLDER_OPEN_ICON: 'angle-double-down',
OP_FILE_ICON: '',
NODE_SEPARATOR: ';',
ROOT_ID: '0',
FILTER_PLACEHOLDER: 'Search...',
convertValueTreeToArray: function (nodeValues, id) {
var array = [];
var push = function (node, id) {
Object.keys(node).forEach(function (key) {
if (key == 'selected' && node[key]) {
if (id != NTree.ROOT_ID) {
array.push(id);
} else {
array.selected = node.selected ? true : undefined;
}
} else {
push(node[key], key);
}
});
};
push(nodeValues, id);
return array;
}
},
getDefaultProps: function () {
return {
defaultOptions: {
root: true,
check: false,
inactiveSlibing: true,
opIconEnabled: false,
multiple: true,
valueAsArray: false,
hierarchyCheck: false,
expandLevel: 1,
border: false,
expandButton: {
comp: {
icon: 'plus-square-o',
style: 'link'
}
},
collapseButton: {
comp: {
icon: 'minus-square-o',
style: 'link'
}
}
}
};
},
getInitialState: function () {
var expandBtn = $.extend(true, {}, this.getComponentOption('expandButton'));
if (expandBtn) {
if (!expandBtn.comp.click) {
expandBtn.comp.click = this.expandAll;
}
expandBtn = $pt.createCellLayout('expand', expandBtn);
}
var collapseBtn = $.extend(true, {}, this.getComponentOption('collapseButton'));
if (collapseBtn) {
if (!collapseBtn.comp.click) {
collapseBtn.comp.click = this.collapseAll;
}
collapseBtn = $pt.createCellLayout('collapse', collapseBtn);
}
return {
activeNodes: {},
root: { text: this.getRootLabel(), id: NTree.ROOT_ID },
expandButton: expandBtn,
collapseButton: collapseBtn,
initialized: false
};
},
componentWillMount: function () {
if (this.state.root.children) {
this.setState({ initialized: true });
}
},
componentWillUpdate: function () {
if (!this.state.initialized && this.state.root.children && this.isSearchable()) {
var nodes = this.filterNodes(this.state.root);
this.setState({ root: nodes, initialized: true });
}
},
renderCheck: function (node, nodeId) {
var canSelected = this.isNodeCanSelect(node);
if (!canSelected) {
return null;
}
var modelValue = this.getValueFromModel();
modelValue = modelValue ? modelValue : {};
var model = $pt.createModel({ selected: this.isNodeChecked(nodeId) });
model.useBaseAsCurrent();
var layoutJSON = {
comp: {
type: $pt.ComponentConstants.Check
}
};
var valueCanChange = this.isNodeCheckCanChange(node);
if (valueCanChange != null) {
layoutJSON.comp.enabled = valueCanChange;
}
var layout = $pt.createCellLayout('selected', layoutJSON);
model.addPostChangeListener('selected', this.onNodeCheckChanged.bind(this, node, nodeId));
return React.createElement($pt.Components.NCheck, { model: model, layout: layout, view: this.isViewMode() });
},
renderNode: function (parentNodeId, node) {
if (node.visible === false) {
return null;
}
var nodeId = this.getNodeId(parentNodeId, node);
var opIcon = null;
if (this.getComponentOption('opIconEnabled')) {
var expandableIconAttrs = {
iconClassName: 'node-op-icon',
fixWidth: true,
icon: this.getNodeOperationIcon(node, nodeId)
};
opIcon = React.createElement(
'a',
{ href: 'javascript:void(0);',
onClick: this.onNodeClicked.bind(this, node, nodeId) },
React.createElement($pt.Components.NIcon, expandableIconAttrs)
);
}
var folderIconAttrs = {
icon: this.getNodeIcon(node, nodeId),
fixWidth: true,
iconClassName: 'node-icon'
};
var folderIcon = React.createElement(
'a',
{ href: 'javascript:void(0);',
onClick: this.onNodeClicked.bind(this, node, nodeId) },
React.createElement($pt.Components.NIcon, folderIconAttrs)
);
var _this = this;
var buttons = this.getComponentOption('nodeOperations');
buttons = buttons ? Array.isArray(buttons) ? buttons : [buttons] : [];
buttons = buttons.map(function (button, buttonIndex) {
var visible = true;
if (typeof button.visible === 'boolean') {
visible = button.visible;
} else if (typeof button.visible === 'function') {
visible = button.visible.call(_this, node);
}
if (!visible) {
return null;
}
var icon = {
icon: button.icon,
fixWidth: true
};
return React.createElement(
'a',
{ href: 'javascript:void(0);',
onClick: _this.onNodeOperationClicked.bind(_this, node, button.click),
className: 'node-button',
key: buttonIndex,
title: button.text },
React.createElement($pt.Components.NIcon, icon)
);
}).filter(function (button) {
return button != null;
});
var active = this.isActive(nodeId) ? 'active' : null;
return React.createElement(
'li',
{ className: active, key: nodeId, 'data-node-id': nodeId },
React.createElement(
'div',
{ className: 'node-content' },
opIcon,
folderIcon,
this.renderCheck(node, nodeId),
React.createElement(
'a',
{ className: 'node-text-link-' + buttons.length,
href: 'javascript:void(0);',
onClick: this.onNodeLabelClicked.bind(this, node, nodeId) },
React.createElement(
'span',
{ className: 'node-text' },
this.getNodeText(node)
)
),
buttons
),
this.getComponentOption('lazyLoad') && !active ? null : this.renderNodes(node, nodeId)
);
},
renderNodes: function (parent, parentNodeId) {
var children = parent.children;
if (children && children.length > 0 && children.some(function (item) {
return item.visible !== false;
})) {
return React.createElement(
'ul',
{ className: 'nav' },
children.map(this.renderNode.bind(this, parentNodeId))
);
} else {
return null;
}
},
renderRoot: function () {
return React.createElement(
'ul',
{ className: 'nav' },
this.renderNode(null, this.state.root)
);
},
renderTopLevel: function () {
return React.createElement($pt.Components.NCodeTableWrapper, { codetable: this.getCodeTable(),
renderer: this.getRealTopLevelRenderer,
model: this.getModel(),
layout: this.getLayout(),
onMounted: this.initExpand });
},
getRealTopLevelRenderer: function () {
var root = this.state.root;
root.children = this.getTopLevelNodes();
return this.isRootPaint() ? this.renderRoot() : this.renderNodes(root, this.getNodeId(null, root));
},
renderButtons: function () {
var expand = this.state.expandButton ? React.createElement($pt.Components.NFormButton, { model: this.getModel(), layout: this.state.expandButton }) : null;
var collapse = this.state.collapseButton ? React.createElement($pt.Components.NFormButton, { model: this.getModel(), layout: this.state.collapseButton }) : null;
if (expand || collapse) {
return React.createElement(
'span',
{ className: 'buttons' },
expand,
collapse
);
} else {
return null;
}
},
renderFilterText: function () {
var model = $pt.createModel({
text: this.state.filterText,
// on mobile phone, set as true to disable the soft keyboard
// set as false to enable it when popover render completed
// see #onPopoverRenderComplete
disabled: this.isMobilePhone() ? true : false
});
var layout = $pt.createCellLayout('text', {
comp: {
placeholder: NTree.FILTER_PLACEHOLDER,
/* rightAddon: {
icon: 'search',
click: this.onFilterTextChange
}, */
enabled: {
depends: 'disabled',
when: function (model) {
return model.get('disabled') !== true;
}
}
},
css: { comp: 'bottom-margin-5' },
evt: {
keyUp: this.onSearchEnterKeyUp
}
});
model.addPostChangeListener('text', this.onFilterTextChange);
this.state.filterModel = model;
return React.createElement($pt.Components.NText, { model: model, layout: layout });
},
initExpand: function () {
var expandLevel = this.getComponentOption('expandLevel');
if (expandLevel == null) {
// default expand root
expandLevel = 0;
}
if (expandLevel === 'all') {
expandLevel = 9999;
}
if (this.state.root.children) {
// this.state.root.children = this.getTopLevelNodes();
this.expandTo(expandLevel);
}
},
render: function () {
var styles = {};
if (this.getComponentOption('height')) {
styles.height = this.getComponentOption('height');
}
if (this.getComponentOption('maxHeight')) {
styles.maxHeight = this.getComponentOption('maxHeight');
}
var css = this.getComponentCSS('n-tree'),
searchable = this.isSearchable();
if (searchable) {
css += ' searchable';
}
if (this.getComponentOption('border')) {
css += ' border';
}
return React.createElement(
'div',
{ className: css, style: styles, ref: 'me' },
searchable ? this.renderFilterText() : null,
this.renderTopLevel(),
this.renderButtons()
);
},
onSearchEnterKeyUp: function (evt) {
evt.preventDefault();
evt.stopPropagation();
if (evt.keyCode !== 13) {
return;
}
this.onFilterTextChange(evt);
},
onFilterTextChange: function (evt) {
var val = evt.new || this.state.filterModel.get('text');
var filteredNodes = this.filterNodes(this.state.root, val);
this.setState({ filterText: val, root: filteredNodes });
},
filterNodes: function (node, value) {
if (node.children && node.children.length > 0) {
node.children = node.children.map(function (item) {
return this.filterNodes(item, value);
}.bind(this));
}
// console.log('[filterNodes]', node, value)
if (!value || node.text.toUpperCase().indexOf(value.toUpperCase()) >= 0 || node.children && node.children.some(function (item) {
return item.visible !== false;
})) {
node.visible = true;
} else {
node.visible = false;
}
return node;
},
onNodeClicked: function (node, nodeId) {
if (!this.isLeaf(node)) {
if (this.state.activeNodes[nodeId]) {
this.collapseNode(node, nodeId);
} else {
this.expandNode(node, nodeId);
}
}
},
onNodeLabelClicked: function (node, nodeId, evt) {
var nodeClick = this.getComponentOption('nodeClick');
if (nodeClick) {
var target = $(evt.target);
var callback = function (keepOtherHighlights) {
if (!this.isMultipleHighlight() || !keepOtherHighlights) {
$(ReactDOM.findDOMNode(this.refs.me)).find('div.node-content').removeClass('node-highlight');
}
target.closest('div.node-content').addClass('node-highlight');
}.bind(this);
var returnValue = nodeClick.call(this, node, callback, evt);
if (returnValue === true) {
callback.call(this);
} else if (returnValue === 'keep') {
callback.call(this, true);
} else if (returnValue && returnValue.done) {
returnValue.done(function (data) {
callback.call(this, data);
}.bind(this));
}
} else {
this.onNodeClicked(node, nodeId);
}
},
onNodeOperationClicked: function (node, click) {
if (click) {
click.call(this, node);
}
},
onNodeCheckChanged: function (node, nodeId, evt, toChildOnly) {
var hierarchyCheck = this.isHierarchyCheck();
var modelValue = this.getValueFromModel();
if (this.isValueAsArray()) {
modelValue = modelValue ? modelValue : [];
if (hierarchyCheck) {
this.checkNodeHierarchy(node, nodeId, evt.new, modelValue);
this.hierarchyCheckToAncestors(nodeId, modelValue);
} else {
this.checkNode(nodeId, evt.new, modelValue);
}
if (this.getValueFromModel() != modelValue) {
// simply set to model
this.setValueToModel(modelValue);
} else {
// fire event manually
this.getModel().firePostChangeEvent(this.getDataId(), modelValue, modelValue);
}
} else {
if (!modelValue) {
modelValue = {};
}
if (hierarchyCheck) {
this.checkNodeHierarchy(node, nodeId, evt.new, modelValue);
this.hierarchyCheckToAncestors(nodeId, modelValue);
} else {
this.checkNode(nodeId, evt.new, modelValue);
}
if (this.getValueFromModel() != modelValue) {
// simply set to model
this.setValueToModel(modelValue);
} else {
// fire event manually
this.getModel().firePostChangeEvent(this.getDataId(), modelValue, modelValue);
}
}
},
isValueAsArray: function () {
return this.getComponentOption('valueAsArray');
},
/**
* check or uncheck node. will not fire post change event.
*/
checkNode: function (nodeId, value, modelValue) {
if (this.isValueAsArray()) {
if (!this.isMultipleSelection()) {
// no multiple selection
modelValue.length = 0;
}
if (nodeId == this.state.root.id) {
modelValue.selected = value;
} else {
var ids = nodeId.split(NTree.NODE_SEPARATOR);
var id = ids[ids.length - 1];
var index = modelValue.findIndex(function (value) {
return value == id;
});
if (value && index == -1) {
modelValue.push(id);
} else if (!value && index != -1) {
modelValue.splice(index, 1);
}
}
} else {
if (!this.isMultipleSelection()) {
// no multiple selection
Object.keys(modelValue).forEach(function (key) {
delete modelValue[key];
});
}
if (nodeId == this.state.root.id) {
$pt.setValueIntoJSON(modelValue, 'selected', value);
} else {
var segments = nodeId.split(NTree.NODE_SEPARATOR);
$pt.setValueIntoJSON(modelValue, segments.slice(1).join($pt.PROPERTY_SEPARATOR) + $pt.PROPERTY_SEPARATOR + 'selected', value);
}
}
return modelValue;
},
/**
* check or uncheck node hierarchy. will not fire post change event.
*/
checkNodeHierarchy: function (node, nodeId, value, modelValue) {
modelValue = this.checkNode(nodeId, value, modelValue);
if (node.children) {
var _this = this;
node.children.forEach(function (child) {
var childId = _this.getNodeId(nodeId, child);
_this.checkNodeHierarchy(child, childId, value, modelValue);
});
}
return modelValue;
},
isNodeChecked: function (nodeId, modelValue) {
modelValue = modelValue ? modelValue : this.getValueFromModel();
modelValue = modelValue ? modelValue : {};
if (Array.isArray(modelValue)) {
if (nodeId == this.state.root.id) {
return modelValue.selected;
} else {
var ids = nodeId.split(NTree.NODE_SEPARATOR);
var id = ids[ids.length - 1];
return -1 != modelValue.findIndex(function (value) {
return value == id;
});
}
} else {
if (nodeId == this.state.root.id) {
return $pt.getValueFromJSON(modelValue, 'selected');
} else {
return $pt.getValueFromJSON(modelValue, nodeId.split(NTree.NODE_SEPARATOR).slice(1).join($pt.PROPERTY_SEPARATOR) + $pt.PROPERTY_SEPARATOR + 'selected');
}
}
},
hierarchyCheckToAncestors: function (nodeId, modelValue) {
var _this = this;
var checkNodeOnChildren = function (node, nodeId) {
if (node.children) {
var hasUncheckedChild = false;
node.children.forEach(function (child) {
var checked = checkNodeOnChildren(child, _this.getNodeId(nodeId, child));
if (!checked) {
hasUncheckedChild = true;
}
});
// window.console.log(nodeId);
_this.checkNode(nodeId, !hasUncheckedChild, modelValue);
return !hasUncheckedChild;
} else {
// no children, return checked of myself
// window.console.log(nodeId);
return _this.isNodeChecked(nodeId, modelValue);
}
};
checkNodeOnChildren(this.state.root, this.getNodeId(null, this.state.root));
// window.console.log(modelValue);
},
expandTo: function (expandLevel) {
var activeNodes = $.extend({}, this.state.activeNodes);
var _this = this;
var expand = function (parentId, node, level) {
if (level < expandLevel) {
var nodeId = _this.getNodeId(parentId, node);
activeNodes[nodeId] = node;
if (node.children) {
node.children.forEach(function (child) {
expand(nodeId, child, level + 1);
});
}
}
};
expand(null, this.state.root, 0);
var previousActiveNodes = null;
this.setState(function (previousState, currentProps) {
previousActiveNodes = previousState.activeNodes;
return { activeNodes: activeNodes };
}, function () {
_this.notifyEvent({
type: 'expand',
before: previousActiveNodes,
after: activeNodes
});
});
},
expandAll: function () {
var activeNodes = $.extend({}, this.state.activeNodes);
var root = this.state.root;
var expand = function (node, parentNodeId) {
if (!this.isLeaf(node)) {
var nodeId = this.getNodeId(parentNodeId, node);
activeNodes[nodeId] = node;
var _this = this;
node.children.forEach(function (child) {
expand.call(_this, child, nodeId);
});
}
};
expand.call(this, root, null);
// this.setState({activeNodes: activeNodes});
var _this = this;
var previousActiveNodes = null;
this.setState(function (previousState, currentProps) {
previousActiveNodes = previousState.activeNodes;
return { activeNodes: activeNodes };
}, function () {
_this.notifyEvent({
type: 'expand',
before: previousActiveNodes,
after: activeNodes
});
});
},
collapseAll: function () {
var _this = this;
// var root = this.state.root;
// var nodeIds = null;
// if (this.isRootPaint()) {
// // this.collapseNode(root, this.getNodeId(null, root));
// nodeIds = [this.getNodeId(null, root)];
// } else {
// var rootNodeId = this.getNodeId(null, root);
// if (root.children) {
// // root.children.forEach(function(node) {
// // this.collapseNode(node, this.getNodeId(rootNodeId, node));
// // }.bind(this));
// nodeIds = root.children.map(function(node) {
// return _this.getNodeId(rootNodeId, node);
// });
// }
// }
// var regexp = new RegExp(nodeIds.map(function(nodeId) {
// return '(' + nodeId + ')';
// }).join(NTree.NODE_SEPARATOR));
// var activeNodes = $.extend({}, this.state.activeNodes);
var activeNodes = {};
Object.keys(activeNodes).forEach(function (key) {
if (key.match(regexp)) {
delete activeNodes[key];
}
});
var previousActiveNodes = null;
this.setState(function (previousState, currentProps) {
previousActiveNodes = previousState.activeNodes;
return { activeNodes: activeNodes };
}, function () {
_this.notifyEvent({
type: 'collapse',
before: previousActiveNodes,
after: activeNodes
});
});
},
isRootPaint: function () {
return this.getComponentOption('root');
},
getRootLabel: function () {
var root = this.getComponentOption('root');
if (typeof root === 'string') {
return root;
} else {
return NTree.ROOT_LABEL;
}
},
isSearchable: function () {
return this.getComponentOption('searchable') && !this.isViewMode();
},
isActive: function (nodeId) {
return this.state.activeNodes[nodeId];
},
isLeaf: function (node) {
return !node.children || node.children.length == 0;
},
isInactiveSlibingWhenActive: function () {
return this.getComponentOption('inactiveSlibing');
},
collapseNode: function (node, nodeId) {
var regexp = new RegExp(nodeId);
var activeNodes = $.extend({}, this.state.activeNodes);
Object.keys(activeNodes).forEach(function (key) {
if (key.match(regexp)) {
delete activeNodes[key];
}
});
// this.setState({activeNodes: activeNodes});
var _this = this;
var previousActiveNodes = null;
this.setState(function (previousState, currentProps) {
previousActiveNodes = previousState.activeNodes;
return { activeNodes: activeNodes };
}, function () {
_this.notifyEvent({
type: 'collapse',
before: previousActiveNodes,
after: activeNodes
});
});
},
expandNode: function (node, nodeId) {
var activeNodes = $.extend({}, this.state.activeNodes);
if (this.isInactiveSlibingWhenActive() && !this.isLeaf(node)) {
// remove all slibings and their children from active list
var lastHyphen = nodeId.lastIndexOf(NTree.NODE_SEPARATOR);
if (lastHyphen > 0) {
var regexp = new RegExp(nodeId.substring(0, lastHyphen + 1));
Object.keys(activeNodes).forEach(function (key) {
if (key.match(regexp)) {
delete activeNodes[key];
}
});
} else if (!this.isRootPaint()) {
// no root painted, current is top level node, and need inactive slibings
// clear all
activeNodes = {};
}
}
activeNodes[nodeId] = node;
// this.setState({activeNodes: activeNodes});
var _this = this;
var previousActiveNodes = null;
this.setState(function (previousState, currentProps) {
previousActiveNodes = previousState.activeNodes;
return { activeNodes: activeNodes };
}, function () {
_this.notifyEvent({
type: 'expand',
before: previousActiveNodes,
after: activeNodes
});
});
},
/**
* get top level nodes
* @returns {{}[]}
*/
getTopLevelNodes: function () {
return this.getCodeTable().list();
},
/**
* get avaiable top level nodes
* @returns {CodeTable}
*/
getCodeTable: function () {
return this.getComponentOption('data');
},
getNodeIcon: function (node, nodeId) {
var isLeaf = this.isLeaf(node);
// not leaf, must be a folder, or node is defined as folder
var isFolder = !isLeaf || node.folder;
var active = this.isActive(nodeId);
if (isFolder) {
if (isLeaf) {
return this.getCustomNodeIcon({
node: node,
active: active,
folder: true,
leaf: true
}, NTree.FOLDER_ICON);
} else if (active) {
return this.getCustomNodeIcon({
node: node,
active: true,
folder: true,
leaf: false
}, NTree.FOLDER_OPEN_ICON);
} else {
return this.getCustomNodeIcon({
node: node,
active: false,
folder: true,
leaf: false
}, NTree.FOLDER_ICON);
}
} else {
return this.getCustomNodeIcon({
node: node,
active: active,
folder: false,
leaf: true
}, NTree.FILE_ICON);
}
},
getNodeText: function (node) {
return node.text;
},
/**
* get customized node icon
* @param options {node: JSON, active: boolean, folder: boolean, leaf: boolean}
*/
getCustomNodeIcon: function (options, defaultIcon) {
var icon = this.getComponentOption('nodeIcon');
if (typeof icon === 'function') {
return icon.call(this, options);
} else if (icon) {
return icon;
} else {
return defaultIcon;
}
},
getNodeOperationIcon: function (node, nodeId) {
var isLeaf = this.isLeaf(node);
// not leaf, must be a folder, or node is defined as folder
var isFolder = !isLeaf || node.folder;
var active = this.isActive(nodeId);
if (isFolder) {
if (isLeaf) {
return this.getCustomNodeOperationIcon({
node: node,
active: active,
folder: true,
leaf: true
}, NTree.OP_FOLDER_LEAF_ICON);
} else {
if (active) {
return this.getCustomNodeOperationIcon({
node: node,
active: true,
folder: true,
leaf: false
}, NTree.OP_FOLDER_OPEN_ICON);
} else {
return this.getCustomNodeOperationIcon({
node: node,
active: false,
folder: true,
leaf: false
}, NTree.OP_FOLDER_ICON);
}
}
} else {
return this.getCustomNodeOperationIcon({
node: node,
active: active,
folder: false,
leaf: true
}, NTree.OP_FILE_ICON);
}
},
/**
* get customized node operation icon
* @param options {node: JSON, active: boolean, folder: boolean, leaf: boolean}
*/
getCustomNodeOperationIcon: function (options, defaultIcon) {
var icon = this.getComponentOption('opNodeIcon');
if (typeof icon === 'function') {
return icon.call(this, options);
} else if (icon) {
return icon;
} else {
return defaultIcon;
}
},
isNodeCanSelect: function (node) {
var check = this.getComponentOption('check');
if (typeof check === 'function') {
return check.call(this, node);
} else if (check) {
return check;
} else {
return false;
}
},
isNodeCheckCanChange: function (node) {
var change = this.getComponentOption('valueCanCheck');
if (typeof change === 'function') {
return change.call(this, node);
} else if (change != null) {
return change;
} else {
return true;
}
},
/**
* is multiple selection allowed
* @returns {boolean}
*/
isMultipleSelection: function () {
return this.getComponentOption('multiple');
},
isMultipleHighlight: function () {
return this.getComponentOption('multipleHightlight', false);
},
/**
* is hierarchy check, effective only when multiple is true
* @returns {boolean}
*/
isHierarchyCheck: function () {
return this.getComponentOption('hierarchyCheck') && this.isMultipleSelection();
},
getNodeId: function (parentNodeId, node) {
var nodeId = null;
if (parentNodeId) {
nodeId = parentNodeId + NTree.NODE_SEPARATOR + node.id;
} else {
nodeId = '' + node.id;
}
return nodeId;
}
}));
// expose to global
$pt.Components.NTree = NTree;
$pt.LayoutHelper.registerComponentRenderer($pt.ComponentConstants.Tree, function (model, layout, direction, viewMode) {
return React.createElement($pt.Components.NTree, $pt.LayoutHelper.transformParameters(model, layout, direction, viewMode));
});
})(window, jQuery, React, ReactDOM, $pt);
// only packaged in browser environment
(function (window, $pt) {
// expose all components definition to window
if (typeof DONT_EXPOSE_PARROT_TO_GLOBAL === 'undefined' || DONT_EXPOSE_PARROT_TO_GLOBAL !== true) {
$pt.exposeComponents(window);
}
})(window, $pt);