"use strict"; /* Minify Order(1) */ window.TK = {}; window.TK.AutoTypeSelection = true; window.TK.Initialize = function (obj, parentObj, nameChildObj, selfObj, taggedObjects) { var reserved = ['Parent', 'Sibling', 'Add', 'AddMultiple', 'Clear', 'Near', 'Remove', 'SetHTML', '_']; var allObjs = [obj]; var inits = []; var copyObj = {}; var defaultInnerHTML = null; var type = obj._; if (!taggedObjects) taggedObjects = {}; var childTagged = {}; // Allow types to point to different objects (overriding their properties) while (type && (typeof type != "string" || type.indexOf(".") > 0)) { if (typeof type == "string") { type = eval(type); } allObjs.push(type); type = type._; } // Allow classname to be specified (example: div.orange) var className = null; /*if (type && type.indexOf && type.indexOf(".") >= 0) { className = type.substr(type.indexOf(".") + 1); type = type.substr(0, type.indexOf(".")); if (type == "") type = null; }*/ // Default type (div, unless name ends with Input, Button, Select or Label) if (!type) { type = "div"; if (nameChildObj && window.TK.AutoTypeSelection) { var autoTypes = ["Input", "TextButton", "Button", "Select", "Label", "Span", "Textarea", "H1", "H2", "H3", "H4"]; var endsWith = function (s, has) { if (!s || !has || s.length < has) return false; return s.substr(s.length - has.length) == has; }; for (var i = 0; i < autoTypes.length; i++) { if (endsWith(nameChildObj, autoTypes[i])) { if (autoTypes[i] == "TextButton") { defaultInnerHTML = nameChildObj.substr(0, nameChildObj.length - 10); type = "button"; } else { type = autoTypes[i].toLowerCase(); } break; } } } } // Actually create the element (or empty object, in case its a component with no DOM representation) var copyObj = type != "component" ? document.createElement(type) : {}; copyObj._ = type; if (type == "button") { copyObj.type = "button"; // Prevent Buttons to automatically become submit buttons in case they are located in a form } // Go through all levels of elements, set (and override) properties. Keep all child elements and Init functions in a seperate list var elements = {}; var resolveProperties = []; for (var i = allObjs.length - 1; i >= 0; i--) { for (var propName in allObjs[i]) { if (propName == "style" && copyObj.appendChild) { for (var styleName in allObjs[i].style) { copyObj.style[styleName] = allObjs[i].style[styleName]; } continue; } if (reserved.indexOf(propName) >= 0) continue; if (propName == "Elements") { for (var elementName in allObjs[i][propName]) { if (typeof allObjs[i][propName][elementName] === 'string') { elements[elementName] = { HTML: allObjs[i][propName][elementName] }; } else if (typeof allObjs[i][propName][elementName] === 'function') { elements[elementName] = { onclick: allObjs[i][propName][elementName] }; } else { elements[elementName] = allObjs[i][propName][elementName]; } } } else { if (allObjs[i][propName] && allObjs[i][propName].ToolkitPropertyReference) resolveProperties.push(propName); if (copyObj["__RecursiveProperties" + propName]) { // We're going to copy all settings recursively window.TK.RecursiveCopy(allObjs[i], copyObj, propName); } else { if (allObjs[i][propName] !== undefined) copyObj[propName] = allObjs[i][propName]; } } if (propName == "Init") inits.push(copyObj[propName]); } } // Set the classname (Automatically adds a Element-Name class, and any custom classes) if (type != "component" && (nameChildObj || className)) { if (!copyObj.className) copyObj.className = ""; copyObj.className += " " + (nameChildObj ? "Element-" + nameChildObj : "") + (className ? " " + className : ""); } if (nameChildObj) { copyObj._Name = nameChildObj; } if (copyObj._Self) // When set to true, this forces this element to be marked as 'Self' selfObj = copyObj; if (defaultInnerHTML && (!copyObj.innerHTML || copyObj.innerHTML == "")) { copyObj.innerHTML = defaultInnerHTML; } if (copyObj._Position && copyObj.style) { var isSet = function (a) { return !(a === null || a === undefined); }; var p = TK.ParsePosition(copyObj._Position); // top,right,bottom,left,width,height[,relative] copyObj.style.top = isSet(p[0]) ? p[0] : ""; copyObj.style.right = isSet(p[1]) ? p[1] : ""; copyObj.style.bottom = isSet(p[2]) ? p[2] : ""; copyObj.style.left = isSet(p[3]) ? p[3] : ""; if (isSet(p[4])) copyObj.style.width = p[4]; if (isSet(p[5])) copyObj.style.height = p[5]; copyObj.style.position = isSet(p[6]) ? p[6] : "absolute"; } for (var name in taggedObjects) { copyObj["$"+name] = taggedObjects[name]; // Tagged parent objects always start with a $ to make them easy to recognize } if (copyObj._Tag) { if (copyObj._Tag === true && nameChildObj) copyObj._Tag = nameChildObj; var tag = copyObj._Tag.substr(0, 1) == "$" ? copyObj._Tag.substr(1) : copyObj._Tag; taggedObjects[tag] = copyObj; } //if (nameChildObj) // taggedObjects["Element_"+nameChildObj] = copyObj; // Speed up the 'Near' method. TODO: Need to test .Add performance impact first // Add extra helper functions copyObj.Add = function (obj, nameChildObj) { // Add single child element var copyTagged = {}; for (var tagName in taggedObjects) { if (childTagged[tagName]) // Make sure we don't pass other siblings childs continue; copyTagged[tagName] = taggedObjects[tagName]; } return window.TK.Initialize(obj, this, nameChildObj, null, copyTagged); }; copyObj.AddMultiple = function (obj, propertyArray, syncPropertyName, useVariable) { // Add multiple child elements var newObjs = []; var allNewObjIds = []; var existingObjIds = []; if (syncPropertyName) { existingObjIds = this.Elements.ToArray().Select(function (a) { return useVariable ? a[useVariable][syncPropertyName] : a[syncPropertyName]; }); } for (var i = 0; i < propertyArray.length; i++) { if (syncPropertyName) { var objId = propertyArray[i][syncPropertyName]; allNewObjIds.push(objId); var existingIndex = existingObjIds.indexOf(objId); if (existingIndex >= 0) { // Already exists existingObjIds.splice(existingIndex, 1); continue; } } var toInitialize = propertyArray[i]; if (useVariable) { toInitialize = { _: obj }; toInitialize[useVariable] = propertyArray[i]; } else { toInitialize._ = obj; } var copyTagged = {}; for (var tagName in taggedObjects) { if (childTagged[tagName]) // Make sure we don't pass other siblings childs continue; copyTagged[tagName] = taggedObjects[tagName]; } newObjs.push(window.TK.Initialize(toInitialize, this, null, null, copyTagged)); } if (syncPropertyName) { // Remove all old child elements which aren't in the new propertyArray for (var childName in this.Elements) { if (typeof this.Elements[childName] != "function" && allNewObjIds.indexOf(useVariable ? this.Elements[childName][useVariable][syncPropertyName] : this.Elements[childName][syncPropertyName]) < 0) { this.Elements[childName].Remove(); } } } return newObjs; }; copyObj.Remove = function (onlyExecuteCallback) { if (this.Destroy) { this.Destroy(); } for (var childName in this.Elements) { if (this.Elements[childName].Remove) this.Elements[childName].Remove(true); } if (onlyExecuteCallback) return; if (this.Parent) { if (this.parentNode) this.parentNode.removeChild(this); for (var childName in this.Parent.Elements) { if (this.Parent.Elements[childName] == this) { delete this.Parent.Elements[childName]; break; } } delete this.Parent; } else { if (this.parentNode && this.parentNode.removeChild) this.parentNode.removeChild(this); } }; copyObj.Clear = function () { this.Elements.ToArray().Select(function (a) { a.Remove(); }); }; copyObj.Near = function (name) { // Find the nearest element with this name, or classname, or id if (name.substr(0, 1) == "$") { // Search by tag var tmp = this; while (tmp) { if (tmp[name]) return tmp[name]; tmp = tmp.Parent; } } var curEle = this; var findName = name; if (name.substr(0, 1) != "." && name.substr(0, 1) != "#") { if (curEle.Elements && curEle.Elements[name]) return curEle.Elements[name]; // Direct child if (curEle["$$" + name]) return curEle["$$" + name]; // Child somewhere in the tree if (curEle["$" + name]) return curEle["$" + name]; // One of the parents findName = ".Element-" + findName; } var found = curEle.querySelector(findName); if (found) return found; while (curEle.Parent || curEle.parentNode) { curEle = curEle.Parent ? curEle.Parent : curEle.parentNode; if (curEle._Name == name) return curEle; if (curEle["$$" + name]) return curEle["$$" + name]; if (curEle["$" + name]) return curEle["$" + name]; /*if (curEle["$$Element_" + name]) // Speed up the 'Near' method. TODO: Need to test .Add performance impact first return curEle["$$Element_" + name]; if (curEle["$Element_" + name]) return curEle["$Element_" + name]; */ if (curEle.className && "." + curEle.className == name) return curEle; if (curEle.Elements && curEle.Elements[name]) return curEle.Elements[name]; var found = curEle.querySelector(findName); if (found) return found; } return null; }; copyObj.DetachElementFromParent = function () { // This appends the div to the document.body element with a fixed position on the same position using getBoundingClientRect if (!this.getBoundingClientRect) return; var rect = this.getBoundingClientRect(); if (!rect || isNaN(rect.top) || isNaN(rect.left)) return; this.style.position = "fixed"; this.style.top = rect.top + "px"; this.style.left = rect.left + "px"; document.body.appendChild(this); }; var getReference = function (capture, observingObject) { var curObj = copyObj; if (typeof capture == "function") { return capture(copyObj); } if (capture.indexOf(":") >= 0) { curObj = copyObj.Near(capture.substr(0, capture.indexOf(":"))); if (!curObj) return ""; capture = capture.substr(capture.indexOf(":") + 1); } if (capture != "") { while (capture.indexOf(".") > 0) { var part = capture.substr(0, capture.indexOf(".")); if (part == "Parent") curObj = parentObj; else curObj = curObj[part]; capture = capture.substr(capture.indexOf(".") + 1); } if (observingObject) { // Add a getter/setter structure to the property we want to observe, when changed, call the object's FieldUpdate function curObj["__Orig" + capture] = curObj[capture]; Object.defineProperty(curObj, capture, { get: function () { return this["__Orig" + capture]; }, set: function (value) { this["__Orig" + capture] = value; if (observingObject.Obj.FieldUpdate) { observingObject.Obj.FieldUpdate(observingObject.Name, value); } } }); } curObj = curObj[capture]; } return curObj; }; copyObj.SetHTML = function (html) { var injectChildsObj = {}; var injectChilds = false; this.innerHTML = html.replace(/\$([\ \-\#\:\w\.]*?)\$/g, function (match, capture) { var curObj = getReference(capture); if (curObj && curObj.tagName) { // Reference added to a HTML element var rnd = "placeholder-" + Math.floor(Math.random() * 1000000) + "-" + Math.floor(Math.random() * 1000000) + "-" + Math.floor(Math.random() * 1000000); injectChildsObj[rnd] = curObj; injectChilds = true; return ""; } return curObj; }); if (injectChilds) { var items = this.querySelectorAll(".tkInternalPlaceHolder"); for (var i = 0; i < items.length; i++) { var elementToAdd = injectChildsObj[items[i].id]; items[i].parentNode.insertBefore(elementToAdd, items[i]); items[i].parentNode.removeChild(items[i]); } } }; copyObj.Elements = {}; copyObj.Elements.ToArray = function () { var arr = []; for (var propName in this) { if (typeof this[propName] != "function") arr.push(this[propName]); } return arr; }; // Set the 'Self' property, which will always point to the main object added with .Add or TK.Initialize if (!selfObj) selfObj = copyObj; if (copyObj.Self === undefined) copyObj.Self = selfObj; // Create all sub elements for (var name in elements) { var copyTagged = {}; for (var tagName in taggedObjects) copyTagged[tagName] = taggedObjects[tagName]; window.TK.Initialize(elements[name], copyObj, name, selfObj, copyTagged); // The copyTagged object is now expanded with child objects, we will just keep the new childs for (var name in copyTagged) { if (taggedObjects[name] == copyTagged[name]) continue; // Old one we've already added as parent, or it is ourself childTagged[name] = copyTagged[name]; } } for (var name in childTagged) { copyObj["$$" + name] = childTagged[name]; taggedObjects[name] = childTagged[name]; // Will also add it to our tagged object (but only at this step), so any of the didn't get the child elements of their siblings. } // Add this element to the child elements of a parent element (and get a reference to it) if (parentObj) { copyObj.Parent = parentObj; copyObj.Sibling = parentObj.Elements; parentObj.Elements[nameChildObj ? nameChildObj : ("ele"+Math.random().toString())] = copyObj; if (copyObj.appendChild && parentObj.appendChild) { // This and parent element are html nodes parentObj.appendChild(copyObj); } } // Special HTML template support. Variables in the same object can be including using their $PropertyName$ , $Parent.Sub.PropertyName$ etc. also works // You can also reference to other elements, which will be appended at the correct locations. if (copyObj.HTML) { copyObj.SetHTML(copyObj.HTML); } // Use TK.P("Parent.PropertyName") in a template to reference to a object on runtime for (var i = 0; i < resolveProperties.length; i++) { var p = copyObj[resolveProperties[i]]; if (!p.ToolkitPropertyReference) continue; if (p.Observe) { // Attach getter/setter to the property we are looking at, so it can call our 'FieldChange' function getReference(p.ToolkitPropertyReference, { Name: resolveProperties[i], Obj: copyObj }); // Attach an getter to the current object, to always retrieve the latest value var attachGetter = function (referencePath) { Object.defineProperty(copyObj, resolveProperties[i], { get: function () { return getReference(referencePath); } }); }; attachGetter(p.ToolkitPropertyReference); } else { copyObj[resolveProperties[i]] = getReference(p.ToolkitPropertyReference); } } // Call all init functions of inheritance objects, lowest level goes first, all the 'overrides' go after for (var i = 0; i < inits.length; i++) { inits[i].call(copyObj); } return copyObj; }; window.TK.P = function (name, observe) { return { ToolkitPropertyReference: name, Observe: observe }; }; window.TK.RecursiveCopy = function (objSource, objTarget, singleProperty) { for (var n in objSource) { if (objSource[n] === undefined || (singleProperty && n != singleProperty)) continue; if (!(objSource[n] instanceof Object) || typeof objSource[n] == "function") { objTarget[n] = objSource[n]; } else if (Array.isArray(objSource[n])) { if (!objTarget[n]) objTarget[n] = []; for (var i = 0; i < objSource[n].length; i++) { objTarget[n].push(objSource[n][i]); } } else { if (!objTarget[n]) objTarget[n] = {}; window.TK.RecursiveCopy(objSource[n], objTarget[n]); } } }; window.TK.ParsePosition = function (p) { if (!p) return []; // Parse a _Position property and returns an array with all values // Supported: // - [top,right,bottom,left,width,height,tags] // - L10 T20 W200 H400 // - L10 T20 R10 B10 // - X10 Y20 W200 H400 relative // - [100, 200, "50px","10%", null, null, "relative"] if (p.substr) { if (p.indexOf(",") >= 0) { // 'old' style p = p.split(/,/g); } else { var newP = []; var matches = p.match(/[XYLTRBWH][\d\-\.]+(%|px|pt|vw|vh)?/g); for (var i = 0; i < matches.length; i++) { p = p.replace(matches[i], ""); var c = matches[i].substr(0, 1); var v = matches[i].substr(1); if (c == "X" || c == "L") newP[3] = v; else if (c == "Y" || c == "T") newP[0] = v; else if (c == "R") newP[1] = v; else if (c == "B") newP[2] = v; else if (c == "W") newP[4] = v; else if (c == "H") newP[5] = v; } // Rest are tags, we'll put them all in the [6] position if (p.trim) p = p.trim(); if (p) newP[6] = p; p = newP; } } else { p = p.slice(); // Don't edit the existing array object as it might be observed } for (var i = 0; i < p.length; i++) { if (p[i] !== null && p[i] !== undefined && !p[i].substr) { p[i] = p[i].toString() + "px"; // Convert numbers to px } } return p; }; /// Helper functions window.SvgPath = function (d, width, height, color, strokeWidth) { if (!color) color = "#333"; if (!strokeWidth) strokeWidth = 3; return ''; }; window.ConvertFromASPTime = function (a) { if (a && a.length && a.length > 6 && a.substr(0, 6) == "/Date(") { var dateObj = new Date(parseInt(a.substr(6, a.length - 2))); return dateObj.toISOString(); } return ""; }; window.ConvertToASPTime = function (a) { if (a == "") return ""; var time = new Date(a).getTime(); return "\\/Date(" + time + ")\\/"; }; "use strict"; /* Minify Order(5) */ window.ajax = function () { var obj = this; this.timeout = 30000; this.timeoutChecker = null; this.ajaxObject = null; this.busy = false; this.queue = []; this.currentRequestInfo = null; // url, callback etc. this.retryCount = 0; this.maxRetries = 3; this.cached = {}; this.allowEmptyResponse = false; this.showServerErrors = true; this.saveResults = false; this.cacheResults = false; this.executeScriptTags = true; this.parseJSONResponse = false; this.errorHandler = null; // function(statusCode,responseText) {} this.extraHeaders = []; // [ ["Content-Type", "application/json"] ] this.beforeRequest = null; // function(requestInfo, callBackExecuteRequest(requestInfo) ) {} this.getSetting = function (name, requestInfo) { if (!requestInfo) requestInfo = this.currentRequestInfo; if (requestInfo && requestInfo.extraSettings && requestInfo.extraSettings[name] !== undefined) { return requestInfo.extraSettings[name]; } return this[name]; }; this.initComponent = function () { this.ajaxObject = this.getAvailableAjaxObject(); if (!this.ajaxObject) // could not init any ajax object return; // set event handlers this.ajaxObject.onreadystatechange = function () { if (obj.ajaxObject.readyState !== 4 || obj.ajaxObject.status == 0) return; if (obj.timeoutChecker) { clearTimeout(obj.timeoutChecker); obj.timeoutChecker = null; } if (obj.ajaxObject.status !== 200) { if (obj.ajaxObject.status >= 400) { var errorHandler = obj.getSetting("errorHandler"); if (errorHandler) errorHandler(obj.ajaxObject.status, obj.ajaxObject.responseText); else if (obj.getSetting("showServerErrors")) alert('Server error, returned code: ' + obj.ajaxObject.status); // mostly unrecoverable server error obj.nextRequest(); } else obj.retryLast(); // retry, sometimes IE returns some unusual number for failed requests return; } var responseIsText = (!obj.ajaxObject.responseType || obj.ajaxObject.responseType === "" || obj.ajaxObject.responseType == "text"); try { // Response is required since bad requests can sometimes give an empty response if (responseIsText && obj.ajaxObject.responseText == "" && !obj.getSetting("allowEmptyResponse")) { obj.retryLast(); return; } } catch (r) { obj.retryLast(); // Sometimes the responseText is unreadable, also retry return; } // Everything seems to be ok! var curReq = obj.currentRequestInfo; obj.retryCount = 0; var callBacks = []; if (curReq.callBack) callBacks.push(curReq.callBack); var pageCacheHash = responseIsText && (obj.getSetting("saveResults") || obj.getSetting("cacheResults")) ? "page" + obj.hashString(curReq.get) : ""; if (responseIsText && obj.getSetting("saveResults")) { window.localStorage[pageCacheHash] = obj.ajaxObject.responseText; } if (responseIsText && obj.getSetting("cacheResults")) { if (obj.cached[pageCacheHash] !== undefined && Array.isArray(obj.cached[pageCacheHash])) callBacks = callBacks.concat(obj.cached[pageCacheHash]); // There were possibly multiple calls waiting for this response obj.cached[pageCacheHash] = obj.ajaxObject.responseText; } // check for callback for (var i = 0; i < callBacks.length; i++) { let cb = callBacks[i]; var responseData = null; if (responseIsText) { responseData = obj.ajaxObject.responseText; if (obj.getSetting("parseJSONResponse") && responseData && (responseData.substr(0, 1) == "{" || responseData.substr(0, 1) == "\"" || responseData.substr(0, 1) == "[")) { responseData = JSON.parse(responseData); } } else { responseData = obj.ajaxObject.response; } obj.executeCallBack(cb, responseData, curReq); } obj.nextRequest(); }; this.ajaxObject.onerror = function (error) { var errorHandler = obj.getSetting("errorHandler"); if (errorHandler) errorHandler(0, null); obj.nextRequest(); }; }; this.executeCallBack = function (callBack, responseData, request) { // Do this as a timeout, so it doesn't mess with our ajax code setTimeout(function () { callBack(responseData, request.get, request.post, request.callBackData); }, 1); } this.getAvailableAjaxObject = function () { // Modern browsers if (window.XMLHttpRequest) return new XMLHttpRequest(); // IE Specific if (window.ActiveXObject) { try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (r) { } try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (r) { } } }; this.hashString = function (string) { var hash = 0; if (string.length == 0) return hash; for (var i = 0; i < string.length; i++) { var char = string.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32bit integer } return hash; }; this.clearSaved = function () { for (var item in window.localStorage) { if (item.substr(0, 4) == "page") { delete window.localStorage[item]; } } }; this.clearCached = function () { this.cached = {}; }; //redo current request this.retryLast = function () { if (this.getSetting("saveResults") && window.localStorage["page" + this.hashString(this.currentRequestInfo.get)]) { console.log("Using saved results"); // Use saved result instead if (this.currentRequestInfo.callBack) { // make a copy since the callback is called in timeout (currentRequestInfo can be changed) var curReq = this.currentRequestInfo; var content = window.localStorage["page" + this.hashString(this.currentRequestInfo.get)]; if (this.getSetting("parseJSONResponse") && content && (content.substr(0, 1) == "{" || content.substr(0, 1) == "\"" || content.substr(0, 1) == "[")) { content = JSON.parse(content); } obj.executeCallBack(curReq.callBack, content, curReq); } this.nextRequest(); return; } this.busy = false; this.retryCount++; if (this.retryCount > this.getSetting("maxRetries")) { this.retryCount = 0; this.nextRequest(); return; } if (this.currentRequestInfo) this.do(this.currentRequestInfo.get, this.currentRequestInfo.post, this.currentRequestInfo.callBack, this.currentRequestInfo.callBackData, this.currentRequestInfo.extraSettings); }; // Prepare everything for a next request (and even do them if there is something in the queue) this.nextRequest = function () { this.busy = false; if (this.queue.length == 0) { return; } var nextRequestInfo = this.queue.shift(); this.do(nextRequestInfo.get, nextRequestInfo.post, nextRequestInfo.callBack, nextRequestInfo.callBackData, nextRequestInfo.extraSettings); }; this.do = function (get, post, callBack, callBackData, extraSettings) { if (!this.ajaxObject) { this.initComponent(); } if (extraSettings) { var orig = extraSettings; extraSettings = JSON.parse(JSON.stringify(extraSettings)); // Clone to make sure we don't override any settings for the next request if (orig.errorHandler) extraSettings.errorHandler = orig.errorHandler; } var requestInfo = { get: get, post: post, callBack: callBack, callBackData: callBackData, extraSettings: extraSettings }; var beforeRequest = this.getSetting("beforeRequest", requestInfo); if (beforeRequest) { beforeRequest(requestInfo, function (requestInfo) { if (!requestInfo.extraSettings) requestInfo.extraSettings = {}; requestInfo.extraSettings.beforeRequest = null; obj.do(requestInfo.get, requestInfo.post, requestInfo.callBack, requestInfo.callBackData, requestInfo.extraSettings); }); return; } if (extraSettings && extraSettings.cacheResults) { var pageHash = ["page" + this.hashString(get)]; var content = this.cached[pageHash]; var done = 0; if (content !== undefined && Array.isArray(content)) { // This request is already on its way, add us to the callbacks content.push(callBack); done = 1; } else if (content) { if (callBack) { if (!(typeof post === "string" || post instanceof String) && this.parseJSONResponse !== false && content && (content.substr(0, 1) == "{" || content.substr(0, 1) == "\"" || content.substr(0, 1) == "[")) { content = JSON.parse(content); } obj.executeCallBack(callBack, content, requestInfo); } done = 1; } else { // We will execute this request, so add a temp array all next callback can be added to if (!this.busy) this.cached[pageHash] = []; } if (done) { if (!this.busy) this.nextRequest(); return; } } if (this.busy) { this.queue.push(requestInfo); return; } this.busy = true; this.currentRequestInfo = requestInfo; if (post == null) { this.ajaxObject.open('GET', get, true); } else { this.ajaxObject.open('POST', get, true); } var extraHeaders = this.getSetting("extraHeaders"); for (var i = 0; i < extraHeaders.length; i++) { this.ajaxObject.setRequestHeader(extraHeaders[i][0], extraHeaders[i][1]); } if (this.ajaxObject.responseType !== this.undefined && this.getSetting("responseType")) this.ajaxObject.responseType = this.getSetting("responseType"); this.timeoutChecker = setTimeout(function () { obj.timeoutChecker = null; try { // try to cancel obj.ajaxObject.abort(); } catch (errie) { } obj.retryLast(); }, this.getSetting("timeout")); if (post == null) { this.ajaxObject.send(); // get } else { if (post instanceof FormData) { // Post form data (including file uploads) this.ajaxObject.send(post); return; } else if (!(typeof post === "string" || post instanceof String)) { // We can only put text in our post data post = JSON.stringify(post); if (!this.currentRequestInfo.extraSettings) this.currentRequestInfo.extraSettings = {}; if (this.currentRequestInfo.extraSettings.parseJSONResponse == undefined) this.currentRequestInfo.extraSettings.parseJSONResponse = true; } this.ajaxObject.setRequestHeader("Content-Type", post.length > 0 && post.substr(0, 1) == "{" ? "application/json" : "application/x-www-form-urlencoded"); this.ajaxObject.setRequestHeader("Content-Size", post.length); if (post.length > 0 && post.substr(0, 1) == "{") { post = post.replace(/"\/Date\((\d*)\)\/"/g, "\"\\/Date($1)\\/\""); } this.ajaxObject.send(post); } }; /* Extra handy functions */ this.doAjaxToDiv = function (get, post, divName, extraSettings) { var obj = this; this.do(get, post, function (txt) { document.getElementById(divName).innerHTML = txt; if (obj.getSetting("executeScriptTags")) { obj.executeScripts(txt); } }, null, extraSettings); }; this.doAjaxToDivElement = function (get, post, divElement, extraSettings) { var obj = this; this.do(get, post, function (txt) { divElement.innerHTML = txt; if (obj.getSetting("executeScriptTags")) { obj.executeScripts(txt); } }, null, extraSettings); }; this.doAjaxFormSubmit = function (formElement, callBack, submitButton, extraSettings) { /*if (window.FormData) { // We can use the FormData api var fd = new FormData(formElement); if (submitButton && submitButton.name) { fd.append(fd.name, fd.value); } this.do(formElement.action, fd, callBack, null, extraSettings); return fd; }*/ var inputs = ""; for (var i = 0; i < formElement.length; i++) { if (formElement[i].type == "submit" && submitButton != formElement[i]) continue; if ((formElement[i].type == "checkbox" || formElement[i].type == "radio") && !formElement[i].checked) continue; if (formElement[i].multiple && formElement[i].options && formElement[i].name) { for (var j = 0; j < formElement[i].options.length; j++) { if (formElement[i].options[j].selected && formElement[i].options[j].value) { inputs += "&" + formElement[i].name + "=" + this.URLEncode(formElement[i].options[j].value); } } continue; } if (formElement[i].value && formElement[i].name) { inputs += "&" + formElement[i].name + "=" + this.URLEncode(formElement[i].value); } } this.do(formElement.action, inputs, callBack, null, extraSettings); }; this.doAjaxFormSubmitToDiv = function (formElement, divName, submitButton, extraSettings) { var obj = this; this.doAjaxFormSubmit(formElement, function (pTxt) { document.getElementById(divName).innerHTML = pTxt; if (obj.getSetting("executeScriptTags")) { obj.executeScripts(pTxt); } }, submitButton, extraSettings); }; this.doFileUpload = function (url, nameFormField, blobOrFileElement, blobFileName, callBack, callBackData, extraSettings) { var fd = new FormData(); fd.append(nameFormField, blobOrFileElement, blobFileName); this.do(url, fd, callBack, callBackData, extraSettings); }; this.executeScripts = function (data) { while (data.indexOf("= 0 && data.indexOf("") >= 0) { var script = data.substr(data.indexOf("") + 1); script = script.substring(0, script.indexOf("")); try { eval(script); } catch (errie) { console.log("Error executing script:"); console.log(errie); } data = data.substr(data.indexOf("") + 9); } }; // URL Encoding script this.gethex = function (decimal) { var hexchars = "0123456789ABCDEFabcdef"; return "%" + hexchars.charAt(decimal >> 4) + hexchars.charAt(decimal & 0xF); }; this.URLEncode = function (decoded) { var unreserved = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.~"; var encoded = ""; for (var i = 0; i < decoded.length; i++) { var ch = decoded.charAt(i); if (unreserved.indexOf(ch) != -1) { encoded = encoded + ch; } else { var charcode = decoded.charCodeAt(i); if (charcode < 128) { encoded = encoded + this.gethex(charcode); } else if (charcode > 127 && charcode < 2048) { encoded = encoded + this.gethex((charcode >> 6) | 0xC0); encoded = encoded + this.gethex((charcode & 0x3F) | 0x80); } else if (charcode > 2047 && charcode < 65536) { encoded = encoded + this.gethex((charcode >> 12) | 0xE0); encoded = encoded + this.gethex(((charcode >> 6) & 0x3F) | 0x80); encoded = encoded + this.gethex((charcode & 0x3F) | 0x80); } else if (charcode > 65535) { encoded = encoded + this.gethex((charcode >> 18) | 0xF0); encoded = encoded + this.gethex(((charcode >> 12) & 0x3F) | 0x80); encoded = encoded + this.gethex(((charcode >> 6) & 0x3F) | 0x80); encoded = encoded + this.gethex((charcode & 0x3F) | 0x80); } } } return encoded; }; }; window.Ajax = new ajax();"use strict"; /* Minify Order(5) */ // Extra array functions, inspired by LinqToSql Array.prototype.Where = function (func) { var items = []; for (var i = 0; i < this.length; i++) { if (func(this[i], i)) items.push(this[i]); } return items; }; Array.prototype.First = function (func) { if (!func) return this[0]; for (var i = 0; i < this.length; i++) { if (func(this[i], i)) return this[i]; } }; Array.prototype.Take = function (count) { var items = []; for (var i = 0; i < this.length && i < count; i++) { items.push(this[i]); } return items; }; Array.prototype.Skip = function (count) { var items = []; for (var i = count; i < this.length; i++) { items.push(this[i]); } return items; }; Array.prototype.OrderBy = function (func) { if (!func) func = function (a) { return a; }; return this.sort(function (a, b) { var a2 = func(a), b2 = func(b); if (a2 === null || a2 === undefined) a2 = 0; if (b2 === null || b2 === undefined) b2 = 0; if (a2.localeCompare && b2.localeCompare) { // Both are string return a2.localeCompare(b2); } else if (a2.localeCompare) { // Only a2 is a string return a2.localeCompare(b2.toString()); } else if (b2.localeCompare) { return a2.toString().localeCompare(b2); // Only b2 is a string } return a2 - b2; }); }; Array.prototype.OrderByDesc = function (func) { return this.OrderBy(func).reverse(); }; Array.prototype.Select = function (func) { var items = []; for (var i = 0; i < this.length; i++) items.push(func(this[i], i)); return items; }; Array.prototype.Max = function (func) { if (!func) func = function (a) { return a; }; var highest = null; for (var i = 0; i < this.length; i++) { var value = func(this[i], i); if (highest == null || value > highest) highest = value; } return highest; }; Array.prototype.Min = function (func) { if (!func) func = function (a) { return a; }; var lowest = null; for (var i = 0; i < this.length; i++) { var value = func(this[i], i); if (lowest == null || value < lowest) lowest = value; } return lowest; }; Array.prototype.Average = function (func) { if (!func) func = function (a) { return a; }; var average = null; var averageCount = 0; for (var i = 0; i < this.length; i++) { var value = func(this[i], i); average = (average * averageCount + value) / (averageCount + 1); averageCount++; } return average; }; Array.prototype.Unique = function (func) { if (!func) func = function (a) { return a; }; var items = []; var uniqueKeys = []; for (var i = 0; i < this.length; i++) { var key = func ? func(this[i], i) : this[i]; if (uniqueKeys.indexOf(key) < 0) { items.push(this[i]); uniqueKeys.push(key); } } return items; }; Array.prototype.Randomize = function () { var items = this.slice(); var currentIndex = items.length, temporaryValue, randomIndex; while (0 !== currentIndex) { randomIndex = Math.floor(Math.random() * currentIndex); currentIndex -= 1; temporaryValue = items[currentIndex]; items[currentIndex] = items[randomIndex]; items[randomIndex] = temporaryValue; } return items; };/* Minify Order(50) */ if (!window.Svg) { window.Svg = {}; } var s=""; window.Svg["Icons"]={ "Settings":s+"", "Home":s+"", "Hamburger":s+"", "Close":s+"", "Resize":s+"", "Move":s+"", "Copy":s+"", "User":s+"", "Meter":s+"", "Barcode":s+"", "Route":s+"", "Find":s+"", "Popout":s+"", "Download":s+"", "Upload":s+"", "Warning":s+"", "Help":s+"", "Message":s+"", "Favorite":s+"", "Dashboard":s+"", "Sync":s+"", "SyncError":s+"", "ErrorAlarrt":s+"", "Report":s+"", "Back":s+"", "Next":s+"", "OK":s+"", "Cancel":s+"", "Text":s+"", "Table":s+"", "Gauge":s+"", "Donut":s+"", "Left":s+"", "Right":s+"", "Up":s+"", "TextIncrease":s+"", "TextDecrease":s+"", "Down":s+"", "TextItalic":s+"", "TextBold":s+"", "TextUnderline":s+"", "TextAlignLeft":s+"", "TextAlignRight":s+"", "TextAlignCenter":s+"", "TextList":s+"", "TextIndent":s+"", "TextOutdent":s+"", "TextQuoteBlock":s+"", "TextCodeBlock":s+"", "Card":s+"", "Link":s+"", "Image":s+"", "TextParagraph":s+"", "TextHeader1":s+"", "TextHeader2":s+"", "TextHeader3":s+"", };"use strict"; window.TK.AjaxList = { _: "div", Url: "", Post: null, IdField: "Id", Template: {}, AjaxSettings: {}, UseVariable: null, Init: function () { var obj = this; if (this.Url) { Ajax.do(this.Url, this.Post, function (response) { obj.AddMultiple(obj.Template, JSON.parse(response), obj.IdField, obj.UseVariable); }, undefined, this.AjaxSettings); } }, Refresh: function () { this.Init(); } };/* Minify Order(100) */ // Component to store client side data with several fallbacks: IndexedDB, LocalStorage // This component has the same methods as TK.ServerStorage TK.ClientStorage = { _: "component", Prefix: "TKStorage_", Store: function (path, blobOrByteArrayOrStringContents, isStoredCallBack) { var obj = this; // We will convert all byte arrays to blobs, which will later be converted to data urls if ((window.Uint8Array && blobOrByteArrayOrStringContents instanceof Uint8Array) || (window.Uint16Array && blobOrByteArrayOrStringContents instanceof Uint16Array)) { blobOrByteArrayOrStringContents = new Blob([blobOrByteArrayOrStringContents], { 'type': 'application/octet-stream' }); } // We will convert all blobs to data urls if ((window.Blob && blobOrByteArrayOrStringContents instanceof Blob) || (window.File && blobOrByteArrayOrStringContents instanceof File)) { var reader = new FileReader(); reader.onload = function () { if (reader.result) { obj.Store(path, reader.result, isStoredCallBack); } }; reader.readAsDataURL(blobOrByteArrayOrStringContents); return; } this.GetIndexedDBInstance(function (db) { if (!db) { // use local storage as fallback var saved = false; try { window.localStorage[obj.Prefix + path] = blobOrByteArrayOrStringContents; saved = true; } catch (ex) { } // not allowed, supported or quota reached if (isStoredCallBack) isStoredCallBack(saved); return; } var transaction = db.transaction(["data"], "readwrite"); transaction.oncomplete = function (event) { if (isStoredCallBack) isStoredCallBack(true); }; transaction.onerror = function (event) { console.log("IndexedDB transaction onerror: " + event); }; var store = transaction.objectStore("data"); var request = store.get(path); request.onerror = function (event) { console.log('IndexedDB objectStore get onerror: ' + event); }; request.onsuccess = function (event) { if (request.result === undefined) { // Add store.add({ key: path, data: blobOrByteArrayOrStringContents }); } else { // Update request.result.data = blobOrByteArrayOrStringContents; store.put(request.result); } }; }); }, Retrieve: function (path, asBlob, callBack) { if (!callBack) return; var dataUrlToBlob = function (dataUrl) { var parts = dataUrl.split(','), mime = parts[0].match(/:(.*?);/)[1] if (parts[0].indexOf('base64') != -1) { var data = atob(parts[1]); var i = data.length; var byteArray = new Uint8Array(data.length); while (i--) byteArray[i] = data.charCodeAt(i); return new Blob([byteArray], { type: mime }); } else { return new Blob([decodeURIComponent(parts[1])], { type: mime }); } }; var obj = this; this.GetIndexedDBInstance(function (db) { if (!db) { // use local storage as fallback if (!window.localStorage[obj.Prefix + path]) { callBack(); } else if (!asBlob) { callBack(window.localStorage[obj.Prefix + path]); } else if (request.result.data.length < 5 && request.result.data.substr(0, 5) != "data:") { callBack(new Blob([window.localStorage[obj.Prefix + path]], { type: 'text/plain' })); } else { callBack(dataUrlToBlob(window.localStorage[obj.Prefix + path])); } return; } var transaction = db.transaction(["data"]); var store = transaction.objectStore("data"); var request = store.get(path); request.onerror = function (event) { console('IndexedDB getAllKeys onerror: ' + event); }; request.onsuccess = function () { if (!request.result) { callBack(); } else if (!asBlob) { callBack(request.result.data); } else if (request.result.data.length < 5 && request.result.data.substr(0, 5) != "data:") { callBack(new Blob([request.result.data], { type: 'text/plain' })); } else { callBack(dataUrlToBlob(request.result.data)); // Data url, turn into blob } }; }); }, GetUrl: function (path, callBack) { if (window.URL && window.URL.createObjectURL) { // Return a blob url if supported this.Retrieve(path, true, function (resp) { if (!resp) { callBack(); } else { callBack(window.URL.createObjectURL(resp)); } }); } else { // Return a data url as fallback this.Retrieve(path, false, function (resp) { if (!resp) { callBack(); } else if (resp.length < 5 || resp.substr(0, 5) != "data:") { callBack("data:text/plain;base64," + btoa(resp)); } else { callBack(resp); } }); } }, Delete: function (path, isDeletedCallBack) { var obj = this; this.GetIndexedDBInstance(function (db) { if (!db) { if (window.localStorage[obj.Prefix + path]) delete window.localStorage[obj.Prefix + path]; if (isDeletedCallBack) isDeletedCallBack(true); return; } var transaction = db.transaction(["data"], "readwrite"); transaction.oncomplete = function (event) { if (isDeletedCallBack) isDeletedCallBack(true); }; transaction.onerror = function (event) { console.log("IndexedDB transaction onerror: " + event); if (isDeletedCallBack) isDeletedCallBack(false); }; var store = transaction.objectStore("data"); var request = store.get(path); request.onerror = function (event) { console.log('IndexedDB objectStore get onerror: ' + event); if (isDeletedCallBack) isDeletedCallBack(false); }; request.onsuccess = function (event) { if (request.result !== undefined) { // Found: Delete store.delete(path); } }; }); }, List: function (pathsCallBack) { if (!pathsCallBack) return; var obj = this; this.GetIndexedDBInstance(function (db) { if (!db) { // use local storage as fallback var keys = []; for (var name in window.localStorage) { if (name.length > obj.Prefix && name.substr(0, obj.Prefix.length) == obj.Prefix) { keys.push(name.substr(obj.Prefix.length)); } } pathsCallBack(keys); return; } var transaction = db.transaction(["data"]); var store = transaction.objectStore("data"); var request = store.getAllKeys(); request.onerror = function (event) { console('IndexedDB getAllKeys onerror: ' + event); }; request.onsuccess = function () { pathsCallBack(request.result); }; }); }, // Internal GetIndexedDBInstance: function (callBack) { if (this.IndexedDB) { callBack(this.IndexedDB); return; } var obj = this; var db = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; if (!db) { callBack(); return; } var request = window.indexedDB.open(this.Prefix, 1); request.onerror = function (event) { console.log("IndexedDB onerror: " + request.errorCode); callBack(); }; request.onupgradeneeded = function (event) { obj.IndexedDB = event.target.result; event.target.transaction.oncomplete = function () { callBack(obj.IndexedDB); }; obj.IndexedDB.createObjectStore("data", { keyPath: "key" }); }; request.onsuccess = function (event) { obj.IndexedDB = event.target.result; callBack(obj.IndexedDB); }; } };"use strict"; window.TK.DragDrop = { _: "div", className: "toolkitDragDrop", Init: function () { this.style.position = "relative"; }, Type: "", Threshold: 10, ElementTemplate: null, // When set, the current element will not be moved, but a new element will be created by this. This will also be used as hover template if the HoverTemplate property is not set. HoverTemplate: null, // When set, this element will be used for a hover-preview. If both are set, the ElementTemplate will be passed as Template to the .Drop method Dropped: function (droppedInContainer, addedElement) { }, // Extra callback ontouchstart: function (e) { this.onmousedown(e.touches[0]); e.stopPropagation(); }, onmousedown: function (e) { var startX, startY; try { startX = e.clientX; startY = e.clientY; } catch (errie) { var e2 = window.event; startX = e2.clientX; startY = e2.clientY; } var obj = this; var origObj = this; var origParent = this.parentNode; var myPosition = this.getBoundingClientRect(); var cursorOffsetX = startX - myPosition.left; var cursorOffsetY = startY - myPosition.top; var positions = []; var newTop, newLeft; var first = true; var elementOrTemplateToAdd = null; var passedThreshold = false; var aboveContainer = null; var aboveContainerPosition = null; window.onmousemove = function (e) { var x, y; try { x = e.clientX; y = e.clientY; } catch (errie) { var e2 = window.event; x = e2.clientX; y = e2.clientY; } if (!passedThreshold && (Math.abs(startX - x) < obj.Threshold && Math.abs(startY - y) < obj.Threshold)) return; passedThreshold = true; if (first) { if (obj.HoverTemplate) { obj = obj.Add(obj.HoverTemplate); origParent = null; } else if (obj.ElementTemplate) { obj = obj.Add(obj.ElementTemplate); origParent = null; } obj.style.position = "fixed"; document.body.appendChild(obj); elementOrTemplateToAdd = (origObj.ElementTemplate && origObj.HoverTemplate ? origObj.ElementTemplate : obj); var containers = document.querySelectorAll(".toolkitDragDropContainer"); for (var i = 0; i < containers.length; i++) { if (containers[i].DropFilter && (!obj.Type || containers[i].DropFilter.indexOf(obj.Type) < 0)) continue; if (containers[i].CanDrop && !containers[i].CanDrop(elementOrTemplateToAdd)) continue; containers[i].className += " toolkitDragDropContainerArea"; positions.push({ Element: containers[i], Position: containers[i].getBoundingClientRect() }); } first = false; } else { for (var i = 0; i < positions.length; i++) { positions[i].Position = positions[i].Element.getBoundingClientRect(); } } newTop = (y - startY) + myPosition.top; newLeft = (x - startX) + myPosition.left; var cursorX = newLeft + cursorOffsetX; var cursorY = newTop + cursorOffsetY; var found = []; for (var i = 0; i < positions.length; i++) { if (positions[i].Position.left <= cursorX && positions[i].Position.right >= cursorX && positions[i].Position.top <= cursorY && positions[i].Position.bottom >= cursorY) { if (aboveContainer != positions[i].Element) { if (aboveContainer != null) { aboveContainer.className = aboveContainer.className.replace(/toolkitDragDropContainerHover/g, ""); } aboveContainer = positions[i].Element; aboveContainerPosition = positions[i].Position; aboveContainer.className += " toolkitDragDropContainerHover"; } positions[i].TotalSize = positions[i].Position.width + positions[i].Position.height; found.push(positions[i]); } } if (found.length == 0 && aboveContainer != null) { aboveContainer.className = aboveContainer.className.replace(/toolkitDragDropContainerHover/g, ""); aboveContainer = null; } else if (found.length > 0) { // We will pick the smallest element hovered over, as that will often work fine for nested elements var newAboveContainer = found.OrderBy(function (a) { return a.TotalSize }).First().Element; if (aboveContainer != newAboveContainer) { if (aboveContainer != null) { aboveContainer.className = aboveContainer.className.replace(/toolkitDragDropContainerHover/g, ""); } aboveContainer = newAboveContainer; aboveContainerPosition = positions[i].Position; aboveContainer.className += " toolkitDragDropContainerHover"; } } obj.style.top = newTop + "px"; obj.style.left = newLeft + "px"; }; window.onmouseup = function () { // If above container element, append to container element, or else move back obj.style.left = ""; obj.style.top = ""; obj.style.position = "relative"; for (var i = 0; i < positions.length; i++) { if (positions[i].Element.className) positions[i].Element.className = positions[i].Element.className.replace(/toolkitDragDropContainerArea/g, ""); } if (aboveContainer) { aboveContainer.className = aboveContainer.className.replace(/toolkitDragDropContainerHover/g, ""); var createdElement = null; if (aboveContainer.Drop) { createdElement = aboveContainer.Drop(elementOrTemplateToAdd, newLeft - aboveContainerPosition.left, newTop - aboveContainerPosition.top); } if (!createdElement && elementOrTemplateToAdd == obj) { if (origObj.ElementTemplate && origObj.HoverTemplate) { // Both are set, but not handled. Create a new element to add // Remove hover element if (obj.Remove) obj.Remove(); else obj.parentNode.removeChild(obj); if (aboveContainer.Add) { // Container is a toolkit element, use the Add method aboveContainer.Add(origObj.ElementTemplate); obj = null; // Mark as finished } else { obj = TK.Initialize(origObj.ElementTemplate); // Initialize and append to this container } } else if (!origObj.ElementTemplate && origObj.HoverTemplate) { // Just the hover is set, add the original object to the new parent // Remove hover element if (obj.Remove) obj.Remove(); else obj.parentNode.removeChild(obj); obj = origObj; } if (obj != null) { aboveContainer.appendChild(obj); obj.Parent = aboveContainer; var newElementId = Math.random(); if (origParent && origParent != aboveContainer && origParent.Elements) { for (var id in origParent.Elements) { if (origParent.Elements[id] == obj) { delete origParent.Elements[id]; newElementId = id; break; } } } if (aboveContainer.Elements) { aboveContainer.Elements[newElementId] = obj; } } } if (origObj.Dropped) { origObj.Dropped(aboveContainer, createdElement); } } else if (passedThreshold && origParent) { origParent.appendChild(obj); } else if (passedThreshold) { // Remove hover element if (obj.Remove) obj.Remove(); else obj.parentNode.removeChild(obj); } window.onmousemove = null; window.onmouseup = null; window.onselectstart = null; window.ontouchmove = null; window.ontouchend = null; }; window.ontouchmove = function (e) { if (window.onmousemove) window.onmousemove(e.touches[0]); e.stopPropagation(); }; window.ontouchend = function (e) { if (window.onmouseup) window.onmouseup(); e.stopPropagation(); }; window.onselectstart = function () { return false; }; if (e && e.preventDefault) e.preventDefault(); else window.event.returnValue = false; } };"use strict"; window.TK.Event = { _: "component", Subscribe: [], Init: function () { if (!window.TK.EventHandlers) { window.TK.EventHandlers = []; } window.TK.EventHandlers.push(this); }, Destroy: function () { var i = window.TK.EventHandlers.indexOf(this); if (i >= 0) { window.TK.EventHandlers.splice(i, 1); } }, Send: function (eventType, eventData) { if (!window.TK.EventHandlers) return; for (var i = 0; i < window.TK.EventHandlers.length; i++) { if (window.TK.EventHandlers[i].Subscribe.indexOf(eventType) >= 0) { window.TK.EventHandlers[i].Receive(eventType, eventData); } } }, Receive: function (eventType, eventData) { /* Callback */ } };"use strict"; window.TK.Form = { _: "form", className: "toolkitForm", Model: null, DefaultModel: null, ModelProperty: "Model", Fields: null, // fieldName: { Required: false, DisplayName: "Field1", Type: "number", Template: {..} } SortByFields: false, IgnoreRest: false, CurrentFields: null, LabelWidth: 0, // when non-zero number: width of labels in px, when string: width of label with unit (ex. 50%), when 0/null: the labels will be displayed as 'block' and fill the complete line. SaveButtonText: "Save", AutoSave: false, RequiredText: "The following fields are required: ", ApplyToModelDirectly: false, RemoveValueOfNotVisibleFields: true, // If false, a field hidden by IsVisible will still keep its value when saving CustomValidation: function (model, callBackResult) { callBackResult([]); }, // Callback with an array of errors. If the array is empty or undefined, the validation is seen as passed. SubmitResult: function (isSaved, errors) { }, // Callback after .onsubmit and .Save or .RenderErrors is called DefaultTemplates: { text: { _: "input", type: "text", Init: function () { this.value = this.Data; }, GetValue: function () { return this.value; } }, textarea: { _: "textarea", Init: function () { this.value = this.Data; }, GetValue: function () { return this.value; } }, textarray: { _: "div", className: "textArray", TemplateEditableDiv: { className: "textArrayItem", Data: null, PlaceHolder: null, Init: function () { this.Elements.Content.disabled = this.Parent ? this.Parent.disabled : false; if (this.PlaceHolder) { this.Elements.Content.value = this.PlaceHolder; this.className += " newItem"; this.Elements.RemoveButton.style.display = "none"; } else { this.Elements.Content.value = this.Data; } }, Elements: { ContentSize: { _: "div", style: { position: "absolute", height: "0px", overflow: "hidden", maxWidth: "70%" } }, Content: { _: "input", onfocus: function () { if (this.Parent.PlaceHolder) { this.value = ""; delete this.Parent.PlaceHolder; this.Parent.className = "textArrayItem"; this.Parent.Elements.RemoveButton.style.display = ""; this.Parent.OnUse(); } }, oninput: function () { this.Parent.Elements.ContentSize.textContent = this.value; this.style.width = (this.Parent.Elements.ContentSize.offsetWidth + 5) + "px"; }, onblur: function () { if (this.value == "") this.Parent.Remove(); } }, RemoveButton: { innerHTML: "X", tabIndex: -1, onclick: function () { this.Parent.Remove(); } } }, OnUse: function () { } }, Init: function () { for (var i = 0; i < this.Data.length; i++) { this.Add({ _: this.TemplateEditableDiv, Data: this.Data[i] }); } this.AddNewItem(); }, AddNewItem: function () { var obj = this; this.Add({ _: this.TemplateEditableDiv, Data: null, PlaceHolder: "New item", OnUse: function () { obj.AddNewItem(); } }); this.ResizeAllElements(); }, ResizeAllElements: function () { var obj = this; setTimeout(function () { for (var name in obj.Elements) { if (!obj.Elements[name].Elements) continue; obj.Elements[name].Elements.Content.oninput(); } }, 1); }, GetValue: function () { var items = this.Elements.ToArray(); var value = []; for (var i = 0; i < items.length; i++) { if (items[i].Elements.Content.value != "" && !items[i].PlaceHolder) value.push(items[i].Elements.Content.value); } return value; } }, number: { _: "input", type: "number", step: "any", className: "digits", Init: function () { this.value = this.Data; }, GetValue: function () { if (this.value == "") return null; return parseFloat(this.value); } }, password: { _: "input", type: "password", Init: function () { this.value = this.Data; }, GetValue: function () { return this.value; } }, boolean: { _: "input", type: "checkbox", Init: function () { this.checked = this.Data; this.Parent.Elements.DataLabel.insertBefore(this, this.Parent.Elements.DataLabel.childNodes[0]); // Put the text behind the checkbox and make the text clickable if (this.Parent.Elements.DataLabel.style.width) { // Insert dummy container in the place where the label used to be var dummy = this.Add({ style: { display: "inline-block", width: this.Parent.Elements.DataLabel.style.width } }); this.Parent.insertBefore(dummy, this.Parent.Elements.DataLabel); this.Parent.Elements.DataLabel.style.width = "auto"; } }, GetValue: function () { return this.checked; } }, select: { _: "select", Init: function () { if (!this.DataSettings.Options) return; for (var i = 0; i < this.DataSettings.Options.length; i++) { this.appendChild(new Option(this.DataSettings.Options[i].Text, this.DataSettings.Options[i].Value.toString())); } if (this.Data) this.value = this.Data.toString(); else this.value = this.DataSettings.ValueIsText ? "" : 0; }, GetValue: function () { return this.DataSettings.ValueIsText ? this.value : this.value !== null && this.value !== undefined ? parseInt(this.value) : 0; } }, ajaxselect: { _: "select", Init: function () { this.Refresh(); }, Refresh: function () { var obj = this; Ajax.do(this.DataSettings.Url, null, function (response) { obj.Values = JSON.parse(response); if (obj.DataSettings.Options && obj.DataSettings.Options.length > 0) { obj.Values = obj.DataSettings.Options.concat(obj.Values); } obj.GenerateOptions(); }, null, { cacheResults: true }); }, GenerateOptions: function () { for (var i = 0; i < this.Values.length; i++) { var name = this.DataSettings.GetName ? this.DataSettings.GetName(this.Values[i]) : this.Values[i].Name; var value = this.DataSettings.GetValue ? this.DataSettings.GetValue(this.Values[i]) : this.Values[i].Id; var optionEle = new Option(name.toString(), value.toString()); optionEle.ValueObj = this.Values[i]; this.appendChild(optionEle); } if (this.Data !== undefined && this.Data !== null) { this.value = this.Data.toString(); // Always set a string for compatibility reasons // Set Data to null so GetValue will return value from here on this.Data = null; } else this.value = this.DataSettings.ValueIsText ? "" : "0"; }, GetValue: function () { // When Data is set and options are being retrieved we can return the Data. // The user was not able to change the value up to this point so the Data is accurate. if (this.Data !== null && this.Data !== undefined) { return this.Data; } else if (this.value !== null && this.value !== undefined) { return this.DataSettings.ValueIsText ? this.value.toString() : parseInt(this.value); } return this.DataSettings.ValueIsText ? "" : 0; } }, date: { _: "input", type: "date", Init: function () { this.value = this.Data; }, GetValue: function () { return this.value; } }, color: { _: "input", type: "color", Init: function () { this.value = this.Data; }, GetValue: function () { return this.value; } }, datetime: { _: "input", type: "text", Init: function () { this.value = this.Data; }, GetValue: function () { return this.value; } }, form: { _: "div", className: "subForm", Init: function () { var dataSettings = this.DataSettings; if (this.LinkedData !== undefined && dataSettings.LinkSettings && dataSettings.LinkSettings[this.LinkedData]) { dataSettings = dataSettings.LinkSettings[this.LinkedData]; } this.Add({ _: TK.Form, SaveButtonText: null, Model: this.Data, DefaultModel: dataSettings.DefaultModel, Fields: dataSettings.Fields, IgnoreRest: dataSettings.IgnoreRest, SortByFields: dataSettings.SortByFields, // Init: dataSettings.Init }, "Form"); }, GetValue: function (errors) { return this.Elements.Form.GetModel(errors); } }, forms: { _: "div", className: "subForms", Forms: null, Init: function () { this.Forms = []; var obj = this; if (this.DataSettings.NewItem) { this.Add({ _: "button", innerHTML: this.DataSettings.AddButtonText ? this.DataSettings.AddButtonText : "Add", className: "addButton", type: "button", onclick: function () { obj.Forms.push(obj.Add({ _: TK.Form, Model: JSON.parse(JSON.stringify(obj.DataSettings.NewItem)), // deepcopy SaveButtonText: null, Fields: obj.DataSettings.Fields, IgnoreRest: obj.DataSettings.IgnoreRest, SortByFields: obj.DataSettings.SortByFields, // Init: obj.DataSettings.Init, Elements: { RemoveButton: { innerHTML: obj.DataSettings.RemoveButtonText ? obj.DataSettings.RemoveButtonText : "Remove", className: "removeButton", onclick: function () { this.Parent.Remove(); } } } })); if (obj.onchange) obj.onchange(); } }); } if (!this.Data) return; for (var i = 0; i < this.Data.length; i++) { this.Forms.push(this.Add({ _: TK.Form, Model: this.Data[i], SaveButtonText: null, Fields: this.DataSettings.Fields, IgnoreRest: this.DataSettings.IgnoreRest, SortByFields: this.DataSettings.SortByFields, //Init: this.DataSettings.Init, Elements: { RemoveButton: { innerHTML: this.DataSettings.RemoveButtonText ? this.DataSettings.RemoveButtonText : "Remove", className: "removeButton", onclick: function () { this.Parent.Remove(); } } } })); } }, GetValue: function (errors) { var newObjs = []; for (var i = 0; i < this.Forms.length; i++) { if (!this.Forms[i].Parent) continue; // This form is removed newObjs.push(this.Forms[i].GetModel(errors)); } return newObjs; } } }, Init: function () { var obj = this; this.CurrentFields = {}; var tmpFields = {}; var model = this[this.ModelProperty]; if (!model && this.DefaultModel) model = this.DefaultModel; else if (!model) return; if (this.Templates) { for (var name in this.Templates) { this.DefaultTemplates[name] = this.Templates[name]; } } var callIsVisible = false; for (var name in model) { var type = this.Fields && this.Fields[name] && this.Fields[name].Type ? this.Fields[name].Type : typeof model[name]; if (this.IgnoreRest && (!this.Fields || !this.Fields[name])) type = "ignore"; var getField = function (fieldName, fallBack) { if (fallBack == undefined) fallBack = null; return obj.Fields && obj.Fields[name] && obj.Fields[name][fieldName] ? obj.Fields[name][fieldName] : fallBack; }; if (type != "ignore") { var defaultTemplate = this.DefaultTemplates[type] ? this.DefaultTemplates[type] : this.DefaultTemplates.text; var isRequired = getField("Required", false); var row = { style: { }, className: "fieldRow field-" + name + (isRequired ? " fieldRequired" : "") + " " + (getField("Inline") ? "inlineBlock" : ""), Elements: { DataLabel: { innerHTML: getField("DisplayName",name), style: {} }, DataField: { _: getField("Template", defaultTemplate), _Self: true, /* required: isRequired, */ placeholder: getField("PlaceHolder",""), Data: model[name], DataName: name, LinkedData: getField("LinkField") ? model[getField("LinkField")] : null, DataSettings: (this.Fields && this.Fields[name] ? this.Fields[name] : null), onfocus: getField("onfocus"), onblur: getField("onblur"), onchange: getField("onchange"), disabled: getField("disabled", false), readOnly: getField("readOnly"), IsVisible: getField("IsVisible"), //Init: (this.Fields && this.Fields[name] && this.Fields[name].Init ? this.Fields[name].Init : undefined), Form: this } } }; if (this.LabelWidth) { row.Elements.DataLabel.style.display = "inline-block"; row.Elements.DataLabel.style.width = this.LabelWidth + (this.LabelWidth.substr ? "" : "px"); } if (this.Fields && this.Fields[name]) { if (this.Fields[name].Width) { row.className += " withWidth"; row.style.width = this.Fields[name].Width; } if (this.Fields[name].IsVisible) { row.style.display = this.Fields[name].IsVisible(model) ? "" : "none"; callIsVisible = true; } if (this.Fields[name].LabelWidth) { row.Elements.DataLabel.style.display = "inline-block"; row.Elements.DataLabel.style.width = this.LabelWidth + (this.LabelWidth.substr ? "" : "px"); } } row.Elements.DataField.origOnBlur = row.Elements.DataField.onblur; row.Elements.DataField.onblur = function () { if (this.origOnBlur) this.origOnBlur(); if (obj.AutoSave) { obj.onsubmit(); } }; row.Elements.DataField.origOnchange = row.Elements.DataField.onchange; row.Elements.DataField.onchange = function () { if (this.origOnchange) this.origOnchange(); var curModel = null; for (var name in obj.CurrentFields) { if (obj.CurrentFields[name] && obj.CurrentFields[name].DataSettings && obj.CurrentFields[name].DataSettings.LinkField && obj.CurrentFields[name].DataSettings.LinkField == this.DataName) { obj.CurrentFields[name].LinkedData = this.GetValue(); obj.CurrentFields[name].Clear(); obj.CurrentFields[name].Init(); } if (obj.CurrentFields[name] && obj.CurrentFields[name].IsVisible && obj.CurrentFields[name].Parent && obj.CurrentFields[name].Parent.style) { // Get model and call obj.CurrentFields[name].DataSettings.IsVisible(model); if (!curModel) curModel = obj.GetModel([]); obj.CurrentFields[name].Parent.style.display = obj.CurrentFields[name].IsVisible(curModel) ? "" : "none"; } } if (obj.AutoSave) { obj.onsubmit(); } }; if (this.SortByFields) { tmpFields[name] = row; } else { var rowObj = this.Add(row); this.CurrentFields[name] = rowObj.Elements.DataField; } } else { this.CurrentFields[name] = "ignore"; } } var parent = this; if (this.SortByFields && this.Fields) { for (var fieldName in this.Fields) { if (this.Fields[fieldName].Type == "section") { parent = this.Add({ _: "fieldset", Elements: { Legend: { _: "legend", innerHTML: this.Fields[fieldName].DisplayName } } }); } if (!tmpFields[fieldName]) { if (this.Fields[fieldName]._) // This is just a template parent.Add(this.Fields[fieldName], fieldName); continue; } var rObj = parent.Add(tmpFields[fieldName]); this.CurrentFields[fieldName] = rObj.Elements.DataField; } } if (callIsVisible) { // There is at least 1 IsVisible function, so we'll get the model and call all methods now the elements are actually created var curModel = this.GetModel([]); for (var name in this.CurrentFields) { if (this.CurrentFields[name] && this.CurrentFields[name].IsVisible && this.CurrentFields[name].Parent && this.CurrentFields[name].Parent.style) { this.CurrentFields[name].Parent.style.display = this.CurrentFields[name].IsVisible(curModel) ? "" : "none"; } } } if (this.SaveButtonText) { this.Add({ _: "button", type: "submit", innerHTML: this.SaveButtonText }, "SaveButton"); } }, Save: function (obj) { }, GetModel: function (errors, applyToModelDirectly) { if (applyToModelDirectly === undefined) applyToModelDirectly = this.ApplyToModelDirectly; var model = this[this.ModelProperty]; var newObj = applyToModelDirectly ? model : {}; if (applyToModelDirectly) { // Check for errors first var tmpErrorList = []; var tmpModel = this.GetModel(tmpErrorList, false); if (tmpErrorList.length > 0) { for (var i = 0; i < tmpErrorList.length; i++) { errors.push(tmpErrorList[i]); } return tmpModel; } } for (var name in this.CurrentFields) { if (this.CurrentFields[name] == "ignore") { if (!applyToModelDirectly && model != null) newObj[name] = model[name]; } else { if (this.CurrentFields[name].IsVisible && this.RemoveValueOfNotVisibleFields && this.CurrentFields[name].Parent && this.CurrentFields[name].Parent.style && this.CurrentFields[name].Parent.style.display == "none") { if (applyToModelDirectly && newObj != null) // Set to null, otherwise don't include it in the new model newObj[name] = null; continue; } newObj[name] = this.CurrentFields[name].GetValue(errors); var hasError = false; if (errors) { if (this.Fields && this.Fields[name] && this.Fields[name].Required && (newObj[name] === null || newObj[name] === "")) { errors.push(this.Fields && this.Fields[name] && this.Fields[name].DisplayName ? this.Fields[name].DisplayName : name); hasError = true; this.CurrentFields[name].Parent.className += " fieldError"; } } if (!hasError) { this.CurrentFields[name].Parent.className = this.CurrentFields[name].Parent.className.replace("fieldError", ""); } } } return newObj; }, RenderErrors: function (errors, textBefore) { if (this.Elements.ErrorText) { this.Elements.ErrorText.innerHTML = textBefore + errors.join(", "); } else { this.Add({ innerHTML: textBefore + errors.join(", "), className: "validationError" }, "ErrorText"); } }, onsubmit: function () { if (this.IsCurrentlySubmitting) return false; this.IsCurrentlySubmitting = true; var obj = this; var errors = []; var newObj = this.GetModel(errors); if (errors.length == 0) { if (this.CustomValidation) { this.CustomValidation(newObj, function (customErrors) { obj.IsCurrentlySubmitting = false; if (!customErrors || customErrors.length == 0) { obj.Save(newObj); } else { obj.RenderErrors(customErrors, ""); } if (obj.SubmitResult) obj.SubmitResult(!customErrors || customErrors.length == 0, customErrors); }); } else { this.IsCurrentlySubmitting = false; this.Save(newObj); if (this.SubmitResult) this.SubmitResult(true, errors); } } else { this.RenderErrors(errors, this.RequiredText); this.IsCurrentlySubmitting = false; if (this.SubmitResult) this.SubmitResult(false, errors); } this.LastErrors = errors; return false; } }; "use strict"; window.TK.Navigator = { _: "div", DefaultHash: "index", Seperator: "/", Current: null, CurrentElement: null, UseTemplates: false, Init: function () { var obj = this; if (this.Templates) { this.UseTemplates = true; } this.onHashChangeHandler = function () { obj.Handle(); }; window.addEventListener("hashchange", this.onHashChangeHandler); if (this.NavigatorLevel === undefined) { this.NavigatorLevel = 0; var parent = this.Parent; while (parent) { if (parent.DefaultHash) { // This is a navigator this.NavigatorLevel++; } parent = parent.Parent; } } this.Handle(); }, Destroy: function () { window.removeEventListener("hashchange", this.onHashChangeHandler); }, Handle: function () { var navigateTo = window.location.hash; if (navigateTo == "") navigateTo = this.DefaultHash; else navigateTo = navigateTo.substr(1); var pagePart = decodeURIComponent(navigateTo).split(this.Seperator); if (pagePart.length <= this.NavigatorLevel) { pagePart.push(this.DefaultHash); } if (this.UseTemplates) { // Add new element, and destroy old elements (Slower, less memory usage) if (pagePart[this.NavigatorLevel] != this.Current) { if (this.CurrentElement) { this.CurrentElement.Remove(); } this.Current = pagePart[this.NavigatorLevel]; if (this.Templates[this.Current]) { this.CurrentElement = this.Add(this.Templates[this.Current], "current"); } } } else { // Just hide/show elements (Faster, but more memory usage) this.CurrentElement = null; for (var elementName in this.Elements) { if (elementName != pagePart[this.NavigatorLevel] && this.Elements[elementName].style && this.Elements[elementName].style.display != "none") this.Elements[elementName].style.display = "none"; else if (elementName == pagePart[this.NavigatorLevel]) { this.CurrentElement = this.Elements[elementName]; if (this.Elements[elementName].style && this.Elements[elementName].style.display != "") this.Elements[elementName].style.display = ""; } } } this.Current = pagePart[this.NavigatorLevel]; pagePart.splice(0, this.NavigatorLevel + 1); if (this.CurrentElement && this.CurrentElement.Navigate) { this.CurrentElement.Navigate(pagePart); } if (this.Navigate) this.Navigate(this.Current); }, Navigate: function (newPage) { } };"use strict"; window.TK.Page = { _: "div", Url: "", Post: null, IsTemplate: false, Template: null, ExecuteScripts: true, ChangeForms: true, AjaxSettings: {}, State: null, Init: function () { var obj = this; this.ajaxCallBack = function (response) { if (response && response.substr(0, 1) == "{") { // Response is a template var template = eval('(' + response + ')'); if (obj.Template) { obj.Template._ = template; obj.Add(obj.Template, "page"); } else { obj.Add(template, "page"); } } else { obj.innerHTML = response; if (obj.ExecuteScripts) { Ajax.executeScripts(response); } if (obj.ChangeForms) { var allForms = obj.querySelectorAll("form"); for (var i = 0; i < allForms.length; i++) { allForms[i].onsubmit = function () { Ajax.doAjaxFormSubmit(this, obj.ajaxCallBack); return false; }; } } } if (obj.Update) { obj.Update(); } }; if (this.Url) { Ajax.do(this.Url, this.Post, this.ajaxCallBack, undefined, this.AjaxSettings); } }, Refresh: function () { if (this.Elements.page) { this.Elements.page.Remove(); } this.innerHTML = ""; this.Init(); }, Update: function () { } };"use strict"; window.TK.Popup = { _: "div", _Tag: "ToolkitPopup", Width: 400, Height: 500, MinWidth: 150, MinHeight: 150, Title: "Popup", EnableCloseButton: true, EnableBackDrop: false, CloseWithEscapeButton: false, CloseByClickingOutsideOfPopup: false, EnableResize: true, EnableSnapping: true, Maximized: false, className: "toolkitPopup", Template: {}, OnResize: function () { }, Buttons: null, // { Ok: function() { alert('ok pressed!'); } } Init: function () { var obj = this; this.style.position = "fixed"; this.CenterWindow(); this.RestoreBodyOverflow = function () { if (obj.OrigBodyOverflow) { document.body.style.overflow = obj.OrigBodyOverflow; obj.OrigBodyOverflow = null; } }; this.Add({ _: "h2", Elements: { TitleSpan: { innerText: this.Title } }, onselectstart: function () { return false; }, ondblclick: function () { if (!obj.EnableResize) return; if (obj.Maximized) { obj.Maximized = false; obj.RestoreBodyOverflow(); obj.style.left = obj.OldCords[0] + "px"; obj.style.top = obj.OldCords[1] + "px"; obj.style.width = obj.OldCords[2] + "px"; obj.style.height = obj.OldCords[3] + "px"; } else { obj.Maximized = true; obj.OrigBodyOverflow = document.body.style.overflow; if (!obj.OrigBodyOverflow) obj.OrigBodyOverflow = "initial"; document.body.style.overflow = "hidden"; obj.OldCords = [obj.offsetLeft, obj.offsetTop, obj.offsetWidth, obj.offsetHeight]; obj.style.left = "0px"; obj.style.top = "0px"; obj.style.width = window.innerWidth + "px"; obj.style.height = window.innerHeight + "px"; } if (obj.OnResize) { obj.OnResize(); } }, ontouchstart: function (e) { this.onmousedown(e.touches[0]); e.stopPropagation(); }, onmousedown: function (e) { var x, y; try { x = e.pageX; y = e.pageY; } catch (errie) { var e2 = window.event; x = e2.clientX; y = e2.clientY; } var startX = x - obj.offsetLeft; var startY = y - obj.offsetTop; var startWidth = obj.offsetWidth; var startHeight = obj.offsetHeight; var snapXSides = [0, window.innerWidth]; var snapYSides = [0, window.innerHeight]; if (obj.EnableSnapping) { var allPopups = document.querySelectorAll(".toolkitPopup"); for (var i = 0; i < allPopups.length; i++) { if (allPopups[i] == obj || !allPopups[i].parentNode) continue; snapXSides.push(allPopups[i].offsetLeft - 2); snapXSides.push(allPopups[i].offsetLeft + allPopups[i].offsetWidth); snapYSides.push(allPopups[i].offsetTop); snapYSides.push(allPopups[i].offsetTop + allPopups[i].offsetHeight); } } window.onmousemove = function (e) { if (obj.Maximized) { obj.Maximized = false; obj.style.left = obj.OldCords[0] + "px"; obj.style.top = obj.OldCords[1] + "px"; obj.style.width = obj.OldCords[2] + "px"; obj.style.height = obj.OldCords[3] + "px"; obj.RestoreBodyOverflow(); } var x, y; try { x = e.pageX; y = e.pageY; } catch (errie) { var e2 = window.event; x = e2.clientX; y = e2.clientY; } var newLeft = (x - startX); var newTop = (y - startY); if (obj.EnableSnapping) { for (var i = 0; i < snapXSides.length; i++) { if (Math.abs(snapXSides[i] - newLeft) < 10) { newLeft = snapXSides[i]; } else if (Math.abs(snapXSides[i] - (newLeft + startWidth)) < 10) { newLeft = snapXSides[i] - startWidth; } } for (var i = 0; i < snapYSides.length; i++) { if (Math.abs(snapYSides[i] - newTop) < 10) { newTop = snapYSides[i]; } else if (Math.abs(snapYSides[i] - (newTop + startHeight)) < 10) { newTop = snapYSides[i] - startHeight; } } } obj.style.left = newLeft + "px"; obj.style.top = newTop + "px"; }; window.onmouseup = function () { window.onmousemove = null; window.onmouseup = null; window.onselectstart = null; window.ontouchmove = null; window.ontouchend = null; }; window.ontouchmove = function (e) { if (window.onmousemove) window.onmousemove(e.touches[0]); e.stopPropagation(); }; window.ontouchend = function (e) { if (window.onmouseup) window.onmouseup(); e.stopPropagation(); }; window.onselectstart = function () { return false; }; if (e && e.preventDefault) e.preventDefault(); else window.event.returnValue = false; } }, "PopupTitle"); if (this.EnableResize) { this.Add({ _: "button", innerHTML: "", onselectstart: function () { return false; }, ontouchstart: function (e) { this.onmousedown(e.touches[0]); e.stopPropagation(); }, onmousedown: function (e) { var x, y; try { x = e.pageX; y = e.pageY; } catch (errie) { var e2 = window.event; x = e2.clientX; y = e2.clientY; } var startX = x; var startY = y; var startWidth = obj.offsetWidth; var startHeight = obj.offsetHeight; window.onselectstart = function () { return false; }; window.onmousemove = function (e) { obj.Maximized = false; obj.RestoreBodyOverflow(); var x, y; try { x = e.pageX; y = e.pageY; } catch (errie) { var e2 = window.event; x = e2.clientX; y = e2.clientY; } var newWidth = startWidth + (x - startX); if (newWidth < obj.MinWidth) newWidth = obj.MinWidth; var newHeight = startHeight + (y - startY); if (newHeight < obj.MinWidth) newHeight = obj.MinWidth; obj.style.width = newWidth + "px"; obj.style.height = newHeight + "px"; window.onmouseup = function () { window.onmousemove = null; window.onselectstart = null; window.ontouchmove = null; window.ontouchend = null; if (obj.OnResize) { obj.OnResize(); } }; if (e && e.preventDefault) e.preventDefault(); else window.event.returnValue = false; }; window.ontouchmove = function (e) { if (window.onmousemove) window.onmousemove(e.touches[0]); e.stopPropagation(); }; window.ontouchend = function (e) { if (window.onmouseup) window.onmouseup(); e.stopPropagation(); }; } }, "ResizeButton"); } if (this.EnableCloseButton) { this.Add({ _: "button", innerHTML: "x", onclick: function () { this.Parent.Remove(); } }, "CloseButton"); } if (this.Buttons) { var buttonBar = { className: "toolkitButtonBar", Elements: {} }; this.className += " toolkitPopupWithButtonBar"; for (var name in this.Buttons) { if (typeof this.Buttons[name] === 'function') { this.Buttons[name] = { Click: this.Buttons[name] }; } buttonBar.Elements[name + "Button"] = { _: this.Buttons[name], ButtonName: name, Init: function () { this.appendChild(document.createTextNode(this.Text ? this.Text : this.ButtonName)); }, onclick: function () { var cancelClosePopup = false; if (this.Click) cancelClosePopup = this.Click(); if (!cancelClosePopup) this.Parent.Parent.Remove(); } }; } this.Add(buttonBar, "ButtonBar"); } this.Add(this.Template, "Content"); if (this.EnableBackDrop) { this.BackDrop = document.createElement("DIV"); this.BackDrop.className = "toolkitPopupBackDrop"; this.BackDrop.style.position = "fixed"; this.BackDrop.style.top = "0px"; this.BackDrop.style.right = "0px"; this.BackDrop.style.bottom = "0px"; this.BackDrop.style.left = "0px"; this.BackDrop.style.zIndex = (window.TK.Popup.StartZIndex++); if (this.CloseByClickingOutsideOfPopup) { this.BackDrop.onclick = function () { obj.Remove(); } }; if (this.CloseWithEscapeButton) { obj.Keydown = function (evt) { if (evt && evt.key === 'Escape' && obj != null) { obj.Remove(); } }; document.addEventListener('keydown', obj.Keydown) }; document.body.appendChild(this.BackDrop); } this.style.zIndex = (window.TK.Popup.StartZIndex++); document.body.appendChild(this); // Move myself to the body element if (this.Maximized || window.innerWidth < this.Width || window.innerHeight < this.Height) { this.Maximized = false; this.Elements.PopupTitle.ondblclick(); } }, Destroy: function () { if (this.BackDrop) { this.BackDrop.parentNode.removeChild(this.BackDrop); } if (this.Keydown) { document.removeEventListener('keydown', this.Keydown); } this.RestoreBodyOverflow(); }, onmousedown: function () { this.style.zIndex = (window.TK.Popup.StartZIndex++); }, CenterWindow: function () { this.style.width = this.Width + "px"; this.style.height = this.Height + "px"; this.style.top = Math.round((window.innerHeight / 2) - (this.Height / 2)) + "px"; this.style.left = Math.round((window.innerWidth / 2) - (this.Width / 2)) + "px"; } }; window.TK.Popup.StartZIndex = 10000; window.TK.PopupOpen = function (template, title, width, height) { TK.Initialize({ _: TK.Popup, Width: width ? width : 600, Height: height ? height : 500, Title: title, Template: template }); }; "use strict"; /* Minify Skip */ /* Minify Order(100) */ // Component to send and receive messages in a public channel, this will try to use WebSockets or fall back to https requests to the toolkit api TK.ServerChannel = { _: "component", Channels: [], ClientId: "Client" + (Math.random() * 100000) + "-" + (Math.random() * 100000) + "-" + (Math.random() * 100000) + "-" + (Math.random() * 100000), // Not visible for other connected client Receive: function (channel, identity, data) { }, Connection: null, Init: function () { if (!TK.ServerChannel.Connections) TK.ServerChannel.Connections = {}; if (!TK.ServerChannel.Connections[this.ClientId]) { // Create connection TK.ServerChannel.Connections[this.ClientId] = TK.Initialize({ _: TK.ServerChannelConnection, ClientId: this.ClientId, Channels: this.Channels.slice(0), UsedBy: [this], Receive: function (channel, identity, data) { for (var i = 0; i < this.UsedBy.length; i++) { if (this.UsedBy[i].Channels.indexOf(channel) >= 0) this.UsedBy[i].Receive(channel, identity, data); } } }); } else { // Use existing connection TK.ServerChannel.Connections[this.ClientId].UsedBy.push(this); for (var i = 0; i < this.Channels.length; i++) { TK.ServerChannel.Connections[this.ClientId].AddChannel(this.Channels[i]); } } this.Connection = TK.ServerChannel.Connections[this.ClientId]; }, Destroy: function () { while (this.Channels.length > 0) this.RemoveChannel(this.Channels[0]); this.Connection.UsedBy.splice(this.Connection.UsedBy.indexOf(this), 1); if (this.Connection.UsedBy.length == 0) this.Connection.Remove(); }, Send: function (channel, data) { this.Connection.Send(channel, data); }, AddChannel: function (channel) { if (this.Channels.indexOf(channel) >= 0) return; this.Channels.push(channel); this.Connection.AddChannel(channel); }, RemoveChannel: function (channel) { if (this.Channels.indexOf(channel) < 0) return; this.Channels.splice(this.Channels.indexOf(channel), 1); // Check if used by other ServerChannel objects for (var i = 0; i < this.Connection.UsedBy.length; i++) { if (this.Connection.UsedBy[i].Channels.indexOf(channel) >= 0) return; } // Not used anymore, leave channel this.Connection.RemoveChannel(channel); } }; // Shared component, this will be used by (multiple) TK.ServerChannel objects TK.ServerChannelConnection = { _: "component", Channels: [], ClientId: null, // Not visible for other connected client UrlFallback: "https://toolkitapi.comgenie.com/Channel/Communicate", UrlWebsocket: "wss://toolkitapi.comgenie.com/ws", FallbackInterval: 5000, Receive: function (channel, identity, data) { }, ChannelIndexes: [], // used internally for fallback method Init: function () { if (this.ClientId == null) return; if (this.UrlWebsocket) this.CreateWebsocket(); else this.CreateFallback(); }, Destroy: function () { // Disconnect communication if (this.WebSocket) this.WebSocket.close(); if (this.FallBackTimeout) { clearTimeout(this.FallBackTimeout); this.FallBackTimeout = null; } }, CreateWebsocket: function () { var obj = this; var webSocket = new WebSocket(this.UrlWebsocket); webSocket.SendData = function (command, data) { //console.log("Send data: " + command, data); this.send(command + "|" + data.length + "|" + data); }; webSocket.onopen = function (eventData) { //console.log("WS Open", eventData); obj.WebSocket = this; this.SendData("ID", obj.ClientId); for (var i = 0; i < obj.Channels.length; i++) this.SendData("JOIN", obj.Channels[i]); }; webSocket.onmessage = function (eventData) { if (!eventData.data.substr) return; //console.log("WS Receive", eventData.data); var firstSeperator = eventData.data.indexOf("|"); if (firstSeperator < 0) return; var secondSeperator = eventData.data.indexOf("|", firstSeperator + 1); if (secondSeperator < 0) return; var command = eventData.data.substr(0, firstSeperator); var dataLength = eventData.data.substring(secondSeperator + 1, secondSeperator); var payload = eventData.data.substr(secondSeperator + 1); if (command == "MSG") { var msg = JSON.parse(payload); console.log(msg); obj.Receive(msg.Channel, msg.Identity, msg.Data); } }; webSocket.onerror = function (eventData) { // Couldn't connect //console.log("WS Error", eventData); obj.CreateFallback(); }; webSocket.onclose = function (eventData) { // Disconnected //console.log("WS Close", eventData) obj.CreateFallback(); }; }, CreateFallback: function () { console.log("Using fallback"); this.WebSocket = null; // TODO: See if we can reconnect this.SendFallBack(null, null); // Send first check message, this activates the interval as well }, AddChannel: function (channel) { if (this.Channels.indexOf(channel) >= 0) return; this.Channels.push(channel); this.ChannelIndexes.push(-1); if (this.WebSocket) this.WebSocket.SendData("JOIN", channel); }, RemoveChannel: function (channel) { var channelIndex = this.Channels.indexOf(channel); if (channelIndex < 0) return; this.Channels.splice(channelIndex, 1); this.ChannelIndexes.splice(channelIndex, 1); if (this.WebSocket) this.WebSocket.SendData("LEAVE", channel); }, SendFallBack: function (channel, data) { if (this.FallBackTimeout) { clearTimeout(this.FallBackTimeout); this.FallBackTimeout = null; } var obj = this; var channelsArr = []; var indexesArr = []; for (var i = 0; i < this.Channels.length; i++) { channelsArr.push(this.Channels[i]); indexesArr.push(this.ChannelIndexes[i] !== undefined ? this.ChannelIndexes[i] : -1); } Ajax.do(this.UrlFallback, { clientId: this.ClientId, channels: channelsArr, indexes: indexesArr, sendChannel: channel, sendData: data }, function (responseObj) { //console.log("Fallback response", responseObj); for (var i = 0; i < responseObj.length; i++) { if (!responseObj[i].channel) continue; var arrChannelIndex = obj.Channels.indexOf(responseObj[i].channel); if (arrChannelIndex < 0) // Unsubscribed before callback was received continue; for (var j = 0; j < responseObj[i].messages.length; j++) { obj.Receive(responseObj[i].channel, responseObj[i].messages[j].identity, responseObj[i].messages[j].data); } // Update our index so we can prevent double-messages if (responseObj[i].index != 0) obj.ChannelIndexes[arrChannelIndex] = responseObj[i].index; } }); this.FallBackTimeout = setTimeout(function () { obj.SendFallBack(null, null); }, this.FallbackInterval); }, Send: function (channel, data) { if (this.WebSocket) this.WebSocket.SendData("MSG", JSON.stringify({ Channel: channel, Data: data })); else if (this.UrlFallback) this.SendFallBack(channel, data); } };"use strict"; /* Minify Skip */ /* Minify Order(100) */ // Component to save and retrieve files, using the Toolkit API // This component has the same methods as TK.ClientStorage TK.ServerStorage = { _: "component", Container: null, // null is only for public file storage/retrieval, use a https:// link for a private storage container with rights check based on the clientId ClientId: "Client" + (Math.random() * 100000) + "-" + (Math.random() * 100000) + "-" + (Math.random() * 100000) + "-" + (Math.random() * 100000), // Used for private containers Url: "https://toolkitapi.comgenie.com/Storage", Store: function (path, blobOrByteArrayOrStringContents, callBack) { var formData = new FormData(); var url = "?clientId=" + encodeURIComponent(this.ClientId) + (this.Container ? "&container=" + encodeURIComponent(this.Container) : ""); if (path) url += "&fileName=" + encodeURIComponent(path); if ((window.Blob && blobOrByteArrayOrStringContents instanceof Blob) || (window.File && blobOrByteArrayOrStringContents instanceof File)) { formData.append("files", blobOrByteArrayOrStringContents); } else if ((window.Uint8Array && blobOrByteArrayOrStringContents instanceof Uint8Array) || (window.Uint16Array && blobOrByteArrayOrStringContents instanceof Uint16Array)) { formData.append("files", new Blob(blobOrByteArrayOrStringContents)); } else if (blobOrByteArrayOrStringContents.substr && blobOrByteArrayOrStringContents.length && blobOrByteArrayOrStringContents.length > 10 && blobOrByteArrayOrStringContents.substr(0, 5) == "data:") { var blob = new Blob(atob(blobOrByteArrayOrStringContents.split(',')[1])); // Convert base64 data url to blob formData.append("files", blob); } else { // string if (!blobOrByteArrayOrStringContents.substr) blobOrByteArrayOrStringContents = blobOrByteArrayOrStringContents.toString(); var buf = new ArrayBuffer(blobOrByteArrayOrStringContents.length * 2); // 2 bytes for each char var bufView = new Uint16Array(buf); for (var i = 0; i < blobOrByteArrayOrStringContents.length; i++) bufView[i] = blobOrByteArrayOrStringContents.charCodeAt(i); formData.append("files", new Blob(bufView)); } Ajax.do(this.Url + "/Store" + url, formData, function (response) { if (!callBack) return; if (response.length && response.length > 0 && response[0].url) callBack(response[0]); else callBack(); }, null, { parseJSONResponse: true }); }, Retrieve: function (path, asBlob, callBack) { Ajax.do(this.Url + "/Retrieve", { clientId: this.ClientId, container: this.Container, fileName: path, directly: true }, function (contents) { if (callBack) callBack(contents); }, null, { parseJSONResponse: false, responseType: (asBlob ? "blob" : undefined) }); }, GetUrl: function (path, callBack) { Ajax.do(this.Url + "/Retrieve", { clientId: this.ClientId, container: this.Container, fileName: path }, function (fileData) { if (!callBack) return; if (fileData.Url) callBack(fileData.Url); else { console.log(fileData); callBack(); } }); }, Delete: function (path, callBack) { Ajax.do(this.Url + "/Delete", { clientId: this.ClientId, container: this.Container, fileName: path }, function (result) { if (callBack) callBack(result == "OK"); }); }, List: function (callBack) { Ajax.do(this.Url + "/List", { clientId: this.ClientId, container: this.Container }, function (files) { if (!callBack) return; if (files && files.length > 0 && files[0].url) callBack(files); else { console.log(files); callBack(); } }); }, }; "use strict"; // Sortable table window.TK.Table = { _: "table", className: "toolkitTable", EnableSort: true, EnableFilter: false, EnableTotals: false, DisableFilterForColumns: [], ThresholdFilterMultiselect: 15, EnableCheckBoxes: false, EnableFullRowCheckBoxToggle: false, EnableFullRowClickDeselectOthers: false, EnableSelectAllCheckBox: true, EnableRemoveButton: false, EnablePartialInit: false, // TODO: Only add rows as TR elements which are currently visible PageSize: null, PageOffset: 0, SpecificColumns: null, ColumnTitles: {}, Templates: {}, HeaderTemplates: {}, SortedBy: null, SortedDesc: false, SortCallBack: null, FilterCallBack: null, MaxRows: null, CurrentFilter: null, DefaultTemplate: { _: "td", Data: null, Init: function () { this.appendChild(document.createTextNode(this.Data)); } }, CheckBoxTemplate: { _: "td", Data: null, onclick: function (event) { if (event) event.stopPropagation(); if (window.event) window.event.cancelBubble = true; return true; }, Elements: { CheckBox: { _: "input", type: "checkbox", className: "tableRowCheckBox", onclick: function (event) { this.Parent.Table.LastCheckBoxRange = null; if (event.shiftKey && this.Parent.Table.PreviousCheckBox) { // Select everything in between this.Parent.Table.LastCheckBoxRange = []; var curIndex = this.Parent.Parent.RowIndex; var otherIndex = this.Parent.Table.PreviousCheckBox.Parent.Parent.RowIndex; for (var i = curIndex; i != otherIndex; i = (curIndex < otherIndex ? i + 1 : i - 1)) { var checkBoxElement = this.Parent.Table.querySelectorAll(".Element-row" + i + " input[type=checkbox].tableRowCheckBox")[0]; if (checkBoxElement.Parent.Parent.style.display == "none") continue; // Skip filtered rows if (checkBoxElement.checked != this.Parent.Table.PreviousCheckBox.checked) { this.Parent.Table.LastCheckBoxRange.push(checkBoxElement.Parent.Row); checkBoxElement.checked = this.Parent.Table.PreviousCheckBox.checked; } checkBoxElement.UpdateData(); } } }, onblur: function () { this.Parent.Table.PreviousCheckBox = this; }, onchange: function (event) { this.UpdateData(); if (this.Parent.Table.EnableSelectAllCheckBox) { var selectAll = this.Parent.Table.querySelectorAll(".tableSelectAllCheckBox")[0]; if (selectAll) { var checkBoxElements = this.Parent.Table.Elements.tbody.Elements.ToArray() .Where(function (a) { return a.Elements.CheckBoxes && a.Elements.CheckBoxes.Elements.CheckBox && a.style.display != "none"; }) .Select(function (a) { return a.Elements.CheckBoxes.Elements.CheckBox }); selectAll.checked = checkBoxElements.length == this.Parent.Table.SelectedRows().length; } } if (this.Parent.Table.CheckboxCheck) { if (!this.Parent.Table.LastCheckBoxRange) { this.Parent.Table.CheckboxCheck([this.Parent.Row], this.checked); } else { if (this.Parent.Table.LastCheckBoxRange.indexOf(this.Parent.Row) < 0) this.Parent.Table.LastCheckBoxRange.push(this.Parent.Row); this.Parent.Table.CheckboxCheck(this.Parent.Table.LastCheckBoxRange, this.checked); } } this.Parent.Table.LastCheckBoxRange = null; }, UpdateData: function () { this.Parent.Row["CheckBoxes"] = this.checked; }, Init: function () { this.checked = this.Parent.Data === true; } } } }, RemoveButtonTemplate: { _: "td", Data: null, onclick: function (event) { if (event) event.stopPropagation(); if (window.event) window.event.cancelBubble = true; return true; }, Elements: { RemoveButton: { _: "button", innerHTML: "Remove", onclick: function (event) { // Find with button clicked and delete it from Rows variable in the table var table = this.Parent.Table; var thisRow = this.Parent.Parent.Row; // When the row was succesfully deleted in the save function remove current row if (table.Save(thisRow, true) !== false) { for (var i = 0; i < table.Rows.length; i++) { if (table.Rows[i] == thisRow) { table.Rows.splice(i, 1); break; } } var thisRowNode = this.Parent.Parent; // When the user was editing the row a form is showing on the next row. When this is the case remove that form as well. if (thisRowNode.subTr) thisRowNode.subTr.Remove(); thisRowNode.Remove(); } } } } }, PreviousCheckBox: null, Rows: [], Form: null, FormAlwaysLoaded: false, // If true, the subViews are initialized directly, but shown/hidden using style.display Init: function () { if (this.SortedBy && this.Rows && this.Rows.OrderBy) { var sortedBy = this.SortedBy; this.Rows = this.SortedDesc ? this.Rows.OrderByDesc(function (a) { return a[sortedBy]; }) : this.Rows.OrderBy(function (a) { return a[sortedBy]; }); } this.Refresh(); }, RowClick: function (rowObj, trElement) { var obj = this; if (this.Form) { if (!trElement) { // Optional parameter, Find the TR element based on the given row Obj if (!this.Elements.tbody) return; for (var rowId in this.Elements.tbody.Elements) { var row = this.Elements.tbody.Elements[rowId]; if (row.Row == rowObj) { trElement = row; break; } } if (!trElement) return; } if (obj.FormAlwaysLoaded) { if (trElement.subTr) { trElement.subTr.style.display = trElement.subTr.style.display == "" ? "none" : ""; return; } } else { if (trElement.subTr) { trElement.subTr.parentNode.removeChild(trElement.subTr); trElement.subTr = null; return; } } var template = { _: obj.Form, Model: rowObj, ApplyToModelDirectly: true, }; if (obj.Form.Save == undefined) { template.Save = function (model) { obj.Save(model, false); obj.UpdateRow(trElement); }; } if (obj.Form.ApplyToModelDirectly == undefined) { template.ApplyToModelDirectly = true; } this.OpenViewForRow(trElement, template); } }, RowDoubleClick: function (rowObj, trElement) { }, Save: function (rowObj, isRemoved) { // Only used when the Form property is given }, OpenViewForRow: function (trElement, template) { if (!template || trElement.subTr) return; var childElements = trElement.Elements.ToArray(); var tds = 0; for (var i = 0; i < childElements.length; i++) { tds += childElements[i].colSpan ? parseInt(childElements[i].colSpan) : 1; } trElement.subTr = trElement.Parent.Add( { _: "tr", ThisIsASubTR: true, Destroy: function () { trElement.subTr = null; }, Elements: { Editor: { _: "td", className: "subView", colSpan: tds, Elements: { View: template } } } }); trElement.parentNode.insertBefore(trElement.subTr, trElement.nextSibling); }, CheckboxCheck: function (rowsChanged, isChecked) { }, SelectedRows: function () { if (!this.EnableCheckBoxes) return []; return this.Elements.tbody.Elements.ToArray() .Where(function (a) { return a.Elements.CheckBoxes && a.Elements.CheckBoxes.Elements.CheckBox && a.Elements.CheckBoxes.Elements.CheckBox.checked; }) .Select(function (a) { return a.Row }); }, ApplyFilter: function (filter, skipCallback) { this.CurrentFilter = filter == null ? "" : filter.toLowerCase ? filter.toLowerCase() : filter; if (this.FilterCallBack && !skipCallback) { this.FilterCallBack(); return; } if (!this.Elements.tbody) { return; } if (this.Elements.thead && this.ColumnFilter) { for (var name in this.ColumnFilter) { if (this.Elements.thead.Elements.tr.Elements[name]) { this.Elements.thead.Elements.tr.Elements[name].className = this.ColumnFilter[name] ? "toolkitHasFilter" : ""; } } } this.Elements.tbody.style.display = "none"; // This prevents row style changes to cause render updates, making it a lot faster var rows = 0; for (var rowId in this.Elements.tbody.Elements) { var row = this.Elements.tbody.Elements[rowId]; if (!row.innerText || row.ThisIsASubTR) continue; if (this.MaxRows != null && rows >= this.MaxRows) { row.style.display = "none"; if (row.subTr) row.subTr.style.display = "none"; continue; } if (row.subTr && row.subTr.Elements && row.subTr.Elements.Editor && row.subTr.Elements.Editor.Elements && row.subTr.Elements.Editor.Elements.View && row.subTr.Elements.Editor.Elements.View.IsVisible && row.subTr.Elements.Editor.Elements.View.IsVisible(this.CurrentFilter)) { // The subTr wants to be visible, so we'll also make the parent row visible row.style.display = ""; row.subTr.style.display = ""; rows++; continue; } if (this.CurrentFilter == "" || this.CurrentFilter == null || !row.innerText || (this.CurrentFilter && this.CurrentFilter.toLowerCase && row.innerText.toLowerCase().indexOf(this.CurrentFilter) >= 0) || (this.CurrentFilter && !this.CurrentFilter.toLowerCase && this.CurrentFilter(row.Row, row))) { row.style.display = ""; if (row.subTr) row.subTr.style.display = ""; rows++; } else { row.style.display = "none"; if (row.subTr) row.subTr.style.display = "none"; } if (this.PageSize != null && ((rows - 1) < this.PageOffset || (rows-1) >= this.PageOffset + this.PageSize)) { row.style.display = "none"; if (row.subTr) row.subTr.style.display = "none"; continue; } } this.Elements.tbody.style.display = ""; this.VisibleRowCount = rows; var ttnc = this.Near(".toolkitTableNavigationContainer"); if (ttnc) ttnc.Init(); return rows; }, Refresh: function () { var obj = this; this.Clear(); this.PreviousCheckBox = null; if (this.Rows.length == 0) { return; } // Build header var thead = { _: "thead", Elements: { tr: { _: "tr", Elements: {} } } }; var columns = []; if (this.EnableCheckBoxes) { columns.push("CheckBoxes"); if (!this.DisableFilterForColumns) this.DisableFilterForColumns = []; if (this.DisableFilterForColumns.indexOf("CheckBoxes") < 0) this.DisableFilterForColumns.push("CheckBoxes"); this.ColumnTitles["CheckBoxes"] = " "; this.Templates["CheckBoxes"] = this.CheckBoxTemplate; if (this.EnableSelectAllCheckBox) { this.HeaderTemplates["CheckBoxes"] = { _: "th", Init: function () { var selectAllCheckBox = this.Add({ _: "input", type: "checkbox", className: "tableSelectAllCheckBox", onclick: function (event) { if (event) event.stopPropagation(); if (window.event) window.event.cancelBubble = true; }, onchange: function (event) { var checkBoxElements = obj.Elements.tbody.Elements.ToArray() .Where(function (a) { return a.Elements.CheckBoxes && a.Elements.CheckBoxes.Elements.CheckBox && a.style.display != "none"; }) .Select(function (a) { return a.Elements.CheckBoxes.Elements.CheckBox }); var changedRange = []; for (var i = 0; i < checkBoxElements.length; i++) { if (checkBoxElements[i].checked != this.checked) { changedRange.push(checkBoxElements[i].Parent.Row); checkBoxElements[i].checked = this.checked; } checkBoxElements[i].UpdateData(); } if (obj.CheckboxCheck) obj.CheckboxCheck(changedRange, this.checked); } }); // See if all rows are already selected var totalRowsChecked = 0; for (; totalRowsChecked < obj.Rows.length; totalRowsChecked++) { if (!obj.Rows[totalRowsChecked]["CheckBoxes"]) break; } if (totalRowsChecked > 0 && totalRowsChecked == obj.Rows.length) selectAllCheckBox.checked = true; } }; } else { this.HeaderTemplates["CheckBoxes"] = null; } } if (this.SpecificColumns && this.SpecificColumns.length > 0) { columns = columns.concat(this.SpecificColumns); } else { for (var name in this.Rows[0]) { columns.push(name); } } if (this.EnableRemoveButton) { columns.push("RemoveButtons"); this.ColumnTitles["RemoveButtons"] = " "; this.Templates["RemoveButtons"] = this.RemoveButtonTemplate; } for (var i = 0; i < columns.length; i++) { var name = columns[i]; thead.Elements.tr.Elements[name] = { _: this.HeaderTemplates && this.HeaderTemplates[name] ? this.HeaderTemplates[name] : "th", innerHTML: (this.ColumnTitles && this.ColumnTitles[name] ? this.ColumnTitles[name] : name), className: (name == obj.SortedBy ? "sorted" + (this.SortedDesc ? " desc" : "") : ""), DataColumnName: name, onclick: function () { if (!obj.EnableSort) return; var thisTh = this; if (obj.SortedBy == this.DataColumnName) { obj.Rows = obj.Rows.reverse(); obj.SortedDesc = !obj.SortedDesc; } else { obj.Rows = obj.Rows.OrderBy(function (a) { if (a[thisTh.DataColumnName + "-SortValue"] !== undefined) return a[thisTh.DataColumnName + "-SortValue"]; return a[thisTh.DataColumnName]; }); obj.SortedDesc = false; } obj.SortedBy = this.DataColumnName; if (obj.SortCallBack) { obj.SortCallBack(); } else { obj.Refresh(); } }, Elements: {} }; // Enable filter button on column if (this.EnableFilter && (!this.DisableFilterForColumns || this.DisableFilterForColumns.indexOf(name) < 0 )) { thead.Elements.tr.Elements[name].Elements.FilterButton = { className: "toolkitFilterButton", innerHTML: "v", onclick: function (event) { // Open filter window for this column and data type (Slider for numbers, Multiselect for few options, search for many options) var currentHeader = this.Parent; var position = this.Parent.getBoundingClientRect(); var filterWindow = { className: "toolkitFilterBox", style: { backgroundColor: "#eee", left: position.left + "px", top: position.bottom + "px", position: "fixed" }, Elements: { CloseButton: { innerHTML: "x", onclick: function () { this.Parent.Remove(); obj.ActiveFilterWindow = null; } }, Container: { Init: function () { var values = []; for (var i = 0; i < obj.Rows.length; i++) { var value = obj.Rows[i][currentHeader.DataColumnName]; if (values.indexOf(value) < 0) values.push(value); if (values.length >= obj.ThresholdFilterMultiselect) { break; } } if (!obj.ColumnFilter) obj.ColumnFilter = {}; var filterFunc = function (rowObj, trObj) { for (var name in obj.ColumnFilter) { if (!obj.ColumnFilter[name]) continue; if (obj.ColumnFilter[name].toLowerCase) { // text match, match if any of the text is in there var lowerColumnSearch = obj.ColumnFilter[name].toLowerCase(); var foundInFieldValue = rowObj[name] && rowObj[name].toString().toLowerCase().indexOf(lowerColumnSearch) >= 0; var foundInTdText = trObj.Elements && trObj.Elements[name] && trObj.Elements[name].innerText && trObj.Elements[name].innerText.toLowerCase().indexOf(lowerColumnSearch) >= 0; if (!foundInFieldValue && !foundInTdText) return false; continue; } if (Array.isArray(obj.ColumnFilter[name]) && rowObj[name] !== undefined) { if (obj.ColumnFilter[name].indexOf(rowObj[name]) < 0) return false; } else if (obj.ColumnFilter[name] != rowObj[name]) { // match exact, for numbers etc. return false; } } return true; }; if (values.length < obj.ThresholdFilterMultiselect) { // Show multi select values = values.OrderBy(function (a) { return a; }); var filterTable = { _: TK.Table, EnableFilter: false, Rows: [], Templates: obj.Templates, EnableCheckBoxes: true, ColumnTitles: {}, CheckboxCheck: function () { var rows = this.SelectedRows(); if (rows.length != this.Rows.length) { var allowed = []; for (var i = 0; i < rows.length; i++) allowed.push(rows[i][currentHeader.DataColumnName]); obj.ColumnFilter[currentHeader.DataColumnName] = allowed; } else { obj.ColumnFilter[currentHeader.DataColumnName] = null; } obj.ApplyFilter(filterFunc); } }; filterTable.ColumnTitles[currentHeader.DataColumnName] = "Filter"; for (var i = 0; i < values.length; i++) { var rowObj = {}; rowObj.CheckBoxes = !obj.ColumnFilter || !obj.ColumnFilter[currentHeader.DataColumnName] || (obj.ColumnFilter[currentHeader.DataColumnName].indexOf(values[i]) >= 0); rowObj[currentHeader.DataColumnName] = values[i]; filterTable.Rows.push(rowObj) } this.Add(filterTable); } else { // Show search field var inputElement = this.Add({ _: "input", value: !obj.ColumnFilter || !obj.ColumnFilter[currentHeader.DataColumnName] ? "" : obj.ColumnFilter[currentHeader.DataColumnName], onkeyup: function (event) { obj.ColumnFilter[currentHeader.DataColumnName] = (this.value == "") ? null : this.value; obj.ApplyFilter(filterFunc); var x = event.which || event.keyCode; if (x == 13) { this.Near("CloseButton").onclick(); } } }); setTimeout(function () { inputElement.focus(); }, 1); } } } } }; if (obj.ActiveFilterWindow) obj.ActiveFilterWindow.Remove(); obj.ActiveFilterWindow = TK.Initialize(filterWindow); document.body.appendChild(obj.ActiveFilterWindow); if (event) event.stopPropagation(); if (window.event) window.event.cancelBubble = true; return true; } }; } } this.TableUseColumns = columns; // Build contents var tbody = { _: "tbody", Elements: {} } this.CurrentAddedRowCount = 0; this.AddNewRowsToTableBody(tbody); this.Add(thead, "thead"); var tbodyElement = this.Add(tbody, "tbody"); if (this.FormAlwaysLoaded) { var trRows = tbodyElement.Elements.ToArray(); for (var i = 0; i < trRows.length; i++) { this.RowClick(trRows[i].Row, trRows[i]); if (trRows[i].subTr) { trRows[i].subTr.style.display = "none"; } } } var tfoot = { _: "tfoot", Elements: {} }; var includeTFoot = false; if (this.EnableTotals) { includeTFoot = true; tfoot.Elements.SubTotals = { _: "tr", Elements: {} }; for (var i = 0; i < columns.length; i++) { var name = columns[i]; if (name == "CheckBoxes" || name == "RemoveButtons") { tfoot.Elements.SubTotals.Elements[name] = { _: "td" }; continue; } var allRowValues = this.Rows.Select(function (a) { return a[name] }); var total = null; for (var j = 0; j < allRowValues.length; j++) { if (allRowValues[j] !== undefined && allRowValues[j] !== null && allRowValues[j] === +allRowValues[j]) total = (total === null ? 0 : total) + allRowValues[j]; } if (total === null) total = ""; tfoot.Elements.SubTotals.Elements[name] = { _: this.Templates[name] ? this.Templates[name] : this.DefaultTemplate, className: "totalColumn-" + name, Data: total, Values: allRowValues, Table: this }; } } if (this.MaxRows != null || this.CurrentFilter != null || this.PageSize != null) { this.ApplyFilter(this.CurrentFilter, true); includeTFoot = true; if (this.PageSize) { tfoot.Elements.NavigationRow = { _: "tr", Elements: { NavigationCell: { _: "td", className: "toolkitTableNavigationContainer", colSpan: columns.length, Init: function () { this.Clear(); var pageCount = Math.ceil(obj.VisibleRowCount / obj.PageSize); var currentPage = Math.floor(obj.PageOffset / obj.PageSize) + 1; var maxBeforeAfter = 3; var template = { _: "button", className: "toolkitTableNavigation", disabled: i == currentPage, innerHTML: i, onclick: function () { obj.PageOffset = this.Offset; obj.Refresh(); } }; if (currentPage > maxBeforeAfter + 1) { template.Offset = 0; template.innerHTML = 1; this.Add(template); this.Add({ _: "span", innerHTML: "..." }); } for (var i = (currentPage > maxBeforeAfter ? currentPage - maxBeforeAfter : 1); i <= pageCount && i < currentPage + maxBeforeAfter; i++) { template.Offset = (i - 1) * obj.PageSize; template.disabled = i == currentPage; template.innerHTML = i; this.Add(template); } if (currentPage + maxBeforeAfter < pageCount) { this.Add({ _: "span", innerHTML: "..." }); template.Offset = (pageCount - 1) * obj.PageSize; template.innerHTML = pageCount; this.Add(template); } } } } }; } } if (includeTFoot) this.Add(tfoot, "tfoot"); }, AddRow: function (row, rowClick) { this.Rows.push(row); if (this.Elements.tbody) { var tr = this.AddNewRowsToTableBody()[0]; if (!tr) { this.Refresh(); } else if (rowClick && tr) { this.RowClick(tr.Row, tr); return; } } else { this.Refresh(); } if (rowClick) { this.RowClick(row); } }, UpdateRow: function (trRow) { var template = this.GenerateRowTemplate(trRow.Row, trRow.RowIndex); var tr = this.Elements.tbody.Add(template, "row" + trRow.RowIndex); trRow.parentNode.insertBefore(tr, trRow); if (trRow.subTr) { trRow.subTr.Remove(); } trRow.Remove(); }, AddNewRowsToTableBody: function (tbody) { if (!tbody) tbody = this.Elements.tbody; var newElements = []; var obj = this; for (var i = this.CurrentAddedRowCount; i < this.Rows.length; i++) { var tr = this.GenerateRowTemplate(this.Rows[i], i); if (tbody.Add) { newElements.push(tbody.Add(tr, "row" + i)); } else { tbody.Elements["row" + i] = tr; } } this.CurrentAddedRowCount = this.Rows.length; return newElements; }, GenerateRowTemplate: function (rowObj, rowIndex) { var obj = this; var tr = { _: "tr", Elements: {}, Row: rowObj, RowIndex: rowIndex, onclick: function (event) { if (obj.EnableFullRowCheckBoxToggle) { var checkBoxElement = this.querySelector("input[type=checkbox]"); if (checkBoxElement != null) { if (obj.EnableFullRowClickDeselectOthers && !event.shiftKey && !event.ctrlKey) { var allCheckBoxes = obj.querySelectorAll("input[type=checkbox]"); for (var i = 0; i < allCheckBoxes.length; i++) { allCheckBoxes[i].checked = false; } } checkBoxElement.checked = !checkBoxElement.checked; checkBoxElement.onclick(event); checkBoxElement.onchange(); obj.PreviousCheckBox = checkBoxElement; } } obj.RowClick(this.Row, this); }, ondblclick: function () { if (obj.RowDoubleClick) { obj.RowDoubleClick(this.Row, this); } } }; for (var j = 0; j < this.TableUseColumns.length; j++) { var name = this.TableUseColumns[j]; var templateToUse = this.DefaultTemplate; if (this.Templates[name]) { templateToUse = this.Templates[name]; } tr.Elements[name] = { _: templateToUse, DataColumnName: name, Data: rowObj[name], Row: rowObj, Table: this }; if (rowObj[name] && rowObj[name].substr && rowObj[name].substr(0, 6) == "/Date(") { var tmp = rowObj[name].substr(6); tmp = tmp.substr(0, tmp.length - 2); rowObj[name + "-SortValue"] = new Date(parseInt(tmp)).getTime(); } } return tr; } }; window.TK.AjaxTable = { _: window.TK.Table, Url: null, Post: null, AjaxSettings: {}, Init: function () { this.RefreshData(); }, RefreshData: function () { this.Clear(); var obj = this; if (this.Url) { var url = this.Url; if (url.indexOf("SORTBY") >= 0) { // Sort using ajax requests var tmp = this.SortedBy ? this.SortedBy : ""; url = url.replace("SORTBY", encodeURIComponent(tmp)); url = url.replace("SORTDESC", this.SortedDesc); this.SortCallBack = function () { obj.RefreshData(); }; } if (url.indexOf("FILTER") >= 0) { // Limit rows using ajax requests var filter = this.CurrentFilter ? this.CurrentFilter : ""; url = url.replace("FILTER", encodeURIComponent(filter)); this.FilterCallBack = function () { obj.RefreshData(); }; } if (url.indexOf("COUNT") >= 0) { // Limit rows using ajax requests // TODO } Ajax.do(url, this.Post, function (response) { if (!obj.Post || typeof obj.Post === "string" || obj.Post instanceof String) { obj.Rows = JSON.parse(response); } else { obj.Rows = response; } obj.Refresh(); obj.Update(); }, undefined, this.AjaxSettings); } }, Update: function () { } }; "use strict"; window.TK.Toast = { _: "component", Template: { _: "div", className: "toolkitToast", Title: "", Message: "", Action: null, Init: function () { if (this.Title) { this.Elements.TitleH3.appendChild(document.createTextNode(this.Title)); } else { this.Elements.TitleH3.style.display = "none"; } this.Elements.Message.appendChild(document.createTextNode(this.Message)); }, onclick: function () { if (this.Action) this.Action(this); }, Elements: { TitleH3: {}, Message: {} }, Destroy: function () { var obj = this; TK.Toast.CurrentToasts = TK.Toast.CurrentToasts.Where(function (a) { return a != obj }); } }, Position: 0, // 0 Top, 0.5 Top right, 1 Right, 1.5 Bottom right, 2 Bottom, 2.5 Bottom left, 3 Left, 3.5 Top left VisibleMS: 3000, Width: 250, Height: 100, CurrentToasts: [], Create: function (title, message, action, customTemplate) { if (customTemplate && !customTemplate._) { customTemplate._ = this.Template; } var toast = TK.Initialize({ _: customTemplate ? customTemplate : this.Template, Title: title, Message: message, Action: action }); toast.style.position = "fixed"; toast.style.width = this.Width + "px"; var offset = 5; var offsetStep = this.Position == 1 || this.Position == 3 ? this.Width : this.Height; for (var i = 0; i < TK.Toast.CurrentToasts.length; i++) { if (TK.Toast.CurrentToasts[i].ToastOffset + offsetStep > offset && TK.Toast.CurrentToasts[i].ToastPosition == this.Position) { offset = TK.Toast.CurrentToasts[i].ToastOffset + offsetStep + 5; } } toast.ToastOffset = offset; toast.ToastPosition = this.Position; TK.Toast.CurrentToasts.push(toast); //toast.style.height = this.Height + "px"; if (this.Position == 0) { toast.style.top = offset + "px"; toast.style.left = ((window.innerWidth / 2) - (this.Width / 2)) + "px"; } else if (this.Position == 0.5) { toast.style.top = offset + "px"; toast.style.right = "5px"; } else if (this.Position == 1) { toast.style.top = ((window.innerHeight / 2) - (this.Height / 2)) + "px"; toast.style.right = offset + "px"; } else if (this.Position == 1.5) { toast.style.bottom = offset + "px"; toast.style.right = "5px"; } else if (this.Position == 2) { toast.style.bottom = offset +"px"; toast.style.left = ((window.innerWidth / 2) - (this.Width / 2)) + "px"; } else if (this.Position == 2.5) { toast.style.bottom = offset +"px"; toast.style.left = "5px"; } else if (this.Position == 3) { toast.style.top = ((window.innerHeight / 2) - (this.Height / 2)) + "px"; toast.style.left = offset +"px"; } else if (this.Position == 3.5) { toast.style.top = offset + "px"; toast.style.left = "5px"; } document.body.appendChild(toast); setTimeout(function () { toast.className += " toolkitVisible"; }, 10); setTimeout(function () { toast.Remove(); }, this.VisibleMS); return toast; } };"use strict"; window.TK.Tree = { _: "ul", IdField: "Id", ParentIdField: "ParentId", CurrentFilter: null, Rows: [], CurRows: null, className: "tree toolkitTree", EnableFullRowExpand: false, AutoExpandChildNodesDuringFilter: true, // When applying filter with showAllChildNodes=true and this setting is set to false, the child rows will be visible but not expanded Template: { _: "li", Data: null, Init: function () { this.Elements.Text.innerText = (this.Data && this.Data.Text) ? this.Data.Text : ""; }, Elements: { Text: { _: "span" } } }, Expanded: function (row, byUserClick, rowElement) { }, Collapsed: function (row, byUserClick, rowElement) { }, Init: function () { if (this.Rows.length == 0) return; this.Refresh(); }, AddExpandButtonToRowElement: function (rowElement) { var obj = this; if (rowElement.SubList) return; // Already has an expand button rowElement.SubList = document.createElement("UL"); rowElement.SubList.style.display = "none"; rowElement.className += " collapsed"; rowElement.appendChild(rowElement.SubList); var expandButton = document.createElement("SPAN"); expandButton.className = "expandCollapseButton"; var collapsed = window.SvgPath("M3,2L7,6L3,10", 12, 12, "#999"); expandButton.innerHTML = collapsed; expandButton.Update = function () { if (this.parentNode.className.indexOf("expanded") >= 0) { this.innerHTML = window.SvgPath("M2,3L6,7L10,3", 12, 12, "#999"); } else { this.innerHTML = collapsed; } }; expandButton.onclick = function (e) { if (this.parentNode.className.indexOf("expanded") >= 0) { obj.Collapsed(this.parentNode.Data, true, this.parentNode); this.parentNode.SubList.style.display = "none"; this.parentNode.className = this.parentNode.className.replace(/expanded/g, "") + " collapsed"; } else { obj.Expanded(this.parentNode.Data, true, this.parentNode); this.parentNode.SubList.style.display = ""; this.parentNode.className = this.parentNode.className.replace(/collapsed/g, "") + " expanded"; } this.Update(); if (e) e.stopPropagation(); return false; }; rowElement.ExpandCollapseButton = expandButton; rowElement.insertBefore(expandButton, rowElement.firstChild); }, AddRows: function (rows) { var obj = this; if (!this.CurRows) this.CurRows = {}; // First add all the rows var ignoredRows = []; var addedRows = []; for (var i = 0; i < rows.length; i++) { var rowId = rows[i][this.IdField]; if (this.CurRows["id" + rowId]) { // Won't insert duplicated id's ignoredRows.push(rows[i]); continue; } var rowElement = this.Add({ _: this.Template, Data: rows[i], onclick: function (e) { if (e && e.target && (e.target.tagName == "INPUT" || e.target.tagName == "SELECT" || e.target.tagName == "TEXTAREA" || e.target.PreventRowClick)) return; obj.RowClick(this.Data, e, this); if (obj.EnableFullRowExpand && this.ExpandCollapseButton) { this.ExpandCollapseButton.click(); } e.stopPropagation(); return false; } }); if (rows[i].AlwaysShowExpandButton) { this.AddExpandButtonToRowElement(rowElement); } if (this.CurrentFilter && rowElement.innerText.toLowerCase().indexOf(this.CurrentFilter) < 0) { rowElement.style.display = "none"; } this.CurRows["id" + rowId] = rowElement; addedRows.push(rows[i]); this.Rows.push(rows[i]); } // Then move them to the right items for (var i = 0; i < rows.length; i++) { if (ignoredRows.indexOf(rows[i]) >= 0) continue; var rowId = rows[i][this.IdField]; var parentId = rows[i][this.ParentIdField]; if (!parentId || !this.CurRows["id" + parentId]) continue; // Add expand button to the parent item var parent = this.CurRows["id" + parentId]; this.AddExpandButtonToRowElement(parent); // Move this item to the right parent element parent.SubList.appendChild(this.CurRows["id" + rowId]); } return addedRows; }, RemoveRows: function (rows) { for (var i = 0; i < rows.length; i++) { var rowId = rows[i][this.IdField]; var rowElement = this.CurRows["id" + rowId]; if (rowElement) rowElement.parentNode.removeChild(rowElement); var posIndex = this.Rows.indexOf(rows[i]); if (posIndex >= 0) { this.Rows.splice(posIndex, 1); } } }, Refresh: function () { this.Clear(); var rows = this.Rows; this.Rows = []; // Will be filled again this.CurRows = {}; this.AddRows(rows); }, ApplyFilter: function (filter, showAllChildNodes, callBackFoundRows) { filter = filter.toLowerCase(); if (filter == "") { this.Refresh(); // Collapse everything return; } // Show item and all parent nodes, display:none the rest this.style.display = "none"; // Faster when updating // First hide everything for (var item in this.CurRows) { var row = this.CurRows[item]; row.style.display = "none"; } var foundRows = []; // Then make everything matching visible, including all parents and optionally all child nodes var filterParts = filter.split(/;/g); for (var i = 0; i < filterParts.length; i++) { for (var item in this.CurRows) { var row = this.CurRows[item]; var txt = ""; if (row.SubList) { // Only look at the text of this element for (var j = 0; j < row.childNodes.length; j++) { if (row.childNodes[j] != row.SubList) txt += row.childNodes[j].innerText; } } else { txt = row.innerText; } if (txt.toLowerCase().indexOf(filterParts[i]) >= 0) { row.style.display = ""; foundRows.push(row); if (!this.AutoExpandChildNodesDuringFilter && row.className.indexOf("collapsed") < 0) { row.className = row.className.replace(/expanded/g, "") + " collapsed"; if (row.ExpandCollapseButton) row.ExpandCollapseButton.Update(); } if (showAllChildNodes && row.SubList) { var subLists = [row.SubList]; for (var j = 0; j < subLists.length; j++) { var curList = subLists[j]; var addClass = "expanded"; var removeClass = "collapsed"; var setStyle = ""; if (!this.AutoExpandChildNodesDuringFilter) { addClass = "collapsed"; removeClass = "expanded"; setStyle = "none"; } curList.style.display = setStyle; for (var n = 0; n < curList.childNodes.length; n++) { var li = curList.childNodes[n]; if (li.className.indexOf(addClass) < 0) { li.className = li.className.replace(removeClass, "") + " " + addClass; if (addClass == "expanded") this.Expanded(li.Data, false, curList); else this.Collapsed(li.Data, false, curList); if (li.ExpandCollapseButton) li.ExpandCollapseButton.Update(); } if (li.style) li.style.display = ""; if (li.SubList) subLists.push(li.SubList); } } } // Expand all items above while (row.parentNode.Rows == undefined) { row = row.parentNode; if (row.SubList && row.className.indexOf("expanded") < 0) { row.className = row.className.replace(/collapsed/g, "") + " expanded"; this.Expanded(row.Data, false, row); if (row.ExpandCollapseButton) row.ExpandCollapseButton.Update(); } row.style.display = ""; } } } } this.style.display = ""; if (callBackFoundRows) { callBackFoundRows(foundRows); } }, SelectRow: function (id) { // Select a single item and expand+scroll the tree to that item var curSelectedItem = this.querySelector(".selectedItem"); if (curSelectedItem) curSelectedItem.className = curSelectedItem.className.replace(/selectedItem/g, ""); var currentRow = this.CurRows["id" + id]; if (!currentRow) return; currentRow.className += " selectedItem"; var row = currentRow; row.style.display = ""; if (row.SubList) { this.Expanded(row.Data, false, row); row.className = row.className.replace(/collapsed/g, "") + " expanded"; row.SubList.style.display = ""; } while (row.parentNode.Rows == undefined) { row = row.parentNode; if (row.SubList) { this.Expanded(row.Data, false, row); row.className = row.className.replace(/collapsed/g, "") + " expanded"; } row.style.display = ""; } currentRow.scrollIntoView(); return currentRow; }, RowClick: function (rowObj, jsEvent) { } }; window.TK.AjaxTree = { _: window.TK.Tree, Url: null, Post: "", AjaxSettings: {}, Init: function () { this.Clear(); var obj = this; if (this.Url) { Ajax.do(this.Url, this.Post, function (response) { if (response && response.substr) response = JSON.parse(response); obj.Rows = response; obj.Refresh(); obj.Update(); }, undefined, this.AjaxSettings); } }, Update: function () { } };"use strict"; /* Minify Order(110) */ // Dragable items on a board window.TK.Dashboard = { _: "div", EnableMove: true, EnableCopy: true, EnableResize: true, EnableRemove: true, EnableEdit: true, EnableLimitX: true, EnableLimitY: true, EnableDrop: false, Spacing: 5, SnapSize: 100, EditMode: 1, // 0: None, 1: Show edit buttons when hovering, 2: Always show edit buttons className: "toolkitDashboard", DashboardItems: [], DefaultWidth: 600, AutoGrow: true, // Automatic increase size of this element (Adds [SnapSize] spacing to this div when moving/resizing elements) AutoShrink: false, AutoWidthCount: null, // When this is a positive value, the snap size will be adjusted automatically to make sure there are always X 'blocks' in the width Init: function () { this.SetEditMode(this.EditMode); if (this.DashboardItems) { var obj = this; setTimeout(function () { obj.Load(obj.DashboardItems); }, 1); } }, SetEditMode: function (newEditMode) { this.EditMode = newEditMode; var newClassName = this.className.replace(/toolkitDashboardEditable/g, "").replace(/toolkitDashboardAlwaysEditable/g, ""); if (this.EditMode == 1) newClassName += " toolkitDashboardEditable toolkitDragDropContainer"; else if (this.EditMode == 2) newClassName += " toolkitDashboardAlwaysEditable toolkitDragDropContainer"; this.className = newClassName; }, Save: function () { var state = []; var items = this.Elements.ToArray(); for (var i = 0; i < items.length; i++) { if (!items[i].Elements || !items[i].Elements.Content || !items[i].Elements.Content.StateProperties) continue; var c = items[i].Elements.Content; var stateProperties = {}; for (var j = 0; j < c.StateProperties.length; j++) { stateProperties[c.StateProperties[j]] = c[c.StateProperties[j]]; } state.push({ _: c.StateObjectName, State: stateProperties, Top: items[i].Top, Left: items[i].Left, Width: items[i].Width, Height: items[i].Height }); } this.DashboardItems = state; return JSON.stringify(state); }, Load: function (state) { if (state.substr) state = JSON.parse(state); this.Clear(); var regex = /[^A-Za-z0-9\.]/g; if (this.AutoWidthCount) { this.SnapSize = Math.floor(this.offsetWidth / this.AutoWidthCount); while (this.offsetWidth / (this.SnapSize + this.Spacing) < this.AutoWidthCount && this.SnapSize > 30) { this.SnapSize -= 5; } } for (var i = 0; i < state.length; i++) { if (regex.exec(state[i]._)) continue; var tmp = eval(state[i]._ + ".StateObjectName"); if (!tmp) continue; var stateProperties = state[i].State; stateProperties._ = state[i]._; this.AddDashboardItem(stateProperties, null, state[i].Left, state[i].Top, state[i].Width, state[i].Height); } }, CanDrop: function (elementOrTemplate) { if (!this.EnableDrop) return false; for (var i = 0; i < 30 && elementOrTemplate._ && !elementOrTemplate.StateObjectName; i++) elementOrTemplate = elementOrTemplate._; return (elementOrTemplate && elementOrTemplate.StateObjectName); }, Drop: function (elementOrTemplate, x, y) { if (!this.EnableDrop) return; var size = this.SnapSize + this.Spacing; y = Math.round(y / size); x = Math.round(x / size); if (y < 0) y = 0; if (x < 0) x = 0; var width = (elementOrTemplate.Width ? elementOrTemplate.Width : 1); var height = (elementOrTemplate.Height ? elementOrTemplate.Height : 1); if (!elementOrTemplate.appendChild) { // A template has been dropped return this.AddDashboardItem(elementOrTemplate, "block" + new Date().getTime(), x, y, width, height); } else { // An element has been dropped, add a block and then replace the content element var addedBlock = this.AddDashboardItem({}, "block" + new Date().getTime(), x, y, width, height); addedBlock.Elements.Content.Remove(); addedBlock.Elements.Content = elementOrTemplate; elementOrTemplate.Parent = addedBlock; addedBlock.appendChild(elementOrTemplate); return addedBlock; } }, AutoGrowHandler: function (restore) { if (restore) { this.className = this.className.replace(/toolkitDashboardEditing/g, ""); } else if (this.className.indexOf("Editing") < 0) { this.className += " toolkitDashboardEditing"; } if (!this.AutoGrow || this.offsetHeight == 0) return; var items = this.Elements.ToArray(); var maxBottomY = 0; for (var i = 0; i < items.length; i++) { if (!items[i].Elements || !items[i].Elements.Content || !items[i].Height) continue; var bottomY = this.TopOrLeftSquaresToPX(items[i].Top) + this.HeightOrWidthSquaresToPX(items[i].Height); if (maxBottomY < bottomY) maxBottomY = bottomY; } var newHeight = maxBottomY + (this.SnapSize + this.Spacing); if (!this.style.height || parseInt(this.style.height) < newHeight || this.AutoShrink) this.style.height = newHeight + "px"; }, TemplateDashboardItem: { style: { position: "absolute" }, DashboardElement: null, // Position/Sizes are in snap-index Left: 0, Top: 0, Width: 1, Height: 1, ontouchstart: function () { if (this.DashboardElement.EditMode == 0) return; if (this.className.indexOf("toolkitSelectedDashboardItem") < 0) { this.className += " toolkitSelectedDashboardItem"; } else { this.className = this.className.replace(/toolkitSelectedDashboardItem/g, ""); } }, Init: function () { var obj = this; if (this.DashboardElement.EnableMove) { this.Add({ _: "div", className: "tkDashboardButton", innerHTML: Svg.Icons.Move, ontouchstart: function (e) { this.onmousedown(e.touches[0]); e.stopPropagation(); }, onmousedown: function (e) { var x, y; try { x = e.pageX; y = e.pageY; } catch (errie) { var e2 = window.event; x = e2.clientX; y = e2.clientY; } var startX = x - obj.offsetLeft; var startY = y - obj.offsetTop; var startWidth = obj.offsetWidth; var startHeight = obj.offsetHeight; var totalWidth = obj.DashboardElement.offsetWidth; var totalHeight = obj.DashboardElement.offsetHeight; window.onmousemove = function (e) { var x, y; try { x = e.pageX; y = e.pageY; } catch (errie) { var e2 = window.event; x = e2.clientX; y = e2.clientY; } var newLeft = (x - startX); var newTop = (y - startY); var size = obj.DashboardElement.SnapSize + obj.DashboardElement.Spacing; newTop = Math.round(newTop / size); newLeft = Math.round(newLeft / size); if (newTop < 0) newTop = 0; if (newLeft < 0) newLeft = 0; totalWidth = obj.DashboardElement.offsetWidth; totalHeight = obj.DashboardElement.offsetHeight; if (obj.DashboardElement.EnableLimitX && totalWidth > 0 && (newLeft * size) + startWidth > totalWidth) newLeft = Math.floor((totalWidth - startWidth) / size); if (obj.DashboardElement.EnableLimitY && totalHeight > 0 && (newTop * size) + startHeight > totalHeight) newTop = Math.floor((totalHeight - startHeight) / size); if (newLeft < 0) newLeft = 0; if (newTop < 0) newTop = 0; obj.Left = newLeft; obj.Top = newTop; obj.SetSize(); obj.DashboardElement.AutoGrowHandler(); }; window.onmouseup = function () { window.onmousemove = null; window.onmouseup = null; window.onselectstart = null; window.ontouchend = null; window.ontouchmove = null; obj.DashboardElement.AutoGrowHandler(true); }; window.ontouchmove = function (e) { if (window.onmousemove) window.onmousemove(e.touches[0]); e.stopPropagation(); }; window.ontouchend = function (e) { if (window.onmouseup) window.onmouseup(); e.stopPropagation(); }; window.onselectstart = function () { return false; }; if (e && e.preventDefault) e.preventDefault(); else window.event.returnValue = false; } }, "MoveButton"); } if (this.DashboardElement.EnableResize) { this.Add({ _: "div", className: "tkDashboardButton", innerHTML: Svg.Icons.Resize, ontouchstart: function (e) { this.onmousedown(e.touches[0]); e.stopPropagation(); }, onmousedown: function (e) { var startX, startY; try { startX = e.pageX; startY = e.pageY; } catch (errie) { var e2 = window.event; startX = e2.clientX; startY = e2.clientY; } var startWidth = obj.offsetWidth; var startHeight = obj.offsetHeight; var totalWidth = obj.DashboardElement.offsetWidth; var totalHeight = obj.DashboardElement.offsetHeight; window.onmousemove = function (e) { var x, y; try { x = e.pageX; y = e.pageY; } catch (errie) { var e2 = window.event; x = e2.clientX; y = e2.clientY; } var newWidth = (x - startX) + startWidth; var newHeight = (y - startY) + startHeight; var size = obj.DashboardElement.SnapSize; newHeight = Math.round(newHeight / size); newWidth = Math.round(newWidth / size); var newWidthPx = ((newWidth * obj.DashboardElement.SnapSize) + ((newWidth - 1) * obj.DashboardElement.Spacing)); var newHeightPx = ((newHeight * obj.DashboardElement.SnapSize) + ((newHeight - 1) * obj.DashboardElement.Spacing)); totalWidth = obj.DashboardElement.offsetWidth; totalHeight = obj.DashboardElement.offsetHeight; if (obj.DashboardElement.EnableLimitX && totalWidth > 0 && obj.DashboardElement.TopOrLeftSquaresToPX(obj.Left) + newWidthPx > totalWidth) newWidth = Math.floor((totalWidth + obj.DashboardElement.Spacing) / (size + obj.DashboardElement.Spacing)) - obj.Left; if (obj.DashboardElement.EnableLimitY && totalHeight > 0 && obj.DashboardElement.TopOrLeftSquaresToPX(obj.Top) + newHeightPx > totalHeight) newHeight = Math.floor((totalHeight + obj.DashboardElement.Spacing) / (size + obj.DashboardElement.Spacing)) - obj.Top; obj.Width = newWidth; obj.Height = newHeight; obj.SetSize(true); obj.DashboardElement.AutoGrowHandler(); }; window.onmouseup = function () { window.onmousemove = null; window.onmouseup = null; window.onselectstart = null; window.ontouchend = null; window.ontouchmove = null; obj.DashboardElement.AutoGrowHandler(true); }; window.ontouchmove = function (e) { if (window.onmousemove) window.onmousemove(e.touches[0]); e.stopPropagation(); }; window.ontouchend = function (e) { if (window.onmouseup) window.onmouseup(); e.stopPropagation(); }; window.onselectstart = function () { return false; }; if (e && e.preventDefault) e.preventDefault(); else window.event.returnValue = false; } }, "ResizeButton"); } if (this.DashboardElement.EnableRemove) { this.Add({ _: "div", className: "tkDashboardButton", innerHTML: Svg.Icons.Close, ontouchstart: function (e) { e.stopPropagation(); }, onclick: function () { obj.Remove(); } }, "RemoveButton"); } if (this.DashboardElement.EnableEdit && this.Elements.Content && this.Elements.Content.Editor) { this.Add({ _: "div", className: "tkDashboardButton", innerHTML: Svg.Icons.Settings, ontouchstart: function (e) { e.stopPropagation(); }, onclick: function () { obj.Elements.Content.Add({ _: obj.Elements.Content.Editor }); } }, "EditButton"); } if (this.DashboardElement.EnableCopy) { this.Add({ _: "div", className: "tkDashboardButton", innerHTML: Svg.Icons.Copy, ontouchstart: function (e) { this.onmousedown(e.touches[0]); e.stopPropagation(); }, onmousedown: function (e) { var x, y; try { x = e.pageX; y = e.pageY; } catch (errie) { var e2 = window.event; x = e2.clientX; y = e2.clientY; } var startX = x - obj.offsetLeft; var startY = y - obj.offsetTop; var startWidth = obj.offsetWidth; var startHeight = obj.offsetHeight; var totalWidth = obj.DashboardElement.offsetWidth; var totalHeight = obj.DashboardElement.offsetHeight; var duplicate = null; window.onmousemove = function (e) { var x, y; try { x = e.pageX; y = e.pageY; } catch (errie) { var e2 = window.event; x = e2.clientX; y = e2.clientY; } var newLeft = (x - startX); var newTop = (y - startY); var size = obj.DashboardElement.SnapSize + obj.DashboardElement.Spacing; newTop = Math.round(newTop / size); newLeft = Math.round(newLeft / size); if (newTop < 0) newTop = 0; if (newLeft < 0) newLeft = 0; totalWidth = obj.DashboardElement.offsetWidth; totalHeight = obj.DashboardElement.offsetHeight; if (obj.DashboardElement.EnableLimitX && totalWidth > 0 && (newLeft * size) + startWidth > totalWidth) newLeft = Math.floor((totalWidth - startWidth) / size); if (obj.DashboardElement.EnableLimitY && totalHeight > 0 && (newTop * size) + startHeight > totalHeight) newTop = Math.floor((totalHeight - startHeight) / size); if (newLeft < 0) newLeft = 0; if (newTop < 0) newTop = 0; if (newLeft == obj.Left && newTop == obj.Top) return; if (!duplicate) { var c = obj.Elements.Content; var stateProperties = {} for (var i = 0; c.StateProperties && i < c.StateProperties.length; i++) { stateProperties[c.StateProperties[i]] = c[c.StateProperties[i]]; } stateProperties = JSON.parse(JSON.stringify(stateProperties)); // Make sure all objects are cloned stateProperties._ = c.StateObjectName; duplicate = obj.DashboardElement.AddDashboardItem(stateProperties); duplicate.Width = obj.Width; duplicate.Height = obj.Height; } duplicate.Left = newLeft; duplicate.Top = newTop; duplicate.SetSize(); obj.DashboardElement.AutoGrowHandler(); }; window.onmouseup = function () { window.onmousemove = null; window.onmouseup = null; window.onselectstart = null; window.ontouchend = null; window.ontouchmove = null; obj.DashboardElement.AutoGrowHandler(true); }; window.ontouchmove = function (e) { if (window.onmousemove) window.onmousemove(e.touches[0]); e.stopPropagation(); }; window.ontouchend = function (e) { if (window.onmouseup) window.onmouseup(); e.stopPropagation(); }; window.onselectstart = function () { return false; }; if (e && e.preventDefault) e.preventDefault(); else window.event.returnValue = false; } }, "CopyButton"); } this.SetSize(); }, SetSize: function (sizeActuallyChanged) { var newWidth = this.DashboardElement.HeightOrWidthSquaresToPX(this.Width) + "px"; var newHeight = this.DashboardElement.HeightOrWidthSquaresToPX(this.Height) + "px"; this.style.top = this.DashboardElement.TopOrLeftSquaresToPX(this.Top) + "px"; this.style.left = this.DashboardElement.TopOrLeftSquaresToPX(this.Left) + "px"; if (newWidth != this.style.width || newHeight != this.style.height) { this.style.width = this.DashboardElement.HeightOrWidthSquaresToPX(this.Width) + "px"; this.style.height = this.DashboardElement.HeightOrWidthSquaresToPX(this.Height) + "px"; } else { sizeActuallyChanged = false; } if (sizeActuallyChanged && this.Elements.Content.SizeChanged) this.Elements.Content.SizeChanged(); } }, TopOrLeftSquaresToPX: function (topOrLeft) { return (topOrLeft * (this.SnapSize + this.Spacing)); }, HeightOrWidthSquaresToPX: function (heightOrWidth) { return ((heightOrWidth * this.SnapSize) + ((heightOrWidth - 1) * this.Spacing)); }, AddDashboardItem: function (element, name, x, y, width, height) { if (!width) width = 1; if (!height) height = 1; if (x === undefined || x === null || y === null || y === undefined) { // Auto find available place var totalWidth = this.offsetWidth; if (totalWidth == 0) totalWidth = this.DefaultWidth; var totalWidthInSquares = Math.floor((totalWidth + this.Spacing) / (this.SnapSize + this.Spacing)); y = 0; var found = false; while (y < 100) { x = 0; while (x <= totalWidthInSquares - width) { // Check if there is overlap with existing items var items = this.Elements.ToArray(); found = true; for (var i = 0; i < items.length; i++) { if (!items[i].Elements || !items[i].Elements.Content || !items[i].Height) continue; if (x >= items[i].Left + items[i].Width || items[i].Left >= x + width) continue; if (y >= items[i].Top + items[i].Height || items[i].Top >= y + height) continue; found = false; break; } if (found) break; x++; } if (found) break; y++; } } var addedBlock = this.Add({ _: this.TemplateDashboardItem, DashboardElement: this, Elements: { Content: element }, Top: y, Left: x, Width: width, Height: height }, name); this.AutoGrowHandler(true); return addedBlock; } }; // Items with a saveable state window.TK.DashboardTemplates = {}; window.TK.DashboardTemplates.BaseWithEditor = { Editor: { _: TK.Popup, Title: "Edit block", Template: { Init: function () { var element = this.Parent.Parent; var properties = {}; for (var i = 0; i < element.StateProperties.length; i++) properties[element.StateProperties[i]] = element.Properties && element.Properties[element.StateProperties[i]] ? element.Properties[element.StateProperties[i]] : { }; this.Add({ _: TK.Form, Model: element, Fields: properties, IgnoreRest: true, ApplyToModelDirectly: true, Save: function () { element.Init(); } }); } } } }; window.TK.DashboardTemplates.Text = { _: window.TK.DashboardTemplates.BaseWithEditor, StateObjectName: "TK.DashboardTemplates.Text", StateProperties: ["Text"], Text: null, Init: function () { if (this.Text) { var obj = this; this.Clear(); this.Add({ _: "span", Init: function () { this.appendChild(document.createTextNode(obj.Text)); } }); } } };"use strict"; /* Minify Order(110) */ // TODO: Support for moment.js, country code: moment.tz.zone(timeZone).abbr(timeZoneOffset); window.TK.DateTime = { _: "div", className: "toolkitDateTime", MonthNames: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], EnableTime: true, EnableTimeZone: true, EnableRelative: false, TimeZone: "Local", // UTC, Local UseGlobalTimeZone: true, // If true, the time zone will be based on the static TK.DateTime.TimeZone , and not this instance AlwaysNavigateToStartOfDay: false, // When set to true, selecting a day will always set time to 00:00, even if there was already a value or the day is today WeekStart: 1, // 0 Sunday, 1 Monday ValueIsEpoch: false, Data: null, onchange: function () { }, readOnly: false, disabled: false, DisplayCodes: { EuropeAmsterdam: "NL", EuropeParis: "FR", EuropeLondon: "UK", EuropeDublin: "IE", EuropeLuxembourg: "LU", EuropeBerlin: "DE", EuropeBrussels: "BR", EuropeOslo: "NO", EuropeStockholm: "SE", AsiaTokyo: "JP", }, Init: function () { if (this.DataSettings) { var fields = ["ValueIsEpoch", "EnableTime", "EnableTimeZone", "EnableRelative", "TimeZone", "WeekStart", "UseGlobalTimeZone"]; for (var i = 0; i < fields.length; i++) { if (this.DataSettings[fields[i]] !== undefined) this[fields[i]] = this.DataSettings[fields[i]]; } } if (this.EnableRelative && this.Data == "now") { this.Data = "|"; } if (!this.EnableTimeZone) { this.UseGlobalTimeZone = false; this.TimeZone = "UTC"; } if (this.ValueIsEpoch && this.Data && (!this.Data.indexOf || this.Data == parseInt(this.Data).toString())) { this.Data = new Date(parseInt(this.Data) * 1000).toISOString(); } this.RenderDateInput(this.Elements.Selection, this.Data); this.RefreshDateInput(true); this.Elements.DateInputContainer.Elements.TimeZoneInfo.style.display = (this.EnableTimeZone ? "" : "none"); }, GetValue: function () { var isoDate = this.Data; if (this.EnableRelative && isoDate && isoDate.indexOf && isoDate.indexOf("|") >= 0) { return isoDate; } if (this.ValueIsEpoch) { return Math.floor(new Date(isoDate).getTime() / 1000); } if (this.EnableTime) return isoDate; if (!isoDate) return null; var dateObj = new Date(isoDate); if (this.GetTimeZone() == "UTC") { dateObj.setUTCHours(0); dateObj.setUTCMinutes(0); dateObj.setUTCSeconds(0); } else if (this.GetTimeZone() == "Local") { dateObj.setHours(0); dateObj.setMinutes(0); dateObj.setSeconds(0); } else { // TODO } return dateObj.toISOString(); }, Elements: { DateInputContainer: { className: "dateTimeContainer", Elements: { TimeZoneInfo: { className: "timeZoneInfo", onclick: function () { this.Parent.Parent.SetTimeZone(this.Parent.Parent.GetTimeZone() == "UTC" ? "Local" : "UTC"); this.Parent.Elements.DateInput.focus(); } }, DateInput: { onkeyup: function () { if (this.value == "") { this.className = ""; this.Parent.Parent.Data = null; return; } var v = this.value.replace(/\\/g, "-").replace(/\//g, "-").replace(/T/g, " ").replace(/\.000/g, "").replace(/Z/g, ""); var parts = v.split(" "); var dParts = parts[0].split("-"); var tParts = []; if (parts.length > 1) tParts = parts[1].split(":"); var year=0, month=1, day=1, hour=0, minute=0, second=0; if (dParts.length >= 3) { if (dParts[0].length > 2) { // Starts with year year = parseInt(dParts[0]); month = parseInt(dParts[1]); day = parseInt(dParts[2]); } else if (dParts[0].length == 2 && dParts[1].length == 2 && dParts[2].length == 2) { // dd-MM-yy day = parseInt(dParts[0]); month = parseInt(dParts[1]); year = 2000 + parseInt(dParts[2]); } else { day = parseInt(dParts[0]); month = parseInt(dParts[1]); year = parseInt(dParts[2]); } } if (tParts.length >= 2) { hour = parseInt(tParts[0]); minute = parseInt(tParts[1]); if (tParts.length >= 3) second = parseInt(tParts[2]); } var validDate = true; if (year < 1900 || year > 9999 || day > 31 || day < 1 || month < 1 || month > 12 || hour >= 24 || hour < 0 || minute < 0 || minute >= 60 || second < 0 || second >= 60) { validDate = false; } var tmpDateTime = null; var obj = this.Parent.Parent; if (obj.GetTimeZone() == "UTC") { tmpDateTime = new Date(year + "-" + obj.NumberFormat(month) + "-" + obj.NumberFormat(day) + "T" + obj.NumberFormat(hour) + ":" + obj.NumberFormat(minute) + ":" + obj.NumberFormat(second)+"Z"); } else { tmpDateTime = new Date(year, month - 1, day, hour, minute, second); } if (!validDate || isNaN(tmpDateTime.getHours())) { this.className = "invalidDate"; return; } this.className = ""; this.Parent.Parent.RenderDateInput(this.Parent.Parent.Elements.Selection, tmpDateTime.toISOString()); }, onblur: function () { if (this.Parent.Parent.readOnly || this.Parent.Parent.disabled) return; var obj = this; if (obj.Parent.Parent.Elements.Selection.InRelativeEditor) return; // Don't auto hide the relative editor this.TimeOut = setTimeout(function () { obj.TimeOut = 0; if (obj.Parent.Parent.Elements.Selection) { obj.Parent.Parent.Elements.Selection.style.display = "none"; obj.Parent.Parent.Elements.Selection.style.position = "absolute"; obj.Parent.Parent.Elements.Selection.style.top = ""; obj.Parent.Parent.Elements.Selection.style.left = ""; obj.Parent.Parent.appendChild(obj.Parent.Parent.Elements.Selection); // Move element back (so the next time the position will be correct as well) } }, 250); }, onfocus: function () { if (this.Parent.Parent.readOnly || this.Parent.Parent.disabled) return; if (this.TimeOut) clearTimeout(this.TimeOut); // TODO: Append the selection div to the body and use a fixed positioning so the selection div will be over anything this.Parent.Parent.Elements.Selection.style.display = ""; this.Parent.Parent.Elements.Selection.style.zIndex = "20000"; this.Parent.Parent.Elements.Selection.DetachElementFromParent(); } }, } }, Selection: { style: { display: "none"} } }, NumberFormat: function (d) { return d < 10 ? "0" + d : "" + d; }, FormatOffset: function (offsetInMinutes) { var direction = offsetInMinutes < 0 ? "-" : "+"; offsetInMinutes = Math.abs(offsetInMinutes); var hours = Math.floor(offsetInMinutes / 60); var minutes = offsetInMinutes % 60; return direction + this.NumberFormat(hours) + ":" + this.NumberFormat(minutes); }, RefreshDateInput: function (dontFocus) { var obj = this; if (this.EnableTimeZone) { if (this.GetTimeZone() == "UTC") { this.Elements.DateInputContainer.Elements.TimeZoneInfo.innerHTML = "UTC"; this.Elements.DateInputContainer.Elements.TimeZoneInfo.title = "Universal timezone"; } else if (this.GetTimeZone() == "Local") { if (this.Data) { this.Elements.DateInputContainer.Elements.TimeZoneInfo.innerHTML = this.FormatOffset(-(new Date(this.Data).getTimezoneOffset())); //this.Elements.DateInputContainer.Elements.TimeZoneInfo.style.display = ""; } else { this.Elements.DateInputContainer.Elements.TimeZoneInfo.innerHTML = this.FormatOffset(-(new Date().getTimezoneOffset())); //this.Elements.DateInputContainer.Elements.TimeZoneInfo.style.display = "none"; } try { var timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; this.Elements.DateInputContainer.Elements.TimeZoneInfo.title = timeZone; timeZone = timeZone.replace("/", ""); if (this.DisplayCodes[timeZone]) { this.Elements.DateInputContainer.Elements.TimeZoneInfo.innerHTML = this.DisplayCodes[timeZone]; } } catch (errie) { this.Elements.DateInputContainer.Elements.TimeZoneInfo.title = "Local timezone"; } } else { // TODO } } this.Elements.DateInputContainer.Elements.DateInput.readOnly = this.readOnly ? true : false; this.Elements.DateInputContainer.Elements.DateInput.disabled = this.disabled ? true : false; var isRelative = (this.EnableRelative && this.Data && this.Data.indexOf && this.Data.indexOf("|") >= 0); this.className = "toolkitDateTime" + (this.disabled ? " toolkitDateTimeDisabled" : "") + (this.readOnly ? " toolkitDateTimeReadOnly" : "") + (isRelative ? " toolkitDateTimeRelative" : ""); if (!this.Data) { this.Elements.DateInputContainer.Elements.DateInput.value = ""; return; } var d; if (isRelative) { // Parse and display d = window.TK.DateTimeRelativeToDateObj(this.Data, this.GetTimeZone()); } else { var d = new Date(this.Data); } if (this.GetTimeZone() == "UTC") { this.Elements.DateInputContainer.Elements.DateInput.value = obj.NumberFormat(d.getUTCFullYear()) + "-" + obj.NumberFormat(d.getUTCMonth() + 1) + "-" + obj.NumberFormat(d.getUTCDate()); if (this.EnableTime) this.Elements.DateInputContainer.Elements.DateInput.value += " " + obj.NumberFormat(d.getUTCHours()) + ":" + obj.NumberFormat(d.getUTCMinutes()) + ":" + obj.NumberFormat(d.getUTCSeconds()); } else if (this.GetTimeZone() == "Local") { this.Elements.DateInputContainer.Elements.DateInput.value = obj.NumberFormat(d.getFullYear()) + "-" + obj.NumberFormat(d.getMonth() + 1) + "-" + obj.NumberFormat(d.getDate()); if (this.EnableTime) this.Elements.DateInputContainer.Elements.DateInput.value += " " + obj.NumberFormat(d.getHours()) + ":" + obj.NumberFormat(d.getMinutes()) + ":" + obj.NumberFormat(d.getSeconds()); } else { // TODO: Implement custom timezones } if (!dontFocus) this.Elements.DateInputContainer.Elements.DateInput.focus(); }, GetTimeZone: function () { return this.UseGlobalTimeZone ? window.TK.DateTime.TimeZone : this.TimeZone; }, SetTimeZone: function (newTimeZone) { if (this.UseGlobalTimeZone) { window.TK.DateTime.TimeZone = newTimeZone; var allPickers = document.querySelectorAll(".toolkitDateTimeSelector"); for (var i = 0; i < allPickers.length; i++) { if (allPickers[i].Parent && allPickers[i].Parent.RenderDateInput && allPickers[i].Parent.UseGlobalTimeZone) { allPickers[i].Parent.RenderDateInput(allPickers[i], allPickers[i].DateISO); allPickers[i].Parent.RefreshDateInput(true); } } } else { this.TimeZone = newTimeZone; this.RenderDateInput(this.Elements.Selection, this.Data); this.RefreshDateInput(true); } }, RenderDateInput: function (element, dateISO) { var obj = this; if (this.NotFirst && dateISO != this.Data) { this.Data = dateISO; if (this.onchange) this.onchange(); } this.NotFirst = true; this.Data = dateISO; element.DateISO = dateISO; if (!dateISO) dateISO = new Date().toISOString(); if (this.EnableRelative && dateISO.indexOf("|") >= 0) { element.InRelativeEditor = true; // Show relative editor element.className = "toolkitDateTimeSelector toolkitDateTimeSelectorRelative"; element.innerHTML = ""; element.onclick = function () { }; var topButtonContainerR = document.createElement("DIV"); topButtonContainerR.className = "topButtonContainer"; var switchRelativeButtonR = document.createElement("DIV"); switchRelativeButtonR.className = "switchRelativeButton"; switchRelativeButtonR.innerHTML = "Relative Date"; switchRelativeButtonR.onclick = function () { // Switch back obj.RenderDateInput(element, window.TK.DateTimeRelativeToDateObj(dateISO, obj.GetTimeZone()).toISOString()); }; topButtonContainerR.appendChild(switchRelativeButtonR); element.appendChild(topButtonContainerR); var parts = dateISO.split(/\|/g); var lineContainer = document.createElement("DIV"); for (var i = 0; i < parts.length; i++) { var p = parts[i]; if (p.length == 0) continue; var line = document.createElement("DIV"); line.className = "toolkitRelativeDateLine"; var selectPart = document.createElement("SELECT"); selectPart.appendChild(new Option("Year", "y")); selectPart.appendChild(new Option("Month", "M")); selectPart.appendChild(new Option("Day", "d")); selectPart.appendChild(new Option("Hour", "H")); selectPart.appendChild(new Option("Minute", "m")); selectPart.appendChild(new Option("Weekday", "w")); selectPart.value = p.substr(0, 1); p = p.substr(1); line.appendChild(selectPart); var selectMutation = document.createElement("SELECT"); selectMutation.appendChild(new Option("=>", "")); selectMutation.appendChild(new Option("+")); selectMutation.appendChild(new Option("-")); selectMutation.value = ""; if (p.length > 0 && p.substr(0, 1) == "+" || p.substr(0, 1) == "-") { selectMutation.value = p.substr(0, 1); p = p.substr(1); } line.appendChild(selectMutation); var inputValue = document.createElement("INPUT"); line.appendChild(inputValue); if (p.length > 0) { inputValue.value = p; } var removeLineButton = document.createElement("BUTTON"); removeLineButton.innerHTML = "x"; removeLineButton.onclick = function () { this.parentNode.parentNode.removeChild(this.parentNode); }; removeLineButton.className = "removeLineButton"; line.appendChild(removeLineButton); line.Part = selectPart; line.Mutation = selectMutation; line.Value = inputValue; lineContainer.appendChild(line); } lineContainer.GetRelativeDate = function () { var str = "|"; for (var i = 0; i < this.childNodes.length; i++) { if (this.childNodes[i].Value.value != "" && !isNaN(parseInt(this.childNodes[i].Value.value))) str += this.childNodes[i].Part.value + this.childNodes[i].Mutation.value + this.childNodes[i].Value.value + "|"; } return str; }; element.appendChild(lineContainer); var addLineButton = document.createElement("BUTTON"); addLineButton.className = "toolkitAddLineButton"; addLineButton.innerHTML = "+"; addLineButton.onclick = function () { obj.RenderDateInput(element, lineContainer.GetRelativeDate() + "|d-1"); }; element.appendChild(addLineButton); var applyButton = document.createElement("BUTTON"); applyButton.className = "toolkitApplyButton"; applyButton.innerHTML = "Apply"; applyButton.onclick = function () { obj.RenderDateInput(element, lineContainer.GetRelativeDate()); obj.RefreshDateInput(); element.style.display = "none"; }; element.appendChild(applyButton); obj.Elements.DateInputContainer.Elements.DateInput.focus(); return; } element.InRelativeEditor = false; var dateObj = new Date(dateISO); element.className = "toolkitDateTimeSelector"; element.innerHTML = ""; element.onclick = function () { obj.Elements.DateInputContainer.Elements.DateInput.focus(); }; window.TK.DateTimeUpdateDateObject(dateObj, obj.GetTimeZone()); var getButtons = function (text, funcPrevious, funcNext) { var div = document.createElement("DIV"); div.className = "buttonLine"; div.PreviousButton = document.createElement("BUTTON"); div.PreviousButton.innerHTML = "<"; div.PreviousButton.className = "previousButton"; div.PreviousButton.onclick = funcPrevious; div.PreviousButton.type = "button"; div.PreviousButton.tabIndex = -1; div.StatusText = document.createElement("DIV"); div.StatusText.className = "statusText"; div.StatusText.innerHTML = text; div.NextButton = document.createElement("BUTTON"); div.NextButton.innerHTML = ">"; div.NextButton.onclick = funcNext; div.NextButton.className = "nextButton"; div.NextButton.type = "button"; div.NextButton.tabIndex = -1; div.appendChild(div.PreviousButton); div.appendChild(div.StatusText); div.appendChild(div.NextButton); return div; }; var topButtonContainer = document.createElement("DIV"); topButtonContainer.className = "topButtonContainer"; if (this.EnableTimeZone) { var switchUTCButton = document.createElement("DIV"); switchUTCButton.className = "switchUTCButton"; switchUTCButton.innerHTML = this.GetTimeZone() == "UTC" ? "UTC" : obj.FormatOffset(-dateObj.getTimezoneOffset()); switchUTCButton.tabIndex = -1; try { if (this.GetTimeZone() == "Local") { var timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; switchUTCButton.title = timeZone; timeZone = timeZone.replace("/", ""); if (this.DisplayCodes[timeZone]) { switchUTCButton.innerHTML = this.DisplayCodes[timeZone]; } } } catch (errie) { } switchUTCButton.onclick = function () { obj.SetTimeZone(obj.GetTimeZone() == "UTC" ? "Local" : "UTC"); }; switchUTCButton.title = dateISO + " - " + dateObj.toLocaleString(); topButtonContainer.appendChild(switchUTCButton); } if (this.EnableRelative) { var switchRelativeButton = document.createElement("DIV"); switchRelativeButton.innerHTML = "Fixed date"; switchRelativeButton.className = "switchRelativeButton"; switchRelativeButton.onclick = function () { // Switch to relative obj.RenderDateInput(element, "y" + dateObj.getFullYear() + "|M" + (dateObj.getMonth() + 1) + "|d" + dateObj.getDate() + "|H" + dateObj.getHours() + "|m" + dateObj.getMinutes()); }; switchRelativeButton.tabIndex = -1; topButtonContainer.appendChild(switchRelativeButton); } element.appendChild(topButtonContainer); element.appendChild(getButtons(dateObj.getFullYear(), function () { dateObj.setFullYear(dateObj.getFullYear() - 1); obj.RenderDateInput(element, dateObj.toISOString()); obj.RefreshDateInput(); },function () { dateObj.setFullYear(dateObj.getFullYear() + 1); obj.RenderDateInput(element, dateObj.toISOString()); obj.RefreshDateInput(); obj.Elements.DateInputContainer.Elements.DateInput.focus(); })); element.appendChild(getButtons(obj.MonthNames[dateObj.getMonth()], function () { var expectedMonth = (dateObj.getMonth() - 1) % 12; dateObj.setMonth(dateObj.getMonth() - 1); if (dateObj.getMonth() != expectedMonth) dateObj.setDate(dateObj.getDate() - 1); obj.RenderDateInput(element, dateObj.toISOString()); obj.RefreshDateInput(); }, function () { var expectedMonth = (dateObj.getMonth() + 1) % 12; dateObj.setMonth(dateObj.getMonth() + 1); if (dateObj.getMonth() != expectedMonth) dateObj.setDate(dateObj.getDate() - 1); obj.RenderDateInput(element, dateObj.toISOString()); obj.RefreshDateInput(); })); var daySelection = getButtons("", function () { dateObj.setDate(dateObj.getDate() - 1); obj.RenderDateInput(element, dateObj.toISOString()); obj.RefreshDateInput(); }, function () { dateObj.setDate(dateObj.getDate() + 1); obj.RenderDateInput(element, dateObj.toISOString()); obj.RefreshDateInput(); }); daySelection.StatusText.className = "statusText daySelection"; var firstDay = new Date(dateISO); firstDay.setDate(1); var getDay = function (dateObj) { return (dateObj.getDay() - obj.WeekStart) < 0 ? 7 + (dateObj.getDay() - obj.WeekStart) : (dateObj.getDay() - obj.WeekStart); }; var lineCount = 0; for (var i = 1 - getDay(firstDay); i <= 31 || lineCount < 7; i++) { if (lineCount == 7) { daySelection.StatusText.appendChild(document.createElement("BR")); lineCount = 0; } lineCount++; var tmpDateObj = new Date(dateISO); var curMonth = tmpDateObj.getMonth(); tmpDateObj.setDate(i); if (i >= 28 && lineCount == 1 && tmpDateObj.getMonth() != curMonth) break; var dayItem = document.createElement("BUTTON"); dayItem.innerHTML = tmpDateObj.getDate(); dayItem.className = "dayItem " + (i == dateObj.getDate() ? "selected" : "") + (tmpDateObj.getMonth() != curMonth ? " otherMonth" : ""); //dayItem.title = tmpDateObj.toISOString(); dayItem.DateIndex = i; dayItem.tabIndex = -1; dayItem.onclick = function (e) { dateObj.setDate(this.DateIndex); dateObj.setMilliseconds(0); if (obj.AlwaysNavigateToStartOfDay || (!obj.Data && (dateObj.getDate() != new Date().getDate() || dateObj.getDay() != new Date().getDay() || dateObj.getFullYear() != new Date().getFullYear()))) { // Different day, default to 00:00:00 dateObj.setHours(0); dateObj.setMinutes(0); dateObj.setSeconds(0); } obj.RenderDateInput(element, dateObj.toISOString()); obj.RefreshDateInput(true); var event = e || window.event; event.stopPropagation(); return false; }; daySelection.StatusText.appendChild(dayItem); } element.appendChild(daySelection); if (this.EnableTime) { element.appendChild(getButtons(obj.NumberFormat(dateObj.getHours()) + ":" + obj.NumberFormat(dateObj.getMinutes()) + ":" + obj.NumberFormat(dateObj.getSeconds()), function () { dateObj.setUTCMinutes(dateObj.getUTCMinutes() - 15); obj.RenderDateInput(element, dateObj.toISOString()); obj.RefreshDateInput(); }, function () { dateObj.setUTCMinutes(dateObj.getUTCMinutes() + 15); obj.RenderDateInput(element, dateObj.toISOString()); obj.RefreshDateInput(); })); } //element.appendChild(document.createTextNode(dateObj.toString())); }, Destroy: function () { if (this.Elements.Selection) this.Elements.Selection.Remove(); } }; window.TK.DateTime.TimeZone = "Local"; window.TK.DateTimeUpdateDateObject = function (dateObj, timeZone) { if (timeZone == "UTC") { dateObj.getMonth = dateObj.getUTCMonth; dateObj.setMonth = dateObj.setUTCMonth; dateObj.getHours = dateObj.getUTCHours; dateObj.setHours = dateObj.setUTCHours; dateObj.getMinutes = dateObj.getUTCMinutes; dateObj.setMinutes = dateObj.setUTCMinutes; dateObj.getSeconds = dateObj.getUTCSeconds; dateObj.setSeconds = dateObj.setUTCSeconds; dateObj.getDate = dateObj.getUTCDate; dateObj.setDate = dateObj.setUTCDate; } }; window.TK.DateTimeRelativeToDateObj = function (dateStr, timeZone) { var dateObj = new Date(); window.TK.DateTimeUpdateDateObject(dateObj, timeZone); dateObj.setSeconds(0); dateObj.setMilliseconds(0); var parts = dateStr.split('|'); for (var i = 0; i < parts.length; i++) { if (parts[i].length < 2) continue; if (parts[i][1] == "+" || parts[i][1] == "-") { var value = parseInt(parts[i].substr(2)); if (parts[i][1] == "-") value = -value; if (parts[i][0] == "y") dateObj.setFullYear(dateObj.getFullYear() + value); else if (parts[i][0] == "M") dateObj.setMonth(dateObj.getMonth() + (value)); else if (parts[i][0] == "d") dateObj.setDate(dateObj.getDate() + value); else if (parts[i][0] == "H") dateObj.setHours(dateObj.getHours() + value); else if (parts[i][0] == "m") dateObj.setMinutes(dateObj.getMinutes() + value); else if (parts[i][0] == "s") dateObj.setSeconds(dateObj.getSeconds() + value); else if (parts[i][0] == "w") { var realModification = Math.abs(value); if (realModification == 7) realModification = 0; dateObj.setDate(dateObj.getDate() + (realModification - dateObj.getDay())); if (dateObj.getDay() < realModification) { if (value < 0) dateObj.setDate(dateObj.getDate() - 7); } else { if (value > 0) dateObj.setDate(dateObj.getDate() + 7); } } } else { // Set var value = parseInt(parts[i].substr(1)); if (parts[i][0] == "y") dateObj.setFullYear(value); else if (parts[i][0] == "M") dateObj.setMonth(value - 1); else if (parts[i][0] == "d") dateObj.setDate(value); else if (parts[i][0] == "H") dateObj.setHours(value); else if (parts[i][0] == "m") dateObj.setMinutes(value); else if (parts[i][0] == "s") dateObj.setSeconds(value); else if (parts[i][0] == "w") { var isoDay = dateObj.getDay(); if (isoDay == 0) isoDay = 7; dateObj.setDate(dateObj.getDate() + (value - isoDay)); } } } return dateObj; }; if (window.TK.Form) { window.TK.Form.DefaultTemplates.datetime = { _: TK.DateTime }; window.TK.Form.DefaultTemplates.date = { _: TK.DateTime, EnableTime: false }; window.TK.Form.DefaultTemplates.datetimeasp = { className: "dateTimeAsp", Init: function () { var isoString = null; if (this.Data) { isoString = window.ConvertFromASPTime(this.Data); if (!isoString) isoString = new Date().toISOString(); } this.Add({ _: TK.DateTime, Data: isoString, disabled: this.disabled, readOnly: this.readOnly, onchange: this.onchange, onfocus: this.onfocus, onblur: this.onblur, DataSettings: this.DataSettings }, "DateInput"); }, GetValue: function () { var value = this.Elements.DateInput.GetValue(); if (!value) return value; var time = new Date(value).getTime(); if (isNaN(time)) { alert("Date time value of " + value + " is not valid."); throw "Date time value of " + value + " is not valid."; } return "/Date(" + time + ")/"; } }; } "use strict"; /* Minify Order(110) */ TK.HtmlEditor = { className: "toolkitHtmlEditor", FillContainer: false, Buttons: ["IncreaseFontSize", "DecreaseFontSize", "Bold", "Italic", "Underline", "AlignLeft", "AlignCenter", "AlignRight", "Indent", "Outdent", "Paragraph", "Header1", "Header2", "Header3", "CodeBlock", "QuoteBlock"], __RecursivePropertiesButtonTemplates: true, EnableHTMLPasting: true, RemoveScripts: true, RemoveScriptsHandler: function (html) { var len = -1; while (len != html.length) { len = html.length; html = html.replace(/<(script|link|iframe|noscript|meta|object|embed|frameset|style)/ig, " 1) this.Parent.CurSize--; document.execCommand("fontSize", false, this.Parent.CurSize); this.Near("Editor").focus(); } }, Bold: { innerHTML: Svg.Icons.TextBold, onmousedown: function () { document.execCommand("bold", false, null); this.Near("Editor").focus(); } }, Italic: { innerHTML: Svg.Icons.TextItalic, onmousedown: function () { document.execCommand("italic", false, null); this.Near("Editor").focus(); } }, Underline: { innerHTML: Svg.Icons.TextUnderline, onmousedown: function () { document.execCommand("underline", false, null); this.Near("Editor").focus(); } }, AlignLeft: { innerHTML: Svg.Icons.TextAlignLeft, onmousedown: function () { document.execCommand("justifyLeft", false, null); this.Near("Editor").focus(); } }, AlignCenter: { innerHTML: Svg.Icons.TextAlignCenter, onmousedown: function () { document.execCommand("justifyCenter", false, null); this.Near("Editor").focus(); } }, AlignRight: { innerHTML: Svg.Icons.TextAlignRight, onmousedown: function () { document.execCommand("justifyRight", false, null); this.Near("Editor").focus(); } }, Indent: { innerHTML: Svg.Icons.TextIndent, onmousedown: function () { document.execCommand("indent", false, null); this.Near("Editor").focus(); } }, Outdent: { innerHTML: Svg.Icons.TextOutdent, onmousedown: function () { document.execCommand("outdent", false, null); this.Near("Editor").focus(); } }, Paragraph: { title: "Paragraph", innerHTML: Svg.Icons.TextParagraph, onmousedown: function () { document.execCommand("formatBlock", false, "p"); this.Near("Editor").focus(); } }, Header1: { title: "Header 1", innerHTML: Svg.Icons.TextHeader1, onmousedown: function () { document.execCommand("formatBlock", false, "h1"); this.Near("Editor").focus(); } }, Header2: { title: "Header 2", innerHTML: Svg.Icons.TextHeader2, onmousedown: function () { document.execCommand("formatBlock", false, "h2"); this.Near("Editor").focus(); } }, Header3: { title: "Header 3", innerHTML: Svg.Icons.TextHeader3, onmousedown: function () { document.execCommand("formatBlock", false, "h3"); this.Near("Editor").focus(); } }, CodeBlock: { title: "Code block", innerHTML: Svg.Icons.TextCodeBlock, onmousedown: function () { document.execCommand("formatBlock", false, "pre"); this.Near("Editor").focus(); } }, QuoteBlock: { title: "Quote block", innerHTML: Svg.Icons.TextQuoteBlock, onmousedown: function () { document.execCommand("formatBlock", false, "blockquote"); } } }, Data: null, Init: function () { if (this.Data) this.Elements.Editor.innerHTML = this.RemoveScripts ? this.RemoveScriptsHandler(this.Data) : this.Data; if (this.FillContainer) { this.style.position = "absolute"; this.style.top = "0px"; this.style.left = "0px"; this.style.right = "0px"; this.style.bottom = "0px"; this.Elements.MenuBar.style.position = "absolute"; this.Elements.MenuBar.style.top = "0px"; this.Elements.MenuBar.style.left = "0px"; this.Elements.MenuBar.style.right = "0px"; this.Elements.MenuBar.style.height = "35px"; this.Elements.Editor.style.position = "absolute"; this.Elements.Editor.style.top = "35px"; this.Elements.Editor.style.left = "0px"; this.Elements.Editor.style.right = "0px"; this.Elements.Editor.style.bottom = "0px"; } }, GetValue: function () { return this.Elements.Editor.innerHTML.replace(//g, "").replace(/<\/b>/g, "").replace(//g, "").replace(/<\/i>/g, ""); }, Elements: { MenuBar: { CurSize: 3, Init: function () { for (var i = 0; i < this.Parent.Buttons.length;i++) { if (this.Parent.ButtonTemplates[this.Parent.Buttons[i]]) this.Add({ _: this.Parent.ButtonTemplates[this.Parent.Buttons[i]], onmouseup: function () { this.Near("Editor").focus(); } }); } }, onselectstart: function () { return false; } }, Editor: { contentEditable: true, onclick: function () { this.Near("MenuBar").CurSize = 3; // TODO: Handle this a lot better.. but at least this works in Chrome }, onpaste: function (e) { if (e.clipboardData) { e.preventDefault(); if (this.Parent.EnableHTMLPasting && e.clipboardData.types && e.clipboardData.types.indexOf("text/html") >= 0) { try { var html = e.clipboardData.getData("text/html"); document.execCommand("insertHTML", false, this.Parent.RemoveScripts ? this.Parent.RemoveScriptsHandler(html) : html); return; } catch (errie) { } // If insertHTML is not supported for any reason, we will still paste it as text } var text = e.clipboardData.getData("text"); document.execCommand("insertText", false, text); } }, onkeydown: function (e) { if (e.keyCode === 13 && !e.shiftKey) { this.RemoveFormat = true; } }, onkeyup: function (e) { if (this.RemoveFormat) { document.execCommand("formatBlock", false, "div"); this.RemoveFormat = null; } } } } }; if (window.TK.Form) { window.TK.Form.DefaultTemplates.html = { _: TK.HtmlEditor }; } "use strict"; /* Minify Skip */ /* Minify Order(150) */ TK.Draw = { _: "canvas", Width: 100, Height: 100, Scale: 2, // Rendering scale, increase to keep sharpness when zooming in on the page but decrease performance Zoom: 1, ViewPortX: 0, ViewPortY: 0, EnableNavigation: false, // If true, the canvas can be dragged around and zoomed in/out using mouse wheel EnableZoom: false, MinZoom: 0.2, MaxZoom: 10, Animations: [], Init: function () { this.Context = this.GetContext(); this.Context.CanvasObj = this; this.SetSize(this.Width, this.Height); }, SetSize: function (width, height, parsePositionTags) { this.Width = width; this.Height = height; this.width = width * this.Scale; this.height = height * this.Scale; this.style.width = width + "px"; this.style.height = height + "px"; if (parsePositionTags && this.SortedElements) { for (var i = 0; i < this.SortedElements.length; i++) { if (this.SortedElements[i]._NormalizePositions !== false) TK.Draw.SetPositionsUsingPositionProperty(this.SortedElements[i]); if (this.SortedElements[i].Resize) this.SortedElements[i].Resize(); } } this.Refresh(); }, GetContext: function () { return this.getContext("2d"); }, Refresh: function (skipSortElements) { if (this.RefreshAlreadyQueued) { if (!skipSortElements) this.ForceSortElements = true; return; } if (this.width != this.Width * this.Scale || this.height != this.Height * this.Scale) { this.width = this.Width; this.height = this.Height; } var obj = this; var hasAnimation = this.ProcessAnimations(); // TODO: If EnableNavigation is true, Draw on an offscreen canvas // TODO: Find a way to only redraw whats needed this.Context.setTransform(this.Scale, 0, 0, this.Scale , 0, 0); this.Context.clearRect(0, 0, this.Width, this.Height); if (!skipSortElements || !this.SortedElements || this.ForceSortElements) { this.SortElements(); } for (var i = 0; i < this.SortedElements.length; i++) { this.Context.OffsetX = -this.ViewPortX; this.Context.OffsetY = -this.ViewPortY; this.Context.Scale = this.Scale * this.Zoom; if (this.SortedElements[i].Draw) this.SortedElements[i].Draw(this.Context); } if (hasAnimation) { this.RefreshAlreadyQueued = true; requestAnimationFrame(function () { obj.RefreshAlreadyQueued = false; obj.Refresh(true); }); } }, ProcessAnimations: function () { // Process animations var cur = new Date().getTime(); var hasAnimation = false; for (var i = 0; i < this.Animations.length; i++) { if (!this.Animations[i]) continue; var a = this.Animations[i]; var r = (cur - a.S) / a.L; // 500 - 400 = 100 / 400 = 0.25 if (r >= 1) { r = 1; this.Animations[i] = null; if (a.I.AnimationEnded) a.I.AnimationEnded(a.P); if (a.AnimationEnded) a.AnimationEnded(a.P); } if (Array.isArray(a.O)) { var rgba = [0, 0, 0, 0]; for (var j = 0; j < a.O.length; j++) { if (Array.isArray(a.O[j])) { // Point array for (var n = 0; n < a.O[j].length; n++) { a.I[a.P][j][n] = a.E(a.O[j][n], a.T[j][n], r); } } else if (a.O.length == 4) { // Colors rgba[j] = a.E(a.O[j], a.T[j], r); } } if (a.O.length == 4) { a.I[a.P] = "rgba(" + rgba.join(",") + ")"; } } else { a.I[a.P] = a.E(a.O, a.T, r); } //a.I[a.P] = a.O + ((a.T - a.O) * r); hasAnimation = true; if (a.I.Invalidate) { a.I.Invalidate(); } } return hasAnimation; }, SortElements: function () { this.ForceSortElements = false; this.SortedElements = []; for (var ele in this.Elements) { if (this.Elements[ele].Draw) { this.SortedElements.push(this.Elements[ele]); } } this.SortedElements = this.SortedElements.OrderBy(function (a) { return a.ZIndex; }); }, onclick: function (e) { this.HandleMouseEvent(e, "Click"); }, onmousemove: function (e) { this.HandleMouseEvent(e, "MouseOver"); this.HandleMouseEvent(e, "MouseMove"); this.HandleMouseEvent(e, "MouseOut"); }, ontouchmove: function (e) { this.HandleMouseEvent(e, "MouseOver"); this.HandleMouseEvent(e, "MouseMove"); this.HandleMouseEvent(e, "MouseOut"); e.preventDefault(); }, onmouseout: function (e) { this.HandleMouseEvent(e, "MouseOut"); }, onmousedown: function (e) { this.HandleMouseEvent(e, "MouseDown"); }, ontouchstart: function (e) { this.HandleMouseEvent(e, "MouseDown"); e.preventDefault(); }, onmouseup: function (e) { this.HandleMouseEvent(e, "MouseUp"); }, ontouchend: function (e) { this.HandleMouseEvent(e, "MouseUp"); e.preventDefault(); }, /*onpointerdown: function (e) { this.HandleMouseEvent(e, "MouseDown"); }, onpointermove: function (e) { this.HandleMouseEvent(e, "MouseOver"); this.HandleMouseEvent(e, "MouseMove"); this.HandleMouseEvent(e, "MouseOut"); }, onpointerup: function (e) { this.HandleMouseEvent(e, "MouseUp"); },*/ onwheel: function (event) { if (this.EnableZoom) { if (!event) event = window.event; if (event.preventDefault) event.preventDefault(); var zoomInPos = [this.ViewPortX + (this.Width / 2 / this.Zoom), this.ViewPortY + (this.Height / 2 / this.Zoom)]; this.Zoom -= event.deltaY < 0 ? -0.25 : 0.25; if (this.Zoom < this.MinZoom) this.Zoom = this.MinZoom; else if (this.Zoom > this.MaxZoom) this.Zoom = this.MaxZoom; // Adjust viewport as we want to zoom in with zoomInPos as center this.Center(zoomInPos[0], zoomInPos[1]); this.Refresh(true); } }, Center: function (x, y, animateLength, animateEase) { // world space px // Adjust ViewPortX and ViewPortY so that the x and y positions are in the middle x = x - ((this.Width / 2) / this.Zoom); // world space px y = y - ((this.Height / 2) / this.Zoom); if (animateLength) { this.Animations.push({ I: this, P: "ViewPortX", O: this.ViewPortX, T: x, L: animateLength, E: animateEase ? animateEase : TK.Draw.EaseExponential, S: new Date().getTime() }); this.Animations.push({ I: this, P: "ViewPortY", O: this.ViewPortY, T: y, L: animateLength, E: animateEase ? animateEase : TK.Draw.EaseExponential, S: new Date().getTime() }); this.Refresh(); } else { this.ViewPortX = x; this.ViewPortY = y; } }, HandleMouseEvent: function (e, func) { var eventHandled = false; var x, y; try { x = e.clientX; y = e.clientY; } catch (errie) { var e2 = window.event; x = e2.clientX; y = e2.clientY; } if (e && e.changedTouches && e.changedTouches.length > 0) { x = e.changedTouches[0].clientX; y = e.changedTouches[0].clientY; } var rect = this.getBoundingClientRect(); // Make sure top/left is always 0,0, then Compensate for the zoom level, then Add the offset from the viewport x = ((x - rect.left) / this.Zoom) + this.ViewPortX; y = ((y - rect.top) / this.Zoom) + this.ViewPortY; var stoppedPropagation = false; if (!this.SortedElements || this.ForceSortElements) { this.SortElements(); } for (var i = this.SortedElements.length - 1; i >= 0; i--) { var el = this.SortedElements[i]; if (!el[func] || !el.GetRect) continue; var r = el.GetRect(); if (r === null) continue; //r[0] -= this.ViewPortX; //r[1] -= this.ViewPortY; var match = false; for (var j = 0; this.CurrentMouseDownElements && j < this.CurrentMouseDownElements.length; j++) { if (this.CurrentMouseDownElements[j] == el) { match = true; break; } } if (match || (r[0] < x && r[0] + r[2] > x && r[1] < y && r[1] + r[3] > y && (!el.CheckMouseOver || el.CheckMouseOver(x, y))) && !stoppedPropagation) { if (func == "MouseDown" && this.CurrentMouseDownElements && this.CurrentMouseDownElements.indexOf(el) >= 0) { el.CurrentlyMouseOver = true; eventHandled = true; continue; } if (func != "MouseOut" && (func != "MouseOver" || !el.CurrentlyMouseOver)) { if (el[func](x, y) === true) { stoppedPropagation = true; el.StoppedPropagation = true; } eventHandled = true; } if (el.CurrentlyMouseOver && el.StoppedPropagation) { stoppedPropagation = true; } if (func == "MouseDown") { if (!this.CurrentMouseDownElements) this.CurrentMouseDownElements = []; this.CurrentMouseDownElements.push(el); } el.CurrentlyMouseOver = true; } else if (func == "MouseOut" && el.CurrentlyMouseOver) { el[func](x, y); eventHandled = true; el.CurrentlyMouseOver = false; el.StoppedPropagation = false; } } if (func == "MouseUp" && this.CurrentMouseDownElements) { this.CurrentMouseDownElements = null; } x = (x - this.ViewPortX) * this.Zoom; y = (y - this.ViewPortY) * this.Zoom; if (this.CurrentCanvasInteraction) { if (func == "MouseMove") { this.ViewPortX = this.CurrentCanvasInteraction[2] + ((this.CurrentCanvasInteraction[0] - x) / this.Zoom); this.ViewPortY = this.CurrentCanvasInteraction[3] + ((this.CurrentCanvasInteraction[1] - y) / this.Zoom); } else if (func == "MouseUp") { this.CurrentCanvasInteraction = null; } } else if (!eventHandled && !this.CurrentMouseDownElements && this.EnableNavigation && func == "MouseDown") { // Interaction on the canvas itself this.CurrentCanvasInteraction = [x, y, this.ViewPortX, this.ViewPortY]; } this.Refresh(); } }; TK.Draw.AnimationsEnabled = true; TK.Draw.AnchorLeft = 1; TK.Draw.AnchorCenter = 2; TK.Draw.AnchorRight = 4; TK.Draw.AnchorTop = 8; TK.Draw.AnchorMiddle = 16; TK.Draw.AnchorBottom = 32; TK.Draw.SmoothNone = 0; TK.Draw.SmoothQuadratic = 1; // Quadratic curvers with the center in between the two points TK.Draw.SmoothCorners = 2; // Only use horizontal and vertical lines, or small 90 degree corners TK.Draw.DirectionTop = 0; TK.Draw.DirectionRight = 1; TK.Draw.DirectionBottom = 2; TK.Draw.DirectionLeft = 3; TK.Draw.EaseLinear = function (a, b, r) { return a + ((b - a) * r); }; TK.Draw.EaseExponential = function (a, b, r) { var m; if (r < 0.5) { m = ((r == 0) ? 0 : Math.pow(2, 10 * (r * 2 - 1)) - 0.001) * 0.5; } else { r = (r * 2) - 1; m = (r == 1) ? 1 : (-Math.pow(2, -10 * r) + 1); m = 0.5 + 0.5 * m; } return a + ((b - a) * m); }; TK.Draw.EaseBack = function (a, b, r) { return a + ((b - a) * (r * r * ((2.70158) * r - 1.70158))); }; TK.Draw.EaseCircular = function(a, b, r) { return a + ((b - a) * -(Math.sqrt(1 - r * r) - 1.0)); }; TK.Draw.EaseBounce = function (a, b, r) { var multiplier = 1; if (r < (1 / 2.75)) { multiplier = 7.5625 * r * r; } else if (r < (2 / 2.75)) { var t = r - (1.5 / 2.75); multiplier = 7.5625 * t * t + 0.75; } else if (r < (2.5 / 2.75)) { var t = r - (2.25 / 2.75); multiplier = 7.5625 * t * t + 0.9375; } else { var t = r - (2.625 / 2.75); multiplier = 7.5625 * t * t + 0.984375; } return a + ((b - a) * multiplier); }; TK.Draw.EaseCubic = function (a, b, r) { return a + ((b - a) * (r * r * r)); }; TK.Draw.EaseElastic = function (a, b, r) { return a + ((b - a) * (1 + Math.pow(2, -10 * r) * Math.sin((r - (0.3 / 4)) * (Math.PI * 2) / 0.3))); }; TK.Draw.EaseSine = function (a, b, r) { return a + ((b - a) * (-Math.cos(r * (Math.PI / 2)) + 1)); }; TK.Draw.EaseStrong = function (a, b, r) { return a + ((b - a) * (r * r * r * r * r)); }; TK.Draw.GetColor = function (s) { if (s.substr(0, 4) == "rgba") { return s.replace("rgba(", "").replace(")", "").split(",").Select(function (a) { return parseFloat(a); }); } else if (s.substr(0, 3) == "rgb") { return (s.replace("rgb(", "").replace(")", "") + ",1").split(",").Select(function (a) { return parseFloat(a); }); } else if (s.substr(0, 1) == "#") { var c = s.substring(1).split(''); if (c.length == 3) c = [c[0], c[0], c[1], c[1], c[2], c[2]]; c = '0x' + c.join(''); return [(c >> 16) & 255, (c >> 8) & 255, c & 255, 1]; } }; TK.Draw.ColorToDifferentColor = function (s, s2, ratio) { s = TK.Draw.GetColor(s); s2 = TK.Draw.GetColor(s2); if (s.length < 4) s.push(1); if (s2.length < 4) s2.push(1); for (var i = 0; i < 4; i++) { s[i] = TK.Draw.EaseLinear(s[i], s2[i], ratio); } return "rgba(" + s.join(",") + ")"; }; TK.Draw.ValueToPx = function (v, curIsWidth, totalWidth, totalHeight) { if (!v.substr) return v; var total = curIsWidth ? totalWidth : totalHeight; if (v.indexOf("px") >= 0) return parseFloat(v.replace("px", "")); if (v.indexOf("%") >= 0) return total * (parseFloat(v.replace("%", "")) / 100); if (v.indexOf("vw") >= 0) return totalWidth * (parseFloat(v.replace("%", "")) / 100); if (v.indexOf("vh") >= 0) return totalHeight * (parseFloat(v.replace("%", "")) / 100); return parseFloat(v); }; TK.Draw.SetPositionsUsingPositionProperty = function (drawableObject) { var t = drawableObject; if (!t._Position) return; var p = t.Parent; while (p !== undefined && p.ProcessAnimations === undefined) // find the TK.Draw component p = p.Parent; if (!p) return; var anchorV = TK.Draw.AnchorTop | TK.Draw.AnchorMiddle | TK.Draw.AnchorBottom; var anchorH = TK.Draw.AnchorLeft | TK.Draw.AnchorCenter | TK.Draw.AnchorRight; var p = window.TK.ParsePosition(t._Position); var totalWidth = t.Parent.Width; var totalHeight = t.Parent.Height; var isSet = function (v) { return !(v === undefined || v === null); }; if (isSet(p[0]) && isSet(p[2])) { t.Y = TK.Draw.ValueToPx(p[0], false, totalWidth, totalHeight); // 50% -> 150px t.H = (totalHeight - TK.Draw.ValueToPx(p[2], false, totalWidth, totalHeight)) - t.Y; // 25% -> (300 - 75) = 225 - 150 = 75 t.Anchor = (t.Anchor & anchorH) | TK.Draw.AnchorTop; } else if (isSet(p[0])) { t.Y = TK.Draw.ValueToPx(p[0], false, totalWidth, totalHeight); t.Anchor = (t.Anchor & anchorH) | TK.Draw.AnchorTop; } else if (isSet(p[2])) { t.Y = totalHeight - TK.Draw.ValueToPx(p[2], false, totalWidth, totalHeight); t.Anchor = (t.Anchor & anchorH) | TK.Draw.AnchorBottom; } if (isSet(p[1]) && isSet(p[3])) { t.X = TK.Draw.ValueToPx(p[3], true, totalWidth, totalHeight); t.W = (totalWidth - TK.Draw.ValueToPx(p[1], true, totalWidth, totalHeight)) - t.X; t.Anchor = (t.Anchor & anchorV) | TK.Draw.AnchorLeft; } else if (isSet(p[1])) { t.X = totalWidth - TK.Draw.ValueToPx(p[1], true, totalWidth, totalHeight); t.Anchor = (t.Anchor & anchorV) | TK.Draw.AnchorRight; } else if (isSet(p[3])) { t.X = TK.Draw.ValueToPx(p[3], true, totalWidth, totalHeight); t.Anchor = (t.Anchor & anchorV) | TK.Draw.AnchorLeft; } if (isSet(p[4])) t.W = TK.Draw.ValueToPx(p[4], true, totalWidth, totalHeight); if (isSet(p[5])) t.H = TK.Draw.ValueToPx(p[5], false, totalWidth, totalHeight); if (isSet(p[6])) { if (p[6].indexOf("middle") >= 0) t.Anchor = (t.Anchor & anchorH) | TK.Draw.AnchorMiddle; if (p[6].indexOf("top") >= 0) t.Anchor = (t.Anchor & anchorH) | TK.Draw.AnchorTop; if (p[6].indexOf("bottom") >= 0) t.Anchor = (t.Anchor & anchorH) | TK.Draw.AnchorBottom; if (p[6].indexOf("left") >= 0) t.Anchor = (t.Anchor & anchorV) | TK.Draw.AnchorLeft; if (p[6].indexOf("center") >= 0) t.Anchor = (t.Anchor & anchorV) | TK.Draw.AnchorCenter; if (p[6].indexOf("right") >= 0) t.Anchor = (t.Anchor & anchorV) | TK.Draw.AnchorRight; } }; // Draws all child elements TK.Draw.Group = { DrawType: "Group", X: 0, Y: 0, // Optional, set these so all child elements can use anchor top/left, while the full group respects it's own anchor setting // This is useful to make self-contained drawable components, and required when wanting to use the _Positions tag _NormalizePositions: false, Anchor: null, W: null, H: null, Init: function () { if (this._NormalizePositions) TK.Draw.SetPositionsUsingPositionProperty(this); }, Draw: function (c) { var x = 0; var y = 0; if (this._NormalizePositions && this.Anchor !== null && this.W && this.H) { if ((this.Anchor & TK.Draw.AnchorCenter) > 0) { x = -(this.W * 0.5); } else if ((this.Anchor & TK.Draw.AnchorRight) > 0) { x = -this.W; } if ((this.Anchor & TK.Draw.AnchorMiddle) > 0) { y = -(this.H * 0.5); } else if ((this.Anchor & TK.Draw.AnchorBottom) > 0) { y = -this.H; } } c.OffsetX += this.X + x; c.OffsetY += this.Y + y; for (var i in this.Elements) { if (this.Elements[i].Draw) this.Elements[i].Draw(c); } c.OffsetX -= this.X + x; c.OffsetY -= this.Y + y; }, GetRect: function () { return this._NormalizePositions ? TK.Draw.DrawableObject.GetRect.apply(this) : null; }, Overlaps: function (otherDrawableObject) { return this._NormalizePositions ? TK.Draw.DrawableObject.Overlaps.apply(this, [otherDrawableObject]) : null; }, Animate: function (propName, targetValue, ms, easing) { return TK.Draw.DrawableObject.Animate.apply(this, [propName, targetValue, ms, easing]); } }; TK.Draw.DrawableObject = { DrawType: "DrawableObject", Fill: null, // Color Stroke: null, // Color BlendMode: null, // Any value of globalCompositeOperation LineWidth: 1, LineCap: null, Rotate: null, Shadow: null, // [X, Y, Size, Color] ShadowForLine: false, Anchor: TK.Draw.AnchorLeft | TK.Draw.AnchorTop, ZIndex: 1, X: 0, Y: 0, W: 0, H: 0, _Position: null, // Alternative way of setting positions, similar to the normal toolkit method, but with added anchor support Opacity: 1, Init: function () { TK.Draw.SetPositionsUsingPositionProperty(this); }, Transform: function (c) { if (this.DrawAndTransformDisabled) { c.setTransform(c.Scale, 0, 0, c.Scale, c.OffsetX * c.Scale, c.OffsetY * c.Scale); return; } var x = 0; var y = 0; if ((this.Anchor & TK.Draw.AnchorCenter) > 0) { x = -(this.W * 0.5); } else if ((this.Anchor & TK.Draw.AnchorRight) > 0) { x = -this.W; } if ((this.Anchor & TK.Draw.AnchorMiddle) > 0) { y = -(this.H * 0.5); } else if ((this.Anchor & TK.Draw.AnchorBottom) > 0) { y = -this.H; } c.SetOffsetX = (c.OffsetX + x); c.SetOffsetY = (c.OffsetY + y); c.setTransform(c.Scale, 0, 0, c.Scale, (c.OffsetX + x) * c.Scale, (c.OffsetY + y) * c.Scale); if (this.Rotate) { var translateX = this.X - x; var translateY = this.Y - y; c.translate(translateX, translateY); //c.ellipse(0, 0, 35, 35, 0, 0, (2 * Math.PI)); c.rotate(this.Rotate * Math.PI / 180); c.translate(-translateX, -translateY); } }, DrawFS: function (c) { if (this.BlendMode) { c.globalCompositeOperation = this.BlendMode; } if (this.Shadow) { c.shadowOffsetX = this.Shadow[0]; c.shadowOffsetY = this.Shadow[1]; c.shadowBlur = this.Shadow[2]; c.shadowColor = this.Shadow[3]; } else { c.shadowColor = "rgba(0,0,0,0)"; c.shadowBlur = 0; } c.globalAlpha = this.Opacity; if (this.Fill) { if (this.Fill.length > 7 && (this.Fill.substr(0, 7) == "radial " || this.Fill.substr(0, 7) == "linear ")) { // Support for gradients if (this._CachedFillStyleKey != this.Fill) { this._CachedFillStyleKey = this.Fill; var parts = this.Fill.split(/ /g); var g = null; var stopOffset = 5; // TODO: Make sure positions and percentages are relative from the current drawableObject if (parts[0] == "radial") { g = c.createRadialGradient( (this.X + TK.Draw.ValueToPx(parts[1], true, this.W, this.H)) /*- c.OffsetX*/, // inner circle X (this.Y + TK.Draw.ValueToPx(parts[2], false, this.W, this.H)) /*- c.OffsetY*/, // inner circle Y TK.Draw.ValueToPx(parts[3], true, this.W, this.H), // inner circle Radius (this.X + TK.Draw.ValueToPx(parts[4], true, this.W, this.H)) /*- c.OffsetX*/, // outer circle X (this.Y + TK.Draw.ValueToPx(parts[5], false, this.W, this.H)) /*- c.OffsetY*/, // outer circle Y TK.Draw.ValueToPx(parts[6], true, this.W, this.H)); // outer circle Radius stopOffset = 7; } else { g = c.createLinearGradient( (this.X + TK.Draw.ValueToPx(parts[1], true, this.W, this.H)) /*- c.OffsetX*/, // Start gradient X (this.Y + TK.Draw.ValueToPx(parts[2], false, this.W, this.H)) /*- c.OffsetY*/, // Start gradient Y (this.X + TK.Draw.ValueToPx(parts[3], true, this.W, this.H)) /*- c.OffsetX*/, // End gradient X (this.Y + TK.Draw.ValueToPx(parts[4], false, this.W, this.H)) /*- c.OffsetY*/); // End gradient Y } for (var i = stopOffset; i + 1 < parts.length; i += 2) { g.addColorStop(parseFloat(parts[i]), parts[i + 1]); } this._CachedFillStyle = g; } c.fillStyle = this._CachedFillStyle; } else { c.fillStyle = this.Fill; } if (!this.DrawAndTransformDisabled) c.fill(); } if (this.LineCap) { c.lineCap = this.LineCap; } if (!this.ShadowForLine) { c.shadowColor = "rgba(0,0,0,0)"; c.shadowBlur = 0; } c.lineWidth = this.LineWidth; if (this.Stroke) { c.strokeStyle = this.Stroke; if (!this.DrawAndTransformDisabled) c.stroke(); } c.globalAlpha = 1; if (this.BlendMode) { c.globalCompositeOperation = "source-over"; // default } }, Animate: function (propName, targetValue, ms, easing) { if (!easing) easing = TK.Draw.EaseLinear; var p = this.Parent; while (p !== undefined && p.ProcessAnimations === undefined) // find the TK.Draw component p = p.Parent; if (!TK.Draw.AnimationsEnabled) { this[propName] = targetValue; if (this.AnimationEnded) this.AnimationEnded(); if (p) p.Refresh(); return this; } if (p) { var s = new Date().getTime(); if (p.Animations.length > 100) // Clear all deleted animations from the array p.Animations = p.Animations.Where(function (a) { return a; }); var animObj = null; for (var i = 0; i < p.Animations.length; i++) { if (p.Animations[i] && p.Animations[i].I == this && p.Animations[i].P == propName) { animObj = p.Animations[i]; break; } } if (animObj == null) { animObj = { I: this, P: propName, }; p.Animations.push(animObj); } animObj.L = ms; // Total animation length animObj.E = easing; animObj.S = s; // Start time if (typeof this[propName] === 'string' && typeof targetValue === 'string') { // Colors animObj.O = TK.Draw.GetColor(this[propName]); animObj.T = TK.Draw.GetColor(targetValue); //p.Animations.push({ I: this, P: propName, O: TK.Draw.GetColor(this[propName]), T: TK.Draw.GetColor(targetValue), L: ms, E: easing, S: s }); } else if (Array.isArray(this[propName])) { animObj.O = JSON.parse(JSON.stringify(this[propName])); animObj.T = targetValue; //p.Animations.push({ I: this, P: propName, O: JSON.parse(JSON.stringify(this[propName])), T: targetValue, L: ms, E: easing, S: s }); } else { animObj.O = parseFloat(this[propName]); animObj.T = targetValue; //p.Animations.push({ I: this, P: propName, O: parseFloat(this[propName]), T: targetValue, L: ms, E: easing, S: s }); } p.Refresh(); } return this; }, GetRect: function () { var x = this.X; var y = this.Y; if ((this.Anchor & TK.Draw.AnchorCenter) > 0) { x -= this.W * 0.5; } else if ((this.Anchor & TK.Draw.AnchorRight) > 0) { x -= this.W; } if ((this.Anchor & TK.Draw.AnchorMiddle) > 0) { y -= this.H * 0.5; } else if ((this.Anchor & TK.Draw.AnchorBottom) > 0) { y -= this.H; } return [x, y, this.W, this.H]; }, Overlaps: function (otherDrawableObject) { var rectA = this.GetRect(); var rectB = otherDrawableObject.GetRect(); return (rectA[0] < (rectB[0] + rectB[2]) && (rectA[0] + rectA[2]) > rectB[0] && rectA[1] < (rectB[1] + rectB[3]) && (rectA[1] + rectA[3]) > rectB[1]); }, Invalidate: function () { // Called whenever a property is changed } }; "use strict"; /* Minify Skip */ /* Minify Order(155) */ TK.Draw.Circle = { DrawType: "Circle", _: TK.Draw.DrawableObject, Angle: 0, Size: null, DonutSize: null, Extrude: 0, Draw: function (c) { c.beginPath(); this.Transform(c); if (this.Size || this.DonutSize) { // Complicated (slower) method for partial circles var outerRadius = this.W * 0.5; var innerRadius = outerRadius * this.DonutSize; var centerPosX = this.X + this.W * 0.5; var centerPosY = this.Y + this.H * 0.5; if (this.Extrude) { var extrudeAngle = (this.Angle + (this.Size * 0.5)) * Math.PI / 180; centerPosX += Math.cos(extrudeAngle) * this.Extrude; centerPosY += Math.sin(extrudeAngle) * this.Extrude; } var th1 = this.Angle * Math.PI / 180; // 0 = begin angle var th2 = (this.Size + this.Angle) * Math.PI / 180; // 45 = end angle var startOfOuterArcX = outerRadius * Math.cos(th2) + centerPosX; var startOfOuterArcY = outerRadius * Math.sin(th2) + centerPosY; c.arc(centerPosX, centerPosY, innerRadius, th1, th2, false); c.lineTo(startOfOuterArcX, startOfOuterArcY); c.arc(centerPosX, centerPosY, outerRadius, th2, th1, true); } else { // Simple (faster) method for simple circles c.ellipse(this.X + this.W * 0.5, this.Y + this.H * 0.5, this.W * 0.5, this.H * 0.5, 0, 0, (2 * Math.PI)); } this.DrawFS(c); c.closePath(); }, CheckMouseOver: function (x, y) { var cx = (this.Anchor & TK.Draw.AnchorLeft) ? this.X + (this.W / 2) : (this.Anchor & TK.Draw.AnchorRight) ? this.X - (this.W / 2) : this.X; var cy = (this.Anchor & TK.Draw.AnchorTop) ? this.Y + (this.H / 2) : (this.Anchor & TK.Draw.AnchorBottom) ? this.Y - (this.H / 2) : this.Y; if (this.Extrude) { var extrudeAngle = (this.Angle + (this.Size * 0.5)) * Math.PI / 180; cx += Math.cos(extrudeAngle) * this.Extrude; cy += Math.sin(extrudeAngle) * this.Extrude; } if (this.Size && this.Size < 360) { var deg = Math.atan2(y - cy, x - cx) * 180.0 / Math.PI; if (deg < 0) deg = 360 + deg; // Turn into a value between 0 and 360 var checkFrom = this.Angle % 360; if (checkFrom < 0) checkFrom = 360 + checkFrom; // Start is also in a value between 0 and 360 var checkTo = (checkFrom + this.Size) % 360; if (deg < checkFrom && (checkTo > checkFrom || deg > checkTo)) return false; if (deg > checkTo && (checkFrom < checkTo || deg < checkTo)) return false; } var dist = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2)); if (this.DonutSize && dist <= (this.W / 2) * this.DonutSize) return false; return (dist <= this.W / 2); } }; "use strict"; /* Minify Skip */ /* Minify Order(155) */ TK.Draw.Image = { DrawType: "Image", _: TK.Draw.DrawableObject, //RoundCorners: [15,15,15,15], Img: null, Src: null, Scaling: 0, // 0 Scretch to fit, 1 Contain, 2 Cover ImageAlign: TK.Draw.AnchorCenter | TK.Draw.AnchorMiddle, Draw: function (c) { var obj = this; if (!this.Img && this.Src) { this.Img = new Image(); this.Img.onload = function () { // Refresh draw c.CanvasObj.Refresh(); }; this.Img.src = this.Src; return; } if (!this.Img) return; c.beginPath(); this.Transform(c); this.DrawFS(c); try { c.globalAlpha = this.Opacity; var drawX = this.X; var drawY = this.Y; var drawWidth = this.W; var drawHeight = this.H; var sourceX = 0; var sourceY = 0; var sourceWidth = this.Img.width; var sourceHeight = this.Img.height; if (this.Scaling == 1) { // Get the aspect ratios var imgAspectRatio = this.Img.width / this.Img.height; var boxAspectRatio = this.W / this.H; if (imgAspectRatio > boxAspectRatio) { // Image is wider relative to the box drawWidth = this.W; drawHeight = this.W / imgAspectRatio; if ((this.ImageAlign & TK.Draw.AnchorMiddle) != 0) drawY = this.Y + (this.H - drawWidth) / 2; else if ((this.ImageAlign & TK.Draw.AnchorBottom) != 0) drawY = this.Y + (this.H - drawWidth); } else { // Image is taller (or same aspect ratio) relative to the box drawHeight = this.H; drawWidth = this.H * imgAspectRatio; if ((this.ImageAlign & TK.Draw.AnchorCenter) != 0) drawX = this.X + (this.W - drawWidth) / 2; else if ((this.ImageAlign & TK.Draw.AnchorRight) != 0) drawX = this.X + (this.W - drawWidth); } } else if (this.Scaling == 2) { // Get the aspect ratios var imgAspectRatio = this.Img.width / this.Img.height; var boxAspectRatio = this.W / this.H; if (imgAspectRatio > boxAspectRatio) { sourceWidth = (sourceWidth / imgAspectRatio) / 2; if ((this.ImageAlign & TK.Draw.AnchorCenter) != 0) sourceX = (this.Img.width - sourceWidth) / 2; else if ((this.ImageAlign & TK.Draw.AnchorRight) != 0) sourceX = this.Img.width - sourceWidth; } else { sourceHeight = (sourceHeight * imgAspectRatio) / 2; if ((this.ImageAlign & TK.Draw.AnchorMiddle) != 0) sourceY = (this.Img.height - sourceHeight) / 2; else if ((this.ImageAlign & TK.Draw.AnchorBottom) != 0) sourceY = this.Img.height - sourceHeight; } } c.drawImage(this.Img, sourceX, sourceY, sourceWidth, sourceHeight, drawX, drawY, Math.round(drawWidth), Math.round(drawHeight)); c.globalAlpha = 1; } catch (errie) { } c.closePath(); } };"use strict"; /* Minify Skip */ /* Minify Order(155) */ TK.Draw.Line = { DrawType: "Line", _: TK.Draw.DrawableObject, Draw: function (c) { c.beginPath(); this.Transform(c); c.moveTo(this.X, this.Y); if (this.X2 != undefined) { c.lineTo(this.X2, this.Y2); } else { c.lineTo(this.X + this.W, this.Y + this.H); } this.DrawFS(c); c.closePath(); } }; "use strict"; /* Minify Skip */ /* Minify Order(155) */ TK.Draw.LineThroughPoints = { DrawType: "LineThroughPoints", _: TK.Draw.DrawableObject, Points: [], // [ [x,y], [x, y] ], optionally a third parameter (direction) can be passed as well [x, y, direction] Heights: [], // Array with heights for each point, for creating an area chart or sankey lines Smoothing: TK.Draw.SmoothNone, DefaultDirection: TK.Draw.DirectionRight, // Default direction if not set at point level, used for smoothing CornerRadius: 10, Draw: function (c) { c.beginPath(); if (!this.W) { this.W = 0; for (var i = 0; i < this.Points.length; i++) { if (this.Points[i][0] > this.W) this.W = this.Points[i][0]; if (this.Points[i][1] > this.H) this.H = this.Points[i][1]; } } this.Transform(c); var obj = this; var passes = this.Heights && this.Heights.length >= this.Points.length ? 2 : 1; var points = this.Points.slice(); var cornersEndPos = function (point, otherPoint, reverseDir) { point = point.slice(); var dir = point.length > 2 && point[2] !== null && point[2] !== undefined ? point[2] : obj.DefaultDirection; var cornerSize = obj.CornerRadius; if (reverseDir) dir = (dir + 2) % 4; if (dir == TK.Draw.DirectionTop) { point[2] = otherPoint[0] >= point[0] ? TK.Draw.DirectionRight : TK.Draw.DirectionLeft; point[1] -= cornerSize; point[0] += otherPoint[0] >= point[0] ? cornerSize : -cornerSize; } else if (dir == TK.Draw.DirectionRight) { point[2] = otherPoint[1] >= point[1] ? TK.Draw.DirectionBottom : TK.Draw.DirectionTop; point[1] += otherPoint[1] >= point[1] ? cornerSize : -cornerSize; point[0] += cornerSize; } else if (dir == TK.Draw.DirectionBottom) { point[2] = otherPoint[0] >= point[0] ? TK.Draw.DirectionRight : TK.Draw.DirectionLeft; point[1] += cornerSize; point[0] += otherPoint[0] >= point[0] ? cornerSize : -cornerSize; } else if (dir == TK.Draw.DirectionLeft) { point[2] = otherPoint[1] >= point[1] ? TK.Draw.DirectionBottom : TK.Draw.DirectionTop; point[1] += otherPoint[1] >= point[1] ? cornerSize : -cornerSize; point[0] -= cornerSize; } return point; }; var cornersDrawCurve = function (point, otherPoint, dir) { // Dir should be the direction after the curve if (dir == TK.Draw.DirectionTop || dir == TK.Draw.DirectionBottom) { c.quadraticCurveTo(otherPoint[0] + obj.X, point[1] + obj.Y, otherPoint[0] + obj.X, otherPoint[1] + obj.Y); } else { c.quadraticCurveTo(point[0] + obj.X, otherPoint[1] + obj.Y, otherPoint[0] + obj.X, otherPoint[1] + obj.Y); } //c.lineTo(otherPoint[0], otherPoint[1]); }; for (var pass = 0; pass < passes; pass++) { if (pass == 1) { // If the heights for all points is set, we will loop through the points a second time // in reverse order with the added heights and reversed directions so we can make a full closed loop. points = points.map(function (p, index) { var newP = p.slice(); newP[1] += obj.Heights[index]; newP[2] = ((newP[2] !== undefined && newP[2] !== null ? newP[2] : obj.DefaultDirection) + 2) % 4; return newP; }); points = points.reverse(); } for (var i = 0; i < points.length; i++) { var p = points[i]; var dir = p.length >= 3 && p[2] !== null && p[2] !== undefined ? p[2] : this.DefaultDirection; if (i == 0) { if (pass == 0) c.moveTo(p[0] + this.X, p[1] + this.Y); else c.lineTo(p[0] + this.X, p[1] + this.Y); continue; } var lp = points[i - 1]; var ldir = lp.length >= 3 && lp[2] !== null && lp[2] !== undefined ? lp[2] : this.DefaultDirection; var x_mid = (lp[0] + p[0]) / 2; var y_mid = (lp[1] + p[1]) / 2; if (!this.Smoothing) { c.lineTo(p[0] + this.X, p[1] + this.Y); } else if (this.Smoothing == TK.Draw.SmoothQuadratic) { if (ldir == TK.Draw.DirectionRight || ldir == TK.Draw.DirectionLeft) { var cp_x1 = (x_mid + lp[0]) / 2; var cp_x2 = (x_mid + p[0]) / 2; c.quadraticCurveTo(cp_x1 + this.X, lp[1] + this.Y, x_mid + this.X, y_mid + this.Y); c.quadraticCurveTo(cp_x2 + this.X, p[1] + this.Y, p[0] + this.X, p[1] + this.Y); } else if (ldir == TK.Draw.DirectionTop || ldir == TK.Draw.DirectionBottom) { var cp_y1 = (y_mid + lp[1]) / 2; var cp_y2 = (y_mid + p[1]) / 2; c.quadraticCurveTo(lp[0] + this.X, cp_y1 + this.Y, x_mid + this.X, y_mid + this.Y); c.quadraticCurveTo(p[0] + this.X, cp_y2 + this.Y, p[0] + this.X, p[1] + this.Y); } } else if (this.Smoothing == TK.Draw.SmoothCorners) { // TODO: // - Check if it's nearby, we might need to reduce the corner radius // - Do multiple corners if we are not in the correct direction yet //var startPoint = cornersEndPos(lp, p); var startPoint = [lp[0], lp[1], ldir]; var endPoint = cornersEndPos(p, lp, true); var revDirEnd = (endPoint[2] + 2) % 4; var max = 100; while (startPoint[0] != endPoint[0] || startPoint[1] != endPoint[1]) { max--; if (max == 0) { console.log("max reached"); break; } var startDirHorizontal = startPoint[2] == TK.Draw.DirectionLeft || startPoint[2] == TK.Draw.DirectionRight; if (startPoint[2] == revDirEnd && startDirHorizontal && startPoint[1] == endPoint[1]) { // If already at the right height, just need 1 line to connect c.lineTo(endPoint[0] + this.X, endPoint[1] + this.Y); break; } else if (startPoint[2] == revDirEnd && !startDirHorizontal && startPoint[0] == endPoint[0]) { // If already at the right x pos, just need 1 line to connect c.lineTo(endPoint[0] + this.X, endPoint[1] + this.Y); break; } else { // Make a line towards the right direction var newPos = startPoint.slice(); if (startPoint[2] == TK.Draw.DirectionRight) { if (endPoint[2] == TK.Draw.DirectionLeft) newPos[0] = endPoint[0] - (2 * this.CornerRadius); else if (endPoint[2] == TK.Draw.DirectionTop || endPoint[2] == TK.Draw.DirectionBottom) newPos[0] = endPoint[0] - this.CornerRadius; else newPos[0] = endPoint[0]; startPoint[0] = newPos[0] > startPoint[0] ? newPos[0] : startPoint[0]; } else if (startPoint[2] == TK.Draw.DirectionLeft) { if (endPoint[2] == TK.Draw.DirectionRight) newPos[0] = endPoint[0] + (2 * this.CornerRadius); else if (endPoint[2] == TK.Draw.DirectionTop || endPoint[2] == TK.Draw.DirectionBottom) newPos[0] = endPoint[0] + this.CornerRadius; else newPos[0] = endPoint[0]; startPoint[0] = newPos[0] < startPoint[0] ? newPos[0] : startPoint[0]; } else if (startPoint[2] == TK.Draw.DirectionBottom) { if (endPoint[2] == TK.Draw.DirectionTop) newPos[1] = endPoint[1] - (2 * this.CornerRadius); else if (endPoint[2] == TK.Draw.DirectionLeft || endPoint[2] == TK.Draw.DirectionRight) newPos[1] = endPoint[1] - this.CornerRadius; else newPos[1] = endPoint[1]; startPoint[1] = newPos[1] > startPoint[1] ? newPos[1] : startPoint[1]; } else if (startPoint[2] == TK.Draw.DirectionTop) { if (endPoint[2] == TK.Draw.DirectionRight) newPos[1] = endPoint[1] + (2 * this.CornerRadius); else if (endPoint[2] == TK.Draw.DirectionLeft || endPoint[2] == TK.Draw.DirectionRight) newPos[1] = endPoint[1] + this.CornerRadius; else newPos[1] = endPoint[1]; startPoint[1] = newPos[1] < startPoint[1] ? newPos[1] : startPoint[1]; } c.lineTo(startPoint[0] + this.X, startPoint[1] + this.Y); // Corner into the right direction and update our start pos var newStartPoint = cornersEndPos(startPoint, p); cornersDrawCurve(startPoint, newStartPoint, newStartPoint[2]); startPoint = newStartPoint; } } //c.moveTo(p[0],p[1]) cornersDrawCurve(startPoint, p, dir); } } if (pass == 1 && this.Points.length > 0) { c.lineTo(this.Points[0][0] + this.X, this.Points[0][1] + this.Y); } } this.DrawFS(c); c.closePath(); } }; "use strict"; /* Minify Skip */ /* Minify Order(155) */ TK.Draw.Rect = { DrawType: "Rect", _: TK.Draw.DrawableObject, //RoundCorners: [15,15,15,15], ShadeSize: 0, ShadePosition: 1, Extrude: 0, // Does not work with RoundCorners yet DrawRoundedRect: function (c, x, y, w, h, corners) { if (!corners || (corners[0] == 0 && corners[1] == 0 && corners[2] == 0 && corners[3] == 0)) { c.rect(x, y, w, h); return; } c.moveTo(x + corners[0], y); c.lineTo(x + w - corners[1], y); c.quadraticCurveTo(x + w, y, x + w, y + corners[1]); c.lineTo(x + w, y + h - corners[2]); c.quadraticCurveTo(x + w, y + h, x + w - corners[2], y + h); c.lineTo(x + corners[3], y + h); c.quadraticCurveTo(x, y + h, x, y + h - corners[3]); c.lineTo(x, y + corners[0]); c.quadraticCurveTo(x, y, x + corners[0], y); }, Draw: function (c) { c.beginPath(); this.Transform(c); var corners = null; if (this.RoundCorners && this.RoundCorners.length == 4) { corners = this.RoundCorners.slice(); var w = this.W; var h = this.H; var f = 2; if (corners[0] * f > w || corners[1] * f > w || corners[2] * f > w || corners[3] * f > w || corners[0] * f > h || corners[1] * f > h || corners[2] * f > h || corners[3] * f > h) corners = null; } this.DrawRoundedRect(c, this.X, this.Y, this.W, this.H, corners); this.DrawFS(c); c.closePath(); if (this.Extrude) { if (this.Extrude > 0) { // Outside, Draw lighter color above, darker color on the right c.beginPath(); c.fillStyle = this.FillExtrudeLightColor ? this.FillExtrudeLightColor : TK.Draw.ColorToDifferentColor(this.Fill, "#FFF", 0.4); c.moveTo(this.X, this.Y); c.lineTo(this.X + this.Extrude, this.Y - this.Extrude); c.lineTo(this.X + this.W + this.Extrude, this.Y - this.Extrude); c.lineTo(this.X + this.W, this.Y); c.lineTo(this.X, this.Y); c.fill(); c.closePath(); c.beginPath(); c.fillStyle = this.FillExtrudeDarkColor ? this.FillExtrudeDarkColor : TK.Draw.ColorToDifferentColor(this.Fill, "#000", 0.4); c.moveTo(this.X + this.W, this.Y); c.lineTo(this.X + this.W + this.Extrude, this.Y - this.Extrude); c.lineTo(this.X + this.W + this.Extrude, this.Y + this.H - this.Extrude); c.lineTo(this.X + this.W, this.Y + this.H); c.lineTo(this.X + this.W, this.Y); c.fill(); c.closePath(); } else if (this.Extrude < 0) { // Inside c.beginPath(); //c.fillStyle = colorToDifferentColor(this.Fill, "#FFF", 0.4); c.fillStyle = this.FillExtrudeDarkColor ? this.FillExtrudeDarkColor : TK.Draw.ColorToDifferentColor(this.Fill, "#000", 0.4); c.moveTo(this.X, this.Y); c.lineTo(this.X + -this.Extrude, this.Y); c.lineTo(this.X + -this.Extrude, this.Y + this.H - -this.Extrude); c.lineTo(this.X, this.Y + this.H); c.lineTo(this.X, this.Y); c.fill(); c.closePath(); c.beginPath(); c.fillStyle = this.FillExtrudeLightColor ? this.FillExtrudeLightColor : TK.Draw.ColorToDifferentColor(this.Fill, "#000", 0.1); c.moveTo(this.X, this.Y + this.H); c.lineTo(this.X + -this.Extrude, this.Y + this.H - -this.Extrude); c.lineTo(this.X + this.W, this.Y + this.H - -this.Extrude); c.lineTo(this.X + this.W, this.Y + this.H); c.lineTo(this.X, this.Y + this.H); c.fill(); c.closePath(); } } if (this.ShadeSize) { c.beginPath(); this.Transform(c); var origFill = this.Fill; var origStroke = this.Stroke; this.Fill = "rgba(0,0,0,0.2)"; this.Stroke = null; if (!corners) corners = [0, 0, 0, 0]; if (this.ShadePosition == 0) this.DrawRoundedRect(c, this.X, this.Y, this.W, this.ShadeSize, [corners[0], corners[1], 0, 0]); else if (this.ShadePosition == 1) this.DrawRoundedRect(c, (this.X + this.W) - this.ShadeSize, this.Y, this.ShadeSize, this.H, [0, corners[1], corners[2], 0]); else if (this.ShadePosition == 2) this.DrawRoundedRect(c, this.X, (this.Y + this.H) - this.ShadeSize, this.W, this.ShadeSize, [0, 0, corners[2], corners[3]]); else if (this.ShadePosition == 3) this.DrawRoundedRect(c, this.X, this.Y, this.ShadeSize, this.H, [corners[1], 0, 0, 0]); this.DrawFS(c); c.closePath(); this.Fill = origFill; this.Stroke = origStroke; } } }; "use strict"; /* Minify Skip */ /* Minify Order(155) */ TK.Draw.TextScalingNormal = 0; // None TK.Draw.TextScalingWhiteSpaceBreak = 1; // Continue on next line to make text fit TK.Draw.TextScalingResize = 2; // Always resize to fit the full frame, while keeping aspect ratio TK.Draw.TextScalingResizeIfNeeded = 3; // Only resize if the text doesn't fit, while keeping aspect ratio TK.Draw.Text = { DrawType: "Text", _: TK.Draw.DrawableObject, BlockedText: false, // Static TK.Draw.Text.BlockedText, If set to true only colored rects will be drawed instead of text, useful for testing or visual censoring Text: "Text", Font: "30pt Arial", Scaling: TK.Draw.TextScalingNormal, LineSpacingRatio: 1.1, WidthPadding: 2, HeightPadding: 2, TextAlign: TK.Draw.AnchorCenter | TK.Draw.AnchorMiddle, Invalidate: function () { this.CachedImage = null; }, Draw: function (c) { if (this.Text === null || this.Text === undefined) return; if (this.Text.substring === undefined) this.Text = this.Text.toString(); c.beginPath(); if (!this.W) { this.W = this.MeasureWidth(this.Text); } if (!this.H) { this.H = this.MeasureHeight(this.Text, this.Font); } if (this.Rotate) { var translateX = this.X; var translateY = this.Y; c.translate(translateX, translateY); //c.ellipse(0, 0, 35, 35, 0, 0, (2 * Math.PI)); c.rotate(this.Rotate * Math.PI / 180); c.translate(-translateX, -translateY); } this.Transform(c); if (TK.Draw.Text.BlockedText) { c.fillRect(this.X, this.Y, this.W, this.H); } else { if (!this.CachedImage) { this.CachedImage = document.createElement("CANVAS"); this.CachedImage.width = Math.round(this.W * c.Scale); this.CachedImage.height = Math.round(this.H * c.Scale); this.CachedImage.style.width = Math.round(this.W * c.Scale) + "px"; this.CachedImage.style.width = Math.round(this.H * c.Scale) + "px"; var text = this.Text; var scaleFactor = 1; var lineHeight = this.MeasureHeight(this.Text, this.Font); if (this.Scaling == TK.Draw.TextScalingWhiteSpaceBreak) { // Try to insert line breaks // TODO: Support text with provided linebreaks var parts = this.Text.split(/ /g); var newText = ""; var sentenceLength = 0; for (var i = 0; i < parts.length; i++) { var curText = parts[i] + (i + 1 == parts.length ? "" : " "); var curWidth = Math.ceil(this.MeasureWidth(curText, this.Font)); if (sentenceLength > 0 && sentenceLength + curWidth >= this.W) { newText += "\n"; sentenceLength = curWidth; } else { sentenceLength += curWidth; } newText += curText; } text = newText; } else if (this.Scaling == TK.Draw.TextScalingResize || this.Scaling == TK.Draw.TextScalingResizeIfNeeded) { // Calculate factor the font size needs to be changed to make it fill the frame // TODO: Support text with provided linebreaks var widthRatio = this.W / this.MeasureWidth(this.Text, this.Font); var heightRatio = this.H / lineHeight; if (this.Scaling == TK.Draw.TextScalingResize || widthRatio < 1 || heightRatio < 1) { scaleFactor = Math.min(widthRatio, heightRatio); lineHeight *= scaleFactor; } } lineHeight = Math.ceil(lineHeight); // unscaled var cachedContext = this.CachedImage.getContext("2d"); cachedContext.imageSmoothingEnabled = false; cachedContext.textAlign = "left"; cachedContext.textBaseline = "top"; cachedContext.font = this.Font; this.DrawFS(cachedContext); cachedContext.setTransform(c.Scale * scaleFactor, 0, 0, c.Scale * scaleFactor, 0, 0); var correctionFactor = 1 / scaleFactor; var lines = text.split(/\n/g); var curY = 0; var totalHeight = lines.length == 0 ? 0 : ((lines.length - 1) * lineHeight * this.LineSpacingRatio) + lineHeight; if ((this.TextAlign & TK.Draw.AnchorMiddle) > 0) { curY = (this.H / 2) - (totalHeight / 2); } else if ((this.TextAlign & TK.Draw.AnchorBottom) > 0) { curY = this.H - totalHeight; } curY = Math.ceil(curY * correctionFactor); for (var i = 0; i < lines.length; i++) { var curX = 0; var lineWidth = this.MeasureWidth(lines[i], this.Font); if (scaleFactor == 1 && (this.TextAlign & TK.Draw.AnchorCenter) > 0) curX = (this.W / 2) - (lineWidth / 2); else if (scaleFactor == 1 && (this.TextAlign & TK.Draw.AnchorRight) > 0) curX = this.W - lineWidth; else if (scaleFactor != 1 && lineWidth * scaleFactor >= this.H && (this.TextAlign & TK.Draw.AnchorCenter) > 0) { // Make sure its centered after scaling curX = ((this.W / 2) - ((lineWidth * scaleFactor) / 2)) * correctionFactor; } else if (scaleFactor != 1 && lineWidth * scaleFactor >= this.H && (this.TextAlign & TK.Draw.AnchorRight) > 0) { // Make sure its centered after scaling curX = (this.W - (lineWidth * scaleFactor)) * correctionFactor; } if (this.Fill) cachedContext.fillText(lines[i], curX, curY); if (this.Stroke) { cachedContext.miterLimit = 3; cachedContext.strokeText(lines[i], curX, curY); } curY += Math.ceil(lineHeight * this.LineSpacingRatio * correctionFactor); } } c.drawImage(this.CachedImage, Math.floor(this.X), Math.floor(this.Y), Math.round(this.W), Math.round(this.H)); } c.closePath(); }, FillWidthHeight: function () { this.W = this.MeasureWidth(this.Text, this.Font); this.H = this.MeasureHeight(this.Text, this.Font); }, MeasureHeight: function (txt, font) { var extra = this.HeightPadding + (this.LineWidth ? this.LineWidth * 2 : 0); var m = this.MeasureText(txt, font); if (m.actualBoundingBoxDescent !== undefined) return Math.ceil(m.actualBoundingBoxAscent + m.actualBoundingBoxDescent) + extra; var height = 16; var f = font.split(' '); if (f.length > 1) { height = parseInt(f[0]); if (f[0].indexOf("pt") > 0) { height *= 1.34; // TODO: Fix for different user settings } else if (f[0].indexOf("em") > 0) { height *= 16; } } return Math.ceil(height) + extra; }, MeasureWidth: function (txt, font) { var extra = this.WidthPadding + (this.LineWidth ? this.LineWidth * 2 : 0); var m = this.MeasureText(txt, font); if (m.actualBoundingBoxRight !== undefined) return Math.ceil(m.actualBoundingBoxLeft + m.actualBoundingBoxRight) + extra; return Math.ceil(m.width) + extra; // fallback }, MeasureText: function (txt, font) { if (txt == undefined) txt = this.Text; if (font == undefined) font = this.Font; if (!TK.Draw.Text.MeasuringCanvas) { TK.Draw.Text.MeasuringCanvas = document.createElement("CANVAS"); TK.Draw.Text.MeasuringCanvasContext = TK.Draw.Text.MeasuringCanvas.getContext("2d"); } var c = TK.Draw.Text.MeasuringCanvasContext; c.textAlign = "left"; c.textBaseline = "top"; c.font = font; return c.measureText(txt); } }; "use strict"; /* Minify Order(200) */ window.TK.ButtonSwitcher = { className: "toolkitButtonSwitcher", Data: null, Init: function () { if (!this.DataSettings) { this.DataSettings = { Options: this.Options, Multiple: this.Multiple, MultipleCheck: this.MultipleCheck }; } var obj = this; for (var i = 0; i < this.DataSettings.Options.length; i++) { var isActive = this.DataSettings.Multiple ? this.Data && this.Data.indexOf(this.DataSettings.Options[i].Value) >= 0 : this.Data == this.DataSettings.Options[i].Value; this.Add({ style: { backgroundImage: this.DataSettings.Options[i].Image ? "url('" + this.DataSettings.Options[i].Image + "')" : null }, className: "toolkitButtonSwitchOption " + (isActive ? "toolkitButtonSwitchActive" : ""), title: this.DataSettings.Options[i].Title ? this.DataSettings.Options[i].Title : "", innerHTML: this.DataSettings.Options[i].Text ? this.DataSettings.Options[i].Text : " ", Value: this.DataSettings.Options[i].Value, onclick: function () { var buttons = obj.Elements.ToArray(); var multiple = obj.DataSettings.Multiple; var currentChecked = this.className.indexOf("toolkitButtonSwitchActive") >= 0; if (obj.DataSettings.MultipleCheck && !currentChecked) { var curSelected = obj.GetValue(); if (curSelected == null) curSelected = []; if (!obj.DataSettings.MultipleCheck(curSelected, this.Value)) { multiple = false; // deselect rest as this combination isn't allowed } } if (!multiple) { for (var i = 0; i < buttons.length; i++) { buttons[i].className = buttons[i].className.replace(/toolkitButtonSwitchActive/g, ""); } this.className += " toolkitButtonSwitchActive"; } else { if (currentChecked) { var curSelected = obj.GetValue(); if (!obj.DataSettings.Required || (curSelected && curSelected.length > 1)) this.className = this.className.replace(/toolkitButtonSwitchActive/g, ""); } else this.className += " toolkitButtonSwitchActive"; } obj.Data = this.Value; if (obj.onchange) obj.onchange(); } }); } }, GetValue: function () { if (this.DataSettings.Multiple) { var buttons = this.Elements.ToArray(); var selected = []; for (var i = 0; i < buttons.length; i++) { if (buttons[i].className.indexOf("toolkitButtonSwitchActive") >= 0) { selected.push(buttons[i].Value); } } return selected.length > 0 ? selected : null; } return this.Data; } }; if (window.TK.Form) { window.TK.Form.DefaultTemplates.buttonSwitcher = { _: TK.ButtonSwitcher }; }"use strict"; /* Minify Skip */ /* Minify Order(200) */ TK.Chart = { _: "div", DefaultColors: ["#0081D5", "#FF9947", "#03BD5B", "#A939B9", "#D1335B", "#53657D", "#339999", "#999933", "#993399", "#333399", "#999933"], Width: 700, Height: 400, __RecursivePropertiesSeries: true, Series: [ // Style (flags): 1 Line, 2 Bar, 4 Points, 8 Area, 16 Text with line and point to the value position // Data is array with X,Y values, X value can be a number, or date-string, or a color //{ Title: "Test series 1", Axis: "X,Y1", Color: "#0081D5",StackGroup: "b", Style: 2, Smoothing: 1, Data: [["2010-01-01", 100], ["2010-01-02", 100], ["2010-01-03", 50], ["2010-01-04", 100], ["2010-01-05", 75]] }, //{ Title: "Test series 2", Axis: "X,Y1", Color: "#FF9947", StackGroup: "b", Style: 2, Smoothing: 1, LineWidth: 2, Data: [["2010-01-01", 10], ["2010-01-02", 20], ["2010-01-03", 10], ["2010-01-04", 10], ["2010-01-05", 20]] }, //{ Title: "Test series 3", Axis: "X,Y1", Color: "#990000", Style: 2, Smoothing: 1, LineWidth: 2, Data: [["2010-01-01", 20], ["2010-01-02", 30], ["2010-01-03", 20], ["2010-01-04", 30], ["2010-01-05", 20]] }, ], __RecursivePropertiesAxis: true, Axis: { X: { //Type: 0, // 0 - Numbers, 1 - DateTime Type: 1, Labels: 0, // 0 - Automatic Range: null, // [null, null] Fixed min/max, null/undefined for automatic, values outside this range will be filtered LabelFormat: null, LabelFormatEdge: null, Title: "", Location: 2, // Bottom ( Top,Right,Bottom,Left,Hidden X,Hidden Y,Size,Color) }, Y1: { Type: 0, // 0 - Numbers, 1 - DateTime (Mapped on epoch), 2 - DateTime (Mapped on labels), 3 - Labels Labels: 0, // 0 - Automatic Range: null, // [null, null] Fixed min/max, null/undefined for automatic, values outside this range will be filtered Title: null, ColorLine: "#1d1d1d", ColorSteps: "#999", ColorMinorSteps: "#CCC", ColorLabels: "#999", ColorTitle: "#999", FontTitle: "12pt Arial", Location: 3, // Left Reverse: true, Title: "" }, Size: { RangeResult: null, // [10, 25], Location: 8 }, Color: { RangeResult: null, //["#CCC", "#F00"], Location: 7 } }, AxisPadding: 5, LabelSpacing: 60, LegendLocation: 2, FontLegend: "10pt Arial", ColorLegend: "#333", TimeZone: "UTC", ShadeSize: 0.1, RoundCorners: [5, 5, 5, 5], Scale: 2, MinChartPadding: 35, EnableNavigator: false, NavigatorStartValue: null, NavigatorEndValue: null, FixedNavigator: false, Navigate: function (minValue, maxValue, final) { }, NavigatorLineWidth: 1, ColorNavigatorActive: "#999", ColorNavigatorInactive: "#EEE", ColorNavigatorLine: "#333", ColorNavigatorOutside: "rgba(0,0,0,0.3)", LegendTemplate: { _: TK.Draw.Group, Series: null, Chart: null, Init: function () { this.Add({ _: TK.Draw.Rect, X: 0, Y: 0, W: 14, H: 14, Fill: this.Series.Color, ShadePosition: 2, ShadeSize: 3, RoundCorners: [3, 3, 3, 3], Anchor: TK.Draw.AnchorLeft | TK.Draw.AnchorTop, }); this.Add({ _: TK.Draw.Text, X: 20, Y: 0, Fill: this.Chart.ColorLegend, Font: this.Chart.FontLegend, Text: this.Series.Title, Anchor: TK.Draw.AnchorLeft | TK.Draw.AnchorTop, }); }, GetWidth: function () { return TK.Draw.Text.MeasureWidth(this.Series.Title, this.Chart.FontLegend) + 40; } }, FormatDate: function (timeStamp, format) { var disableAutoSmaller = false; if (format.substr(0, 1) == "!") { disableAutoSmaller = true; format = format.substr(1); } var date = new Date(timeStamp); var p = function (d) { return (d < 10 ? "0" + d : d.toString()); }; if (this.TimeZone == "UTC") { format = format.replace("YYYY", date.getUTCFullYear()).replace("MM", date.getUTCMonth() + 1).replace("DD", date.getUTCDate()).replace("HH", p(date.getUTCHours())).replace("mm", p(date.getUTCMinutes())).replace("ss", p(date.getUTCSeconds())); } else { if (this.TimeZone != "Local" && window.moment && window.moment.tz) { format = moment(timeStamp).tz(this.TimeZone).format(format); } else { format = format.replace("YYYY", date.getFullYear()).replace("MM", date.getMonth() + 1).replace("DD", date.getDate()).replace("HH", p(date.getHours())).replace("mm", p(date.getMinutes())).replace("ss", p(date.getSeconds())); } } return disableAutoSmaller ? format : format.replace("00:00:00", "").replace(" 00:00", "").replace(/:00$/, ""); }, Init: function () { this.Refresh(); }, Refresh: function () { if (!this.Elements.Canvas) { this.Add({ _: TK.Draw, Scale: this.Scale, Width: this.Width, Height: this.Height }, "Canvas"); } else { this.Elements.Canvas.Clear(); this.Elements.Canvas.Width = this.Width; this.Elements.Canvas.Height = this.Height; this.Elements.Canvas.Scale = this.Scale; this.Elements.Canvas.Init(); } this.RefreshAxises(); this.RefreshData(); if (this.EnableNavigator) { this.AddNavigator(this.NavigatorStartValue, this.NavigatorEndValue); } else { delete this.AxisesDetected; } //console.log(this.AxisesDetected); //delete this.AxisesDetected; this.Elements.Canvas.Refresh(); }, AddNavigator: function (from, till) { // Add or set navigator points var obj = this; var d = this.AxisesDetected["X"]; var minPX = d.Position[0]; var maxPX = d.Position[0] + d.Position[2]; var heightPX = d.Position[1]; if (heightPX == 0) heightPX = this.Height; var fromPX = minPX; var tillPX = maxPX; if (from !== undefined && from !== null) fromPX = d.ValueToPX(from); if (till !== undefined && till !== null) tillPX = d.ValueToPX(till); var cObj = this.Elements.Canvas.Elements; if (cObj.NavigatorCenterBlock) { // Already added, just update cObj.NavigatorMinBlock.X = fromPX; cObj.NavigatorMaxBlock.X = tillPX; cObj.NavigatorMinLine.X = fromPX; cObj.NavigatorMinOutside.W = fromPX - minPX; cObj.NavigatorMaxLine.X = tillPX; cObj.NavigatorMaxOutside.X = tillPX; cObj.NavigatorMaxOutside.W = maxPX - tillPX; this.Elements.Canvas.Refresh(); return; } var outsideConfig = { _: TK.Draw.Rect, Fill: this.ColorNavigatorOutside, X: 0, Y: 0, W: 0, H: heightPX, Anchor: TK.Draw.AnchorLeft | TK.Draw.AnchorTop, }; var lineConfig = { _: TK.Draw.Line, Stroke: this.ColorNavigatorLine, LineWidth: this.NavigatorLineWidth, X: 0, Y: 0, W: 1, H: heightPX, Anchor: TK.Draw.AnchorCenter | TK.Draw.AnchorTop, }; var blockConfig = { _: TK.Draw.Rect, X: 0, Y: (heightPX / 2) - Math.round(heightPX * 0.15), W: 10, H: Math.round(heightPX * 0.3), Fill: this.ColorNavigatorInactive, Stroke: this.ColorNavigatorLine, LineWidth: this.NavigatorLineWidth, Anchor: TK.Draw.AnchorRight | TK.Draw.AnchorTop, MouseDown: function (x, y) { this.Fill = obj.ColorNavigatorActive; var offsetX = this.X - x; this.MouseMove = function (x2, y2) { this.X = x2 + offsetX; if (this.X < minPX) this.X = minPX; if (this.X > maxPX) this.X = maxPX; if (this.LeftSide && this.X > cObj.NavigatorMaxBlock.X) { this.X = cObj.NavigatorMaxBlock.X; } else if (!this.LeftSide && this.X < cObj.NavigatorMinBlock.X) { this.X = cObj.NavigatorMinBlock.X; } //this.Y = y2 + offsetY; if (this.LeftSide) { cObj.NavigatorMinLine.X = this.X; cObj.NavigatorMinOutside.W = this.X - minPX; } else { cObj.NavigatorMaxLine.X = this.X; cObj.NavigatorMaxOutside.X = this.X; cObj.NavigatorMaxOutside.W = maxPX - this.X; } cObj.NavigatorCenterBlock.W = cObj.NavigatorMaxLine.X - cObj.NavigatorMinLine.X; cObj.NavigatorCenterBlock.X = cObj.NavigatorMinLine.X; if (obj.Navigate) obj.Navigate(cObj.NavigatorMinBlock.GetPositionAsValue(), cObj.NavigatorMaxBlock.GetPositionAsValue(), false); return true; }; }, MouseUp: function (x, y) { this.Fill = obj.ColorNavigatorInactive; this.MouseMove = null; if (obj.Navigate) obj.Navigate(cObj.NavigatorMinBlock.GetPositionAsValue(), cObj.NavigatorMaxBlock.GetPositionAsValue(), true); }, GetPositionAsValue: function () { // Convert X value to actual value var r = (this.X - minPX) / (maxPX - minPX); if (this.X - 3 < minPX) // If its near start end, pick the start value r = 0; if (this.X + 3 > maxPX) // If its near the end, pick the end value r = 1; return (r * (d.ScaleMax - d.ScaleMin)) + d.ScaleMin; } }; this.Elements.Canvas.Add({ _: outsideConfig, X: minPX, W: fromPX - minPX }, "NavigatorMinOutside"); this.Elements.Canvas.Add({ _: outsideConfig, X: tillPX, W: maxPX - tillPX }, "NavigatorMaxOutside"); this.Elements.Canvas.Add({ _: lineConfig, X: fromPX }, "NavigatorMinLine"); this.Elements.Canvas.Add({ _: lineConfig, X: tillPX }, "NavigatorMaxLine"); var minBlock, maxBlock; if (!this.FixedNavigator) { minBlock = this.Elements.Canvas.Add({ _: blockConfig, X: fromPX, LeftSide: true }, "NavigatorMinBlock"); maxBlock = this.Elements.Canvas.Add({ _: blockConfig, Anchor: TK.Draw.AnchorLeft | TK.Draw.AnchorTop, X: tillPX, LeftSide: false }, "NavigatorMaxBlock"); } this.Elements.Canvas.Add({ _: TK.Draw.Rect, X: fromPX, Y: 0, W: (tillPX - fromPX), H: heightPX, Fill: "rgba(0,0,0,0)", MouseOver: function () { obj.Elements.Canvas.style.cursor = "grab"; }, MouseOut: function () { obj.Elements.Canvas.style.cursor = ""; }, MouseDown: function (x, y) { var offsetX = this.X - x; obj.Elements.Canvas.style.cursor = "grabbing"; this.MouseMove = function (x2, y2) { this.X = x2 + offsetX; if (this.X < minPX) this.X = minPX; if (this.X + this.W > maxPX) this.X = maxPX - this.W; cObj.NavigatorMinLine.X = this.X; cObj.NavigatorMinOutside.W = this.X - minPX; if (minBlock) minBlock.X = this.X; if (maxBlock) maxBlock.X = this.X + this.W; cObj.NavigatorMaxLine.X = this.X + this.W; cObj.NavigatorMaxOutside.X = this.X + this.W; cObj.NavigatorMaxOutside.W = maxPX - (this.X + this.W); if (obj.Navigate) obj.Navigate(cObj.NavigatorCenterBlock.GetPositionAsValue(true), cObj.NavigatorCenterBlock.GetPositionAsValue(false), false); return true; }; return true; }, MouseUp: function (x, y) { this.MouseMove = null; obj.Elements.Canvas.style.cursor = "grab"; if (obj.Navigate) obj.Navigate(this.GetPositionAsValue(true), this.GetPositionAsValue(false), true); }, GetPositionAsValue: function (start) { // Convert X value to actual value var x = (this.X + (start ? 0 : this.W)); var r = (x - minPX) / (maxPX - minPX); if (x - 3 < minPX) // If its near start end, pick the start value r = 0; if (x + 3 > maxPX) // If its near the end, pick the end value r = 1; return (r * (d.ScaleMax - d.ScaleMin)) + d.ScaleMin; } }, "NavigatorCenterBlock"); }, RefreshAxises: function () { var defaultColorIndex = 0; // Analyse data so we know what Axises we have to generate var detected = {}; var stackGroupData = {}; for (var i = 0; i < this.Series.length; i++) { if (!this.Series[i].Data || this.Series[i].Data.length == 0 || !this.Series[i].Axis) continue; if (!this.Series[i].Color) this.Series[i].Color = this.DefaultColors[defaultColorIndex++]; var sizePerValue = null; if ((this.Series[i].Style & 4) > 0) sizePerValue = 8; else if ((this.Series[i].Style & 2) == 0) sizePerValue = 1; var axisesUsed = this.Series[i].Axis.split(','); var axises = []; for (var j = 0; j < axisesUsed.length; j++) { var axisName = axisesUsed[j]; if (!detected[axisName]) { detected[axisName] = { Min: this.Axis[axisName] && this.Axis[axisName].Range ? this.Axis[axisName].Range[0] : null, Max: this.Axis[axisName] && this.Axis[axisName].Range ? this.Axis[axisName].Range[1] : null, RangeResult: this.Axis[axisName] && this.Axis[axisName].RangeResult ? this.Axis[axisName].RangeResult : null, Location: this.Axis[axisName] && this.Axis[axisName].Location !== undefined ? this.Axis[axisName].Location : axisName == "X" ? 2 : axisName == "Y1" ? 3 : 1, Type: this.Axis[axisName] ? this.Axis[axisName].Type : 0, Reverse: this.Axis[axisName] && this.Axis[axisName].Reverse !== undefined ? this.Axis[axisName].Reverse : axisName == "X" || axisName.length > 2 ? false : true, Size: this.Axis[axisName] && this.Axis[axisName].Size ? this.Axis[axisName].Size : sizePerValue, Color: this.Axis[axisName] && this.Axis[axisName].Color ? this.Axis[axisName].Color : "#999", ColorLine: this.Axis[axisName] && this.Axis[axisName].ColorLine ? this.Axis[axisName].ColorLine : "#333", ColorSteps: this.Axis[axisName] && this.Axis[axisName].ColorSteps ? this.Axis[axisName].ColorSteps : "#333", ColorMinorSteps: this.Axis[axisName] && this.Axis[axisName].ColorMinorSteps ? this.Axis[axisName].ColorMinorSteps : "#999", ColorLabels: this.Axis[axisName] && this.Axis[axisName].ColorLabels ? this.Axis[axisName].ColorLabels : "#999", FontLabels: this.Axis[axisName] && this.Axis[axisName].FontLabels ? this.Axis[axisName].FontLabels : "9pt Arial", ColorTitle: this.Axis[axisName] && this.Axis[axisName].ColorTitle ? this.Axis[axisName].ColorTitle : "#999", FontTitle: this.Axis[axisName] && this.Axis[axisName].FontTitle ? this.Axis[axisName].FontTitle : "12pt Arial", Title: this.Axis[axisName] && this.Axis[axisName].Title ? this.Axis[axisName].Title : "", Labels: this.Axis[axisName] && this.Axis[axisName].Labels ? this.Axis[axisName].Labels : 0, LabelFormat: this.Axis[axisName] && this.Axis[axisName].LabelFormat ? this.Axis[axisName].LabelFormat : "dd-MM-yyyy", LabelFormatEdge: this.Axis[axisName] && this.Axis[axisName].LabelFormatEdge ? this.Axis[axisName].LabelFormatEdge : "dd-MM-yyyy", ValueCount: this.Series[i].Data.length, SizeBetweenValues: null, Series: [] }; } axises.push(detected[axisName]); detected[axisName].Series.push(i); } var primaryAxis = null; var secondaryAxis = null; if (this.Series[i].StackGroup) { if (!stackGroupData[this.Series[i].StackGroup]) stackGroupData[this.Series[i].StackGroup] = {}; var sg = stackGroupData[this.Series[i].StackGroup]; } // Find our primary axis and secondary axis for (var n = 0; n < axises.length; n++) { if (axises[n].Location >= 0 || axises[n].Location < 6) { if (primaryAxis === null) { primaryAxis = n; } else if (secondaryAxis === null) { secondaryAxis = n; } } } if (this.Series[i].StackGroup) { // Merge with existing stackGroup this.Series[i].Offsets = []; for (var n = 0; n < this.Series[i].Data.length; n++) { var item = this.Series[i].Data[n]; if (sg[item[primaryAxis]] === undefined) { this.Series[i].Offsets.push(0); sg[item[primaryAxis]] = item[secondaryAxis]; } else { this.Series[i].Offsets.push(sg[item[primaryAxis]]); sg[item[primaryAxis]] += item[secondaryAxis]; } } } for (var j = 0; j < axises.length; j++) { var d = axises[j]; var first = this.Series[i].Data[0][j]; var isDate = first.toLowerCase && d.Location != 7 ? true : false; if (d.Location < 6 && first.toLowerCase && (d.Type === undefined || d.Type === null)) { d.Type = 1; } if (d.Type == 3) { // Labels if (!d.LabelMapping) { d.LabelMapping = {}; d.CustomSteps = []; } d.Min = 0; for (var n = 0; n < this.Series[i].Data.length; n++) { if (d.LabelMapping[this.Series[i].Data[n][j]] !== undefined) continue; d.LabelMapping[this.Series[i].Data[n][j]] = d.CustomSteps.length; d.CustomSteps.push({ Text: this.Series[i].Data[n][j], Value: d.CustomSteps.length }); } d.Max = d.CustomSteps.length - 1; continue; } for (var n = 0; n < this.Series[i].Data.length; n++) { var value = isDate ? new Date(this.Series[i].Data[n][j]).getTime() : this.Series[i].Data[n][j]; if (value == null || isNaN(value)) continue; if (j == secondaryAxis && this.Series[i].Offsets) { value += this.Series[i].Offsets[n]; } else if (j == primaryAxis && n > 0) { var difference = (this.Series[i].Data[n][j].toLowerCase) ? new Date(this.Series[i].Data[n][j]).getTime() - new Date(this.Series[i].Data[n - 1][j]).getTime() : this.Series[i].Data[n][j] - this.Series[i].Data[n - 1][j]; if (d.SizeBetweenValues === null || d.SizeBetweenValues > difference) d.SizeBetweenValues = difference; } if (d.Min === null || d.Min > value) d.Min = value; if (d.Max === null || d.Max < value) d.Max = value; } if (d.Location >= 6 && !d.RangeResult) { d.RangeResult = [d.Min, d.Max]; } } } // Calculated required widths/heights var offsets = [0, 0, 0, 0]; // top, right, bottom, left for (var axisName in detected) { var d = detected[axisName]; if (d.Min == d.Max) { if (d.Max > 0) d.Min = 0; else if (d.Min < 0) d.Max = 0; else if (d.Min == 0) d.Max += 1; else { d.Min -= 1; d.Max += 1; } } d.Position = [0, 0, 0, 0]; // left, top, width, height if (d.Location == 0 || d.Location == 2) { // Top, Bottom var reqSize = 35 + (this.LegendLocation % 2 == d.Location % 2 ? 25 : 0); d.Position[3] = reqSize; d.Position[1] = (d.Location == 0 ? 0 : this.Height - reqSize); } else if (d.Location == 1 || d.Location == 3) { // Right, Left var reqSize = 75 + (this.LegendLocation % 2 == d.Location % 2 ? 100 : 0); d.Position[2] = reqSize; d.Position[0] = (d.Location == 3 ? 0 : this.Width - reqSize); } offsets[d.Location] += reqSize; } for (var i = 0; i < 4; i++) offsets[i] = offsets[i] == 0 ? this.MinChartPadding : offsets[i] + this.AxisPadding; // Generic axis padding for (var axisName in detected) { var d = detected[axisName]; if (d.Location == 0 || d.Location == 2 || d.Location == 4) { // Top/Bottom, use max width minus any offsets claimed on the left/right d.Position[2] = this.Width - (offsets[1] + offsets[3]); d.Position[0] = offsets[3]; if (!d.Size) { var pxPerValue = d.Position[2] / (d.Max - d.Min); if (d.SizeBetweenValues && !isNaN(d.SizeBetweenValues)) { d.Size = pxPerValue * d.SizeBetweenValues * 0.7; } else if (d.CustomSteps) { d.Size = (d.Position[2] / d.CustomSteps.length) * 0.7; } if (d.Size < 1) d.Size = 1; d.Position[0] += Math.ceil(d.Size / 1.2); d.Position[2] -= Math.ceil(d.Size / 1.2) * 2; } } else if (d.Location == 1 || d.Location == 3 || d.Location == 5) { // Right/Left d.Position[3] = this.Height - (offsets[0] + offsets[2]); d.Position[1] = offsets[0]; if (!d.Size) { var pxPerValue = d.Position[3] / (d.Max - d.Min); if (d.SizeBetweenValues && !isNaN(d.SizeBetweenValues)) { d.Size = pxPerValue * d.SizeBetweenValues * 0.7; } else if (d.CustomSteps) { d.Size = (d.Position[3] / d.CustomSteps.length) * 0.7; } if (d.Size < 1) d.Size = 1; d.Position[1] += Math.ceil(d.Size / 1.2); d.Position[3] -= Math.ceil(d.Size / 1.2) * 2; } } else if (d.Location >= 6) { continue; } // Find out the optimal label count/step var stepCount = Math.floor(d.Position[(d.Location % 2) == 0 ? 2 : 3] / this.LabelSpacing); // Optimal(max) label count if (stepCount < 2) stepCount = 2; if (d.Type == 1 || d.Type == 2) { // DateTime, mapped on time || DateTime, mapped on labels // Date time var labelFormat = d.LabelFormat; var labelFormatEdge = d.LabelFormatEdge; var timeDifference = (d.Max - d.Min) / 1000; if (d.Labels == 0) { // Automatic, find out what labels to use based on the time difference if (timeDifference > 60 * 60 * 24 * 365) { d.Labels = 1; // Years labelFormatEdge = "DD-MM-YYYY"; labelFormat = "YYYY"; } else if (timeDifference > 60 * 60 * 24 * 35) { d.Labels = 2; // Months labelFormatEdge = "DD-MM-YYYY"; labelFormat = "DD-MM-YYYY"; } else if (timeDifference > 60 * 60 * 24 * 2) { d.Labels = 4; // Days labelFormatEdge = "DD-MM-YYYY HH:mm"; labelFormat = "DD"; } else if (timeDifference > 60 * 60 * 5) { d.Labels = 8; // Hours labelFormatEdge = "DD-MM-YYYY HH:mm"; labelFormat = "HH"; } else if (timeDifference > 60 * 2) { d.Labels = 16; // Minutes labelFormatEdge = "DD-MM-YYYY HH:mm:ss"; labelFormat = "HH:mm"; } else { d.Labels = 32; // Seconds labelFormatEdge = "DD-MM-YYYY HH:mm:ss"; labelFormat = "mm:ss"; } } d.CustomSteps = []; d.LabelMapping = {}; d.ScaleMin = d.Min; var cur = d.Min; d.CustomSteps.push({ Text: this.FormatDate(d.Min, labelFormatEdge), Value: d.Min }); while (cur < d.Max) { // Note: Make sure the labels will fall at exact day/month/year changes if (d.Labels == 32) { // Seconds cur += 1 * 1000; cur = cur - (cur % 1000); } else if (d.Labels == 16) { // Minutes cur += 60 * 1000; cur = cur - (cur % (60 * 1000)); // Round down } else if (d.Labels == 8) { // Hours cur += 60 * 60 * 1000; cur = cur - (cur % (60 * 60 * 1000)); // Round down } else if (d.Labels == 4) { // Days var tmp = new Date(cur); if (this.TimeZone == "UTC") { tmp.setUTCDate(tmp.getUTCDate() + 1); tmp.setUTCHours(0); tmp.setUTCMinutes(0); tmp.setUTCSeconds(0); } else { tmp.setDate(tmp.getDate() + 1); tmp.setHours(0); tmp.setMinutes(0); tmp.setSeconds(0); } cur = tmp.getTime(); } else if (d.Labels == 2) { // Months var tmp = new Date(cur); if (this.TimeZone == "UTC") { tmp.setUTCMonth(tmp.getUTCMonth() + 1); tmp.setUTCDate(1); tmp.setUTCHours(0); tmp.setUTCMinutes(0); tmp.setUTCSeconds(0); } else { tmp.setMonth(tmp.getMonth() + 1); tmp.setDate(1); tmp.setHours(0); tmp.setMinutes(0); tmp.setSeconds(0); } cur = tmp.getTime(); } else if (d.Labels == 1) { // Years var tmp = new Date(cur); if (this.TimeZone == "UTC") { tmp.setUTCFullYear(tmp.getUTCFullYear() + 1); tmp.setUTCMonth(0); tmp.setUTCDate(1); tmp.setUTCHours(0); tmp.setUTCMinutes(0); tmp.setUTCSeconds(0); } else { tmp.setFullYear(tmp.getFullYear() + 1); tmp.setMonth(0); tmp.setDate(1); tmp.setHours(0); tmp.setMinutes(0); tmp.setSeconds(0); } cur = tmp.getTime(); } else { cur = d.Max; } d.LabelMapping[cur] = d.CustomSteps.length; d.CustomSteps.push({ Text: this.FormatDate(cur >= d.Max ? d.Max : cur, (cur >= d.Max ? labelFormatEdge : labelFormat)), Value: cur >= d.Max ? d.Max : cur }); d.ScaleMax = cur >= d.Max ? d.Max : cur; } if (d.CustomSteps.length > stepCount) { // Reduce amount of labels // TODO: Try to make more logical/nicer steps var stepAmount = Math.ceil(d.CustomSteps.length / stepCount); for (var i = 0; i < d.CustomSteps.length - 1; i += 1) { if (i % stepAmount > 0 || i + stepAmount*0.8 > d.CustomSteps.length) { d.CustomSteps[i].Text = ""; } } } } if (!d.CustomSteps || d.CustomSteps.length == 0) { // https://stackoverflow.com/questions/237220/tickmark-algorithm-for-a-graph-axis var epsilon = (d.Max - d.Min) / 1e6; var max = d.Max/* + epsilon*/; var min = d.Min/* - epsilon*/; var range = max - min; var roughStep = range / (stepCount - 1); var goodNormalizedSteps = [1, 1.5, 2, 2.5, 5, 7.5, 10]; Math.log10 = Math.log10 || function (x) { return Math.log(x) * Math.LOG10E; }; var stepPower = Math.pow(10, -Math.floor(Math.log10(Math.abs(roughStep)))); var normalizedStep = roughStep * stepPower; var goodNormalizedStep = goodNormalizedSteps.First(function (n) { return n >= normalizedStep; }); d.Step = goodNormalizedStep / stepPower; if (!d.Reverse) { d.ScaleMax = Math.ceil(max / d.Step) * d.Step; if (d.ScaleMax + d.Step <= d.Max) d.ScaleMax = d.Max; d.ScaleMin = Math.floor(min / d.Step) * d.Step; if (d.ScaleMin - d.Step >= d.Min) d.ScaleMin = d.Min; d.StepCount = ((d.ScaleMax - d.ScaleMin) / d.Step) + 1; } else { d.ScaleMax = Math.floor(min / d.Step) * d.Step; if (d.ScaleMax + d.Step <= d.Min) d.ScaleMax = d.Min; d.ScaleMin = Math.ceil(max / d.Step) * d.Step; if (d.ScaleMin - d.Step >= d.Max) d.ScaleMin = d.Max; d.StepCount = ((d.ScaleMin - d.ScaleMax) / d.Step) + 1; d.Step = 0 - d.Step; } } else { d.StepCount = d.CustomSteps.length; if (d.ScaleMin === undefined) { d.ScaleMin = d.Min; d.ScaleMax = d.Max; } if (d.Reverse) d.CustomSteps = d.CustomSteps.reverse(); } d.ValueToPX = function (value, excludeOwnPosition) { if (value && value.toLowerCase) { if (this.LabelMapping[value] !== undefined) { value = this.LabelMapping[value]; } else if (this.Type == 1 || this.Type == 2) { value = new Date(value).getTime(); } } var sizePX = this.Position[(this.Location % 2) == 0 ? 2 : 3]; if (this.Reverse) { // Reverse var difference = (this.ScaleMin - this.ScaleMax); return Math.round(sizePX - (((value - this.ScaleMax) / difference) * sizePX) + (excludeOwnPosition ? 0 : this.Position[(this.Location % 2) == 0 ? 0 : 1])); } return Math.round((((value - this.ScaleMin) / (this.ScaleMax - this.ScaleMin)) * sizePX) + (excludeOwnPosition ? 0 : this.Position[(this.Location % 2) == 0 ? 0 : 1])); }; // Draw axis and legend var stepTexts = []; if (d.Location == 2 || d.Location == 0) { // Bottom, Top var y = d.Location == 2 ? d.Position[1] : d.Position[3] + d.Position[1]; if (d.Title) { this.Elements.Canvas.Add({ _: TK.Draw.Text, X: d.Position[2] + d.Position[0], Y: (d.Location == 2 ? y + 30 : y - 30), Anchor: TK.Draw.AnchorRight | (d.Location == 2 ? TK.Draw.AnchorTop : TK.Draw.AnchorBottom), Fill: d.ColorTitle, Font: d.FontTitle, Text: d.Title }); } this.Elements.Canvas.Add({ _: TK.Draw.Line, X: d.Position[0], Y: y, W: d.Position[2], H: 0, Stroke: d.ColorLine }); for (var i = 0; i < d.StepCount; i++) { var posX = (d.Position[0] + (d.Position[2] / (d.StepCount - 1)) * i); if (d.CustomSteps && d.CustomSteps[i] && d.CustomSteps[i].Value !== null && d.CustomSteps[i].Value !== undefined) posX = d.ValueToPX(d.CustomSteps[i].Value); if (d.CustomSteps && d.CustomSteps[i] && d.CustomSteps[i].Text == "") { this.Elements.Canvas.Add({ _: TK.Draw.Line, X: posX, Y: (d.Location == 2 ? y : y - 5), W: 0, H: 5, Stroke: d.ColorMinorSteps }); continue; } this.Elements.Canvas.Add({ _: TK.Draw.Line, X: posX, Y: (d.Location == 2 ? y : y - 10), W: 0, H: 10, Stroke: d.ColorSteps }); stepTexts[i] = this.Elements.Canvas.Add({ _: TK.Draw.Text, X: posX, Y: (d.Location == 2 ? y + 15 : y - 15), Fill: d.ColorLabels, Font: d.FontLabels, Text: d.CustomSteps && d.CustomSteps[i] ? d.CustomSteps[i].Text : Math.round((d.ScaleMin + (d.Step * i)) * 10000)/ 10000, Anchor: TK.Draw.AnchorCenter | (d.Location == 2 ? TK.Draw.AnchorTop : TK.Draw.AnchorBottom), }); if (i + 1 == d.StepCount) { var width = stepTexts[i].MeasureWidth(); // Move label if text goes outside the canvas if (posX + width / 2 > (this.Width - 5)) stepTexts[i].X = this.Width - ((width / 2) + 5); // Hide overlapping labels for (var n = i - 1; n >= 0; n--) { if (!stepTexts[n]) continue; var curWidth = stepTexts[n].MeasureWidth(); if (stepTexts[n].X + (curWidth / 2) + 10 < stepTexts[i].X - (width / 2)) break; stepTexts[n].Text = ""; } if (stepTexts[0] && stepTexts[0].Text) { width = stepTexts[0].MeasureWidth(); for (var n = 1; n < i - 1; n++) { if (!stepTexts[n]) continue; var curWidth = stepTexts[n].MeasureWidth(); if (stepTexts[n].X - ((curWidth / 2) + 10) > stepTexts[0].X + (width / 2)) break; stepTexts[n].Text = ""; } } } } if (this.LegendLocation == 2 || this.LegendLocation == 0) { var curPos = 25; if (this.LegendLocation == 0) y = -25; for (var i = 0; i < this.Series.length; i++) { if (!this.Series[i].Title || this.Series[i].Title == "" || this.Series[i].HiddenInLegend) continue; var legendBlock = this.Elements.Canvas.Add({ _: this.LegendTemplate, X: curPos, Y: y + 35, Series: this.Series[i], Chart: this }); curPos += legendBlock.GetWidth(); } } } else if (d.Location == 1 || d.Location == 3) { // Right, Left var x = d.Location == 1 ? d.Position[0] : d.Position[2] + d.Position[0]; if (d.Title) { this.Elements.Canvas.Add({ _: TK.Draw.Text, X: d.Location == 3 ? 5 : this.Width - 5, Y: d.Position[1] + (d.Position[3] / 2), Anchor: TK.Draw.AnchorCenter | TK.Draw.AnchorTop, Fill: d.ColorTitle, Font: d.FontTitle, Text: d.Title, Rotate: d.Location == 3 ? -90 : 90 }); } this.Elements.Canvas.Add({ _: TK.Draw.Line, X: x, Y: d.Position[1], W: 0, H: d.Position[3], Stroke: d.ColorLine }); for (var i = 0; i < d.StepCount; i++) { var posY = (d.Position[1] + (d.Position[3] / (d.StepCount - 1)) * i); if (d.CustomSteps && d.CustomSteps[i] && d.CustomSteps[i].Value !== null && d.CustomSteps[i].Value !== undefined) posY = d.ValueToPX(d.CustomSteps[i].Value); if (d.CustomSteps && d.CustomSteps[i] && d.CustomSteps[i].Text == "") { this.Elements.Canvas.Add({ _: TK.Draw.Line, X: (d.Location == 1 ? x : x - 5), Y: posY, W: 5, H: 0, Stroke: d.ColorMinorSteps }); continue; } this.Elements.Canvas.Add({ _: TK.Draw.Line, X: (d.Location == 1 ? x : x - 10), Y: posY, W: 10, H: 0, Stroke: d.ColorSteps }); this.Elements.Canvas.Add({ _: TK.Draw.Text, X: (d.Location == 1 ? x + 15 : x - 15), Y: posY, Fill: d.ColorLabels, Font: d.FontLabels, Text: d.CustomSteps && d.CustomSteps[i] ? d.CustomSteps[i].Text : Math.round((d.ScaleMin + (d.Step * i)) * 10000) / 10000, Anchor: (d.Location == 1 ? TK.Draw.AnchorLeft : TK.Draw.AnchorRight) | TK.Draw.AnchorMiddle, }); } } } this.AxisesDetected = detected; }, RefreshData: function () { for (var i = 0; i < this.Series.length; i++) { var s = this.Series[i]; if (!s.Data || s.Data.length == 0 || !s.Axis) continue; var axisesUsed = s.Axis.split(','); var pos = []; var firstAxis = null; for (var j = 0; j < axisesUsed.length; j++) { var axisName = axisesUsed[j]; var d = this.AxisesDetected[axisName]; if (!d) continue; if (!firstAxis) firstAxis = d; for (var n = 0; n < s.Data.length; n++) { var value = s.Data[n][j]; if (!pos[n]) pos[n] = [0, 0, d.Size, s.Color, 0, 15]; // X px center, Y px center, Size Primary, Color, Offset secondary axis,Size Secondary if (d.Location < 6) { var px = d.ValueToPX(value); if (!d.OffsetsPX) d.OffsetsPX = {}; if (firstAxis != d) { if (s.Offsets) { pos[n][4] = d.ValueToPX(s.Offsets[n] + d.ScaleMax) - d.ValueToPX(d.ScaleMax); } var key = ((d.Location % 2 == 1) ? pos[n][0] : pos[n][1]); key += "-" + (s.StackGroup ? s.StackGroup : Math.random()); var max = firstAxis.Location == 0 || firstAxis.Location == 3 ? ((d.Location % 2 == 0) ? d.Position[0] : d.Position[1]) : ((d.Location % 2 == 1) ? d.Position[3] + d.Position[1] : d.Position[2] + d.Position[0]); if (d.OffsetsPX[key] === undefined) d.OffsetsPX[key] = max; var height = 0; if (firstAxis.Location == 0 || firstAxis.Location == 3) { height = (px + pos[n][4]) - d.OffsetsPX[key]; d.OffsetsPX[key] += height; } else { height = d.OffsetsPX[key] - (px + pos[n][4]); d.OffsetsPX[key] -= height; } pos[n][5] = height; } if (d.Location % 2 == 0) { pos[n][0] = px; } else { pos[n][1] = px; } } else if (d.Location == 8) { // Size if (d.Max == d.Min) pos[n][2] = d.RangeResult[1]; else pos[n][2] = (((value - d.Min) / (d.Max - d.Min)) * (d.RangeResult[1] - d.RangeResult[0])) + d.RangeResult[0]; } else if (d.Location == 7) { // Color if (value.toLowerCase) { pos[n][3] = value; } else { var colorA = TK.Draw.GetColor(d.RangeResult[0]), colorB = TK.Draw.GetColor(d.RangeResult[1]); for (var n2 = 0; n2 < colorA.length; n2++) { if (d.Max == d.Min) { colorA[n2] = colorB[n2]; } else { colorA[n2] = (((value - d.Min) / (d.Max - d.Min)) * (colorB[n2] - colorA[n2])) + colorA[n2]; } } pos[n][3] = "rgba(" + colorA.join(",") + ")"; } } } } var curStackGroups = {}; var barCount = 0; var barIndex = 0; for (var j = 0; j < firstAxis.Series.length; j++) { var otherSerie = this.Series[firstAxis.Series[j]]; if ((otherSerie.Style & 2) == 0) { // Check if this serie has bars continue; } if (otherSerie.StackGroup && curStackGroups[otherSerie.StackGroup] !== undefined) { if (firstAxis.Series[j] == i) barIndex = curStackGroups[otherSerie.StackGroup]; continue; } if (otherSerie.StackGroup) curStackGroups[otherSerie.StackGroup] = barCount; if (firstAxis.Series[j] == i) barIndex = barCount; barCount++; } for (var j = 0; j < pos.length; j++) { if ((s.Style & 4) > 0) { // Points this.Elements.Canvas.Add({ _: TK.Draw.Circle, X: pos[j][0], Y: pos[j][1] + pos[j][4], W: pos[j][2], H: pos[j][2], Fill: pos[j][3], Anchor: TK.Draw.AnchorCenter | TK.Draw.AnchorMiddle, Click: s.Click, MouseOver: s.MouseOver, MouseOut: s.MouseOut }); } if ((s.Style & 2) > 0) { // Bars, We use our first axis as 'base' var size = (pos[j][2] / barCount) * 0.8; var offset = barIndex * size - (pos[j][2] / 2 - pos[j][2] * 0.1); var barRect = { _: TK.Draw.Rect, Fill: pos[j][3], Click: s.Click, MouseOver: s.MouseOver, MouseOut: s.MouseOut, RoundCorners: this.RoundCorners, ShadeSize: this.ShadeSize ? Math.round(size * this.ShadeSize) : 0 }; if (firstAxis.Location == 2 || firstAxis.Location == 0) { barRect.X = pos[j][0] + offset; barRect.Y = pos[j][1] + pos[j][4]; barRect.W = size; barRect.H = pos[j][5]; barRect.ShadePosition = 1; if (firstAxis.Location == 2) barRect.Anchor = TK.Draw.AnchorLeft | TK.Draw.AnchorTop; else barRect.Anchor = TK.Draw.AnchorLeft | TK.Draw.AnchorBottom; } else if (firstAxis.Location == 3 || firstAxis.Location == 1) { barRect.X = (pos[j][0] + pos[j][4]); barRect.Y = pos[j][1] + offset; barRect.W = pos[j][5]; barRect.H = size; barRect.ShadePosition = 2; if (firstAxis.Location == 3) barRect.Anchor = TK.Draw.AnchorRight | TK.Draw.AnchorTop; else barRect.Anchor = TK.Draw.AnchorLeft | TK.Draw.AnchorTop; } this.Elements.Canvas.Add(barRect); } } if ((s.Style & 1) > 0 || (s.Style & 8) > 0) { //console.log(pos); var heights = []; for (var n = 0; n < pos.length; n++) { pos[n][(firstAxis.Location == 2 || firstAxis.Location == 0) ? 1 : 0] += pos[n][4]; if ((s.Style & 8) > 0) { heights.push(pos[n][5]); } } this.Elements.Canvas.Add({ _: TK.Draw.LineThroughPoints, X: 0, Y: 0, LineWidth: s.LineWidth, Heights: heights, Fill: s.Fill, Points: pos.Select(function (a) { return [a[0], a[1]] }), Stroke: pos[0][3], Smoothing: s.Smoothing, Anchor: TK.Draw.AnchorLeft | TK.Draw.AnchorTop }); } } } }; "use strict"; /* Minify Skip */ /* Minify Order(200) */ TK.Donut = { _: "div", Values: [ /* { Name: "Value A", Color: "#F00", ColorLabel: "#F00", Value: 1, Extrude: 10, Label: "Label 123" }, { Name: "Value B", Color: "#090", Value: 2 }, { Name: "Value B", Color: "#009", Value: 2 } */ ], Width: 400, Height: 200, Size: null, DonutStartX: null, DonutStartY: null, FinalSizeRatio: 0.8, DonutSize: 0.5, StartAngle: -90, EnableLabels: true, FontLabels: "10pt Verdana", ColorLabels: "rgba(0,0,0,0.5)", ColorLabelLines: "rgba(0,0,0,0.5)", LabelStyle: 2, // 0 = Name only, 1 = Value only, 2 = Both ShowValueAsPercentage: true, AnimationLength: 1000, HideZeroes: true, EnableLegend: false, LocationLegend: 1, // 0 = Top, 1 = Right, 2 = Bottom, 3 = Left ColorLegend: "rgba(0,0,0,0.8)", FontLegend: "10pt Verdana", LegendStyle: 0, // 0 = Name only, 1 = Value only, 2 = Both LegendSize: 0, // 0 = Auto LegendLineHeight: 25, Click: null, // function (valuePart) { }, Init: function () { if (!this.Size) this.Size = (this.Width > this.Height ? this.Height : this.Width); this.Clear(); this.Canvas = this.Add({ _: TK.Draw, Width: this.Width, Height: this.Height }, "Canvas"); this.Refresh(); }, Refresh: function () { this.Canvas.Clear(); var obj = this; var total = 0; var countLegend = 0; for (var i = 0; i < this.Values.length; i++) { if (this.Values[i].Value && this.Values[i].Value < 0) this.Values[i].Value = 0; if (this.Values[i].HideZeroes && !this.Values[i].Value) continue; total += this.Values[i].Value; countLegend++; } var donutStartX = this.DonutStartX; var donutStartY = this.DonutStartY; if (donutStartX === null) donutStartX = Math.round((this.Width - this.Size) / 2); if (donutStartY === null) donutStartY = Math.round((this.Height - this.Size) / 2); if (this.EnableLegend) { if (this.LegendSize == 0) { if (this.LocationLegend == 0 || this.LocationLegend == 2) { this.LegendSize = this.Width * 0.9; } else { // Find max width of the legend labels var maxWidth = 10; for (var i = 0; i < this.Values.length; i++) { var labelLegend = this.Values[i].Name; if (this.LegendStyle != 0) { labelLegend = (this.LegendStyle == 2 ? labelLegend + ": " : "") + (this.ShowValueAsPercentage ? "100 %" : this.Values[i].Value); } var curWidth = TK.Draw.Text.MeasureWidth(labelLegend, this.FontLegend); if (curWidth > maxWidth) maxWidth = curWidth; } this.LegendSize = maxWidth + 20; // Extra padding } } var legendSize = (this.LocationLegend == 0 || this.LocationLegend == 2) ? this.LegendLineHeight * countLegend : this.LegendSize; if ((this.LocationLegend == 0 || this.LocationLegend == 2) && this.Size > this.Height - legendSize) { this.Size = this.Height - legendSize; donutStartX = Math.round((this.Width - this.Size) / 2); donutStartY = Math.round((this.Height - this.Size) / 2); } else if ((this.LocationLegend == 1 || this.LocationLegend == 3) && this.Size > this.Width - legendSize) { this.Size = this.Width - legendSize; donutStartX = Math.round((this.Width - this.Size) / 2); donutStartY = Math.round((this.Height - this.Size) / 2); } if (this.LocationLegend == 0) donutStartY = legendSize + (((this.Height - legendSize) / 2) - (this.Size / 2)); else if (this.LocationLegend == 1) donutStartX = ((this.Width - legendSize) - this.Size) / 2; else if (this.LocationLegend == 2) donutStartY = ((this.Height - legendSize) - this.Size) / 2; else if (this.LocationLegend == 3) donutStartX = legendSize + (((this.Width - legendSize) / 2) - (this.Size / 2)); } var middlePointX = (this.Size * 0.5) + donutStartX; var middlePointY = (this.Size * 0.5) + donutStartY; var curAngle = this.StartAngle; var textElements = []; for (var i = 0; i < this.Values.length; i++) { if (this.Values[i].HideZeroes && !this.Values[i].Value) continue; var size = (this.Values[i].Value / total) * 360; var donutPart = this.Canvas.Add({ _: TK.Draw.Circle, DonutSize: this.DonutSize, X: middlePointX, Y: middlePointY, W: this.Size, H: this.Size, Size: size, Angle: curAngle, Extrude: 10, Fill: this.Values[i].Color, Value: this.Values[i], Anchor: window.TK.Draw.AnchorCenter | window.TK.Draw.AnchorMiddle, MouseOver: function () { if (obj.EnableLegend || obj.Click) { if (this.Value.LegendElement) this.Value.LegendElement.Elements.Text.Animate("X", 20, 150); this.Animate("Extrude", 10, 150); } }, MouseOut: function () { if (obj.EnableLegend || obj.Click) { if (this.Value.LegendElement) this.Value.LegendElement.Elements.Text.Animate("X", 15, 150); this.Animate("Extrude", this.Value.Extrude ? this.Value.Extrude : 0, 150); } }, Click: function () { if (obj.Click) obj.Click(this.Value); } }); donutPart.Animate("W", this.Size * this.FinalSizeRatio, this.AnimationLength, window.TK.Draw.EaseBounce); donutPart.Animate("Extrude", this.Values[i].Extrude ? this.Values[i].Extrude : 0, this.AnimationLength, window.TK.Draw.EaseBounce); // Generate labels (line from the middle) var label = this.Values[i].Name; if (this.LabelStyle != 0) { label = (this.LabelStyle == 2 ? label + ": " : "") + (this.ShowValueAsPercentage ? Math.round(this.Values[i].Value / total * 100) + " %" : this.Values[i].Value); } if (this.Values[i].Label) { label = this.Values[i].Label; } var labelLegend = this.Values[i].Name; if (this.LegendStyle != 0) { labelLegend = (this.LegendStyle == 2 ? labelLegend + ": " : "") + (this.ShowValueAsPercentage ? Math.round(this.Values[i].Value / total * 100) + " %" : this.Values[i].Value); } if (this.EnableLabels && label && !this.Values[i].DisableLabel) { var maxSizeFromMiddle = this.Size * this.FinalSizeRatio * 0.5; if (this.Values[i].Extrude) { maxSizeFromMiddle += this.Values[i].Extrude; } var middleRad = (curAngle + (size * 0.5)) * Math.PI / 180; var x = Math.cos(middleRad) * maxSizeFromMiddle * (this.DonutSize * 1.5); var y = Math.sin(middleRad) * maxSizeFromMiddle * (this.DonutSize * 1.5); var xText = Math.cos(middleRad) * maxSizeFromMiddle * 1.1; var yText = Math.sin(middleRad) * maxSizeFromMiddle * 1.1; var middleCircle, line1, line2, text; middleCircle = this.Canvas.Add({ _: TK.Draw.Circle, X: middlePointX + x, Y: middlePointY + y, W: 10, H: 10, ZIndex: 10, Fill: "rgba(0,0,0,0)", Anchor: window.TK.Draw.AnchorCenter | window.TK.Draw.AnchorMiddle, }); line1 = this.Canvas.Add({ _: TK.Draw.Line, X: middlePointX + x, Y: middlePointY + y, X2: middlePointX + xText, Y2: middlePointY + yText, ZIndex: 10, Stroke: "rgba(0,0,0,0)", Anchor: window.TK.Draw.AnchorTop | window.TK.Draw.AnchorLeft, }); if (xText < 0) { line2 = this.Canvas.Add({ _: TK.Draw.Line, X: middlePointX + xText, Y: middlePointY + yText, X2: middlePointX + xText + -5, Y2: middlePointY + yText, ZIndex: 10, Stroke: "rgba(0,0,0,0)", Anchor: window.TK.Draw.AnchorTop | window.TK.Draw.AnchorLeft, }); text = this.Canvas.Add({ _: TK.Draw.Text, X: middlePointX + xText + -10, Y: middlePointY + yText, ZIndex: 100, Fill: "rgba(0,0,0,0)", Text: label, Font: this.FontLabels, Anchor: window.TK.Draw.AnchorMiddle | window.TK.Draw.AnchorRight, }); } else { line2 = this.Canvas.Add({ _: TK.Draw.Line, X: middlePointX + xText, Y: middlePointY + yText, X2: middlePointX + xText + 5, Y2: middlePointY + yText, ZIndex: 10, Stroke: "rgba(0,0,0,0)", Anchor: window.TK.Draw.AnchorTop | window.TK.Draw.AnchorLeft, }); text = this.Canvas.Add({ _: TK.Draw.Text, X: middlePointX + xText + 10, Y: middlePointY + yText, ZIndex: 100, Fill: "rgba(0,0,0,0)", Text: label, Font: this.FontLabels, Anchor: window.TK.Draw.AnchorMiddle | window.TK.Draw.AnchorLeft, }); } text.FillWidthHeight(); //console.log(text.GetRect()); for (var textI = 0; textI < textElements.length; textI++) { while (text.Overlaps(textElements[textI])) { if (xText < 0) text.Y -= 1; else text.Y += 1; line1.Y2 = text.Y; line2.Y = text.Y; line2.Y2 = text.Y; } } textElements.push(text); var finalColor = this.Values[i].ColorLabel ? this.Values[i].ColorLabel : this.ColorLabels; var finalColorLine = this.Values[i].ColorLabelLine ? this.Values[i].ColorLabelLine : this.ColorLabelLines; middleCircle.Animate("Fill", finalColorLine, this.AnimationLength, TK.Draw.EaseExponential); line1.Animate("Stroke", finalColorLine, this.AnimationLength, TK.Draw.EaseExponential); line2.Animate("Stroke", finalColorLine, this.AnimationLength, TK.Draw.EaseExponential); text.Animate("Fill", finalColor, this.AnimationLength, TK.Draw.EaseExponential); } if (this.EnableLegend) { var obj = this; var legend = { _: TK.Draw.Group, DonutPart: donutPart, ValuesObj: this.Values[i], X: 0, Y: i * 25, Anchor: window.TK.Draw.AnchorMiddle | window.TK.Draw.AnchorLeft, Elements: { Circle: { _: TK.Draw.Circle, X: 0, Y: this.LegendLineHeight / 2, W: 10, H: 10, Fill: this.Values[i].Color, Anchor: window.TK.Draw.AnchorMiddle | window.TK.Draw.AnchorLeft, }, Text: { _: TK.Draw.Text, X: 15, Y: this.LegendLineHeight / 2, Fill: this.ColorLegend, Text: labelLegend, Font: this.FontLegend, Anchor: window.TK.Draw.AnchorMiddle | window.TK.Draw.AnchorLeft, } }, GetRect: function () { return [this.X, this.Y, obj.LegendSize, obj.LegendLineHeight]; }, MouseOver: function () { this.Elements.Text.Animate("X", 20, 150); this.DonutPart.Animate("Extrude", 10, 150); }, MouseOut: function () { this.Elements.Text.Animate("X", 15, 150); this.DonutPart.Animate("Extrude", this.ValuesObj.Extrude ? this.ValuesObj.Extrude : 0, 150); } }; if (this.LocationLegend == 0) { legend.Y = (i * this.LegendLineHeight); legend.X = (this.Width / 2) - (this.LegendSize / 2); legend.Anchor = window.TK.Draw.AnchorTop | window.TK.Draw.AnchorLeft; } else if (this.LocationLegend == 1) { legend.X = this.Width - this.LegendSize; legend.Y = ((this.Height / 2) - ((this.LegendLineHeight * countLegend) / 2)) + (i * this.LegendLineHeight); } else if (this.LocationLegend == 2) { legend.Y = (this.Height - (this.LegendLineHeight * countLegend)) + (i * this.LegendLineHeight); legend.X = (this.Width / 2) - (this.LegendSize / 2); legend.Anchor = window.TK.Draw.AnchorTop | window.TK.Draw.AnchorLeft; } else if (this.LocationLegend == 3) { legend.Y = ((this.Height / 2) - ((this.LegendLineHeight * countLegend) / 2)) + (i * this.LegendLineHeight); } this.Values[i].LegendElement = this.Canvas.Add(legend); } curAngle += size; } this.Canvas.Refresh(); } };"use strict"; /* Minify Order(200) */ window.TK.Dropdown = { _: "div", className: "toolkitDropdown", Data: null, // Selected value(s) Options: [], // Array of { Value: ..., Text: ... } Multiple: false, // Allow multiple selections Placeholder: "Select...", SelectedText: "{0} selected", PaddingCheckSelectedFit: 10, MaxDisplayItems: 10, // Maximum items to show before scrolling SelectedClass: "toolkitDropdownSelected", // Class for selected items OpenClass: "toolkitDropdownOpen", // Class when dropdown is open ListItemTemplate: { _: "li", Dropdown: null, Data: null, Init: function () { this.innerText = this.Data.Text; this.Value = this.Data.Value; this.className = this.Dropdown.IsSelected(this.Data.Value) ? this.Dropdown.SelectedClass : ""; }, onclick: function (a) { this.Dropdown.SelectItem(this.Value); if (!this.Dropdown.Multiple) { this.Dropdown.ToggleDropdown(); } } }, Init: function () { var obj = this; if (this.DataSettings) { var fields = ["Placeholder", "MaxDisplayItems", "Multiple", "Options", "SelectedClass", "OpenClass"]; for (var i = 0; i < fields.length; i++) { if (this.DataSettings[fields[i]] !== undefined) this[fields[i]] = this.DataSettings[fields[i]]; } } this.Clear(); this.Add({ _: "div", className: "toolkitDropdownDisplay", onclick: function (a) { obj.ToggleDropdown() }, Elements: { SelectedItems: { _: "ul", style: { listStyle: "none", padding: 0, margin: 0 }, }, Placeholder: { _: "span", innerText: this.Placeholder, style: { display: this.Data ? "none" : "inline" }, }, }, }, "Display"); this.Add({ _: "div", className: "toolkitDropdownOptions", style: { display: "none", maxHeight: this.MaxDisplayItems * 35 + "px" }, Elements: { OptionsList: { _: "ul", style: { listStyle: "none", padding: 0, margin: 0 }, }, }, }, "Options"); this.RefreshOptions(); this.UpdateDisplay(); }, RefreshOptions: function () { var obj = this; this.Elements.Options.Elements.OptionsList.Clear(); for (var i = 0; i < this.Options.length;i++) { var option = this.Options[i]; if (option.Text === undefined || option.Text === null) option.Text = option.Value; else if (option.Value === undefined || option.Value === null) option.Value = option.Text; if (option.Text === undefined || option.Text === null) continue; this.Elements.Options.Elements.OptionsList.Add({ _: this.ListItemTemplate, Dropdown: this, Data: option }); } }, SelectItem: function (value) { if (this.Multiple) { if (!this.Data) this.Data = []; if (this.IsSelected(value)) { this.Data.splice(this.Data.indexOf(value), 1); } else { this.Data.push(value); } } else { this.Data = value; } this.RefreshOptions(); this.UpdateDisplay(); if (this.onchange) this.onchange(); }, IsSelected: function (value) { return this.Multiple ? this.Data && this.Data.indexOf(value) >= 0 : this.Data === value; }, UpdateDisplay: function () { var obj = this; this.Elements.Display.Elements.SelectedItems.Clear(); if (!this.Data) return; var bboxDisplay = this.Elements.Display.getBoundingClientRect(); var first = true; if (this.Multiple && this.Data) { for (var i = 0; i < this.Data.length; i++) { var value = this.Data[i]; var option = this.Options.First(function (a) { return a.Value == value; }); if (option) { if (!first) { this.Elements.Display.Elements.SelectedItems.Add({ _: "span", innerText: ", ", }); } first = false; var item = this.Elements.Display.Elements.SelectedItems.Add({ _: "li", innerText: option.Text, }); var bboxItem = item.getBoundingClientRect(); if (bboxItem.right > bboxDisplay.right - this.PaddingCheckSelectedFit) { this.Elements.Display.Elements.SelectedItems.Clear(); this.Elements.Display.Elements.SelectedItems.Add({ _: "li", innerText: this.SelectedText.replace(/\{0\}/g, this.Data.length), }); break; } } } } else if (!this.Multiple && this.Data) { var option = this.Options.First(function (a) { return a.Value == obj.Data; }); if (option) { first = false; this.Elements.Display.Elements.SelectedItems.Add({ _: "li", innerText: option.Text, }); } } if (first) { this.Elements.Display.Elements.SelectedItems.Add({ _: "span", innerText: this.Placeholder, }); } this.Elements.Display.Elements.Placeholder.style.display = this.Data ? "none" : "inline"; }, ToggleDropdown: function () { this.Elements.Options.style.display = this.Elements.Options.style.display === "none" ? "block" : "none"; this.className = this.className.indexOf(this.OpenClass) >= 0 ? this.className.replace(this.OpenClass, "") : this.className + " " + this.OpenClass; }, GetValue: function () { return this.Data; } }; if (window.TK.Form) { window.TK.Form.DefaultTemplates.dropdown = { _: TK.Dropdown, }; } "use strict"; /* Minify Skip */ /* Minify Order(200) */ TK.Gauge = { _: "div", Ranges: [ /* { MinValue: 0, MaxValue: 5, Color: "#C00" }, { MinValue: 5, MaxValue: 15, Color: "#FC0" }, { MinValue: 15, MaxValue: 30, Color: "#090" } */ ], Width: 400, Height: 200, Value: 0, StartAngle: -180, EndAngle: 0, DonutSize: 0.8, Label: "", EnableValue: true, ColorLabel: "#666", FontLabel: "12pt Verdana", ColorValue: null, // Automatic (pick range color) FontValue: "14pt Verdana", TextValue: null, // Automatic (Use actual value) EnableShadow: true, ColorCursor: "#666", AnimationLength: 1000, ExtraSpacingBottom: 0, Style: 0, // 0: Circular gauge, 1: Horizontal Gauge SizeBar: 20, Init: function () { this.Clear(); this.Canvas = this.Add({ _: TK.Draw, Width: this.Width, Height: this.Height }, "Canvas"); this.Refresh(); }, Refresh: function () { this.Canvas.Clear(); var centerX = this.Width / 2; var extraSpacingHeight = (this.Height * 0.1) + this.ExtraSpacingBottom; if (this.Label) extraSpacingHeight += 25; if (this.EnableValue) extraSpacingHeight += 25; var centerY = this.Height - extraSpacingHeight; var size = this.Width > centerY * 2 ? centerY * 2 : this.Width; this.MinValue = this.Ranges.Min(function (a) { return a.MinValue; }); this.MaxValue = this.Ranges.Max(function (a) { return a.MaxValue; }); this.Difference = this.MaxValue - this.MinValue; this.DifferenceAngles = this.EndAngle - this.StartAngle; for (var i = 0; i < this.Ranges.length; i++) { if (this.Style == 0) { var startAngle = this.StartAngle + (((this.Ranges[i].MinValue - this.MinValue) / this.Difference) * this.DifferenceAngles); var sizeAngle = ((this.Ranges[i].MaxValue - this.Ranges[i].MinValue) / this.Difference) * this.DifferenceAngles; this.Canvas.Add({ _: TK.Draw.Circle, X: centerX, Y: centerY, W: size, H: size, Fill: this.Ranges[i].Color, DonutSize: this.DonutSize, Angle: startAngle, Size: sizeAngle, Anchor: window.TK.Draw.AnchorCenter | window.TK.Draw.AnchorMiddle, }); if (this.EnableShadow) { this.Canvas.Add({ _: TK.Draw.Circle, X: centerX, Y: centerY, W: size * (this.DonutSize + 0.05), H: size * (this.DonutSize + 0.05), Fill: "rgba(0,0,0,0.1)", DonutSize: 0.85, Angle: startAngle, Size: sizeAngle, Anchor: window.TK.Draw.AnchorCenter | window.TK.Draw.AnchorMiddle, }); } } else if (this.Style == 1) { var fromX = ((this.Ranges[i].MinValue - this.MinValue) / this.Difference) * this.Width; var widthX = ((this.Ranges[i].MaxValue - this.Ranges[i].MinValue) / this.Difference) * this.Width; this.Canvas.Add({ _: TK.Draw.Rect, X: fromX, Y: 10, W: widthX, H: this.SizeBar, Fill: this.Ranges[i].Color, Anchor: window.TK.Draw.AnchorLeft| window.TK.Draw.AnchorTop, }); if (this.EnableShadow) { this.Canvas.Add({ _: TK.Draw.Rect, X: fromX, Y: this.SizeBar + 5, W: widthX, H: 5, Fill: "rgba(0,0,0,0.1)", Anchor: window.TK.Draw.AnchorLeft | window.TK.Draw.AnchorTop, }); } } } // Draw cursor if (this.Style == 0) { this.Cursor = this.Canvas.Add({ _: TK.Draw.Rect, X: centerX, Y: centerY, W: size * 0.025, H: size * (0.5 - ((1 - this.DonutSize) * 0.25)), Fill: this.ColorCursor, Rotate: 0, Anchor: window.TK.Draw.AnchorCenter | window.TK.Draw.AnchorBottom, }); this.Canvas.Add({ _: TK.Draw.Circle, X: centerX, Y: centerY, W: size * 0.1, H: size * 0.1, Fill: this.ColorCursor, Anchor: window.TK.Draw.AnchorCenter | window.TK.Draw.AnchorMiddle, }); } else { this.Cursor = this.Canvas.Add({ _: TK.Draw.Rect, X: 0, Y: 0, W: 2, H: this.SizeBar + 20, Fill: this.ColorCursor, Anchor: window.TK.Draw.AnchorCenter | window.TK.Draw.AnchorTop, }); } var curY = centerY + (size * 0.1); if (this.Label) { this.LabelText = this.Canvas.Add({ _: TK.Draw.Text, X: centerX, Y: curY, Fill: this.ColorLabel, Font: this.FontLabel, Text: this.Label, Anchor: window.TK.Draw.AnchorCenter | window.TK.Draw.AnchorMiddle, }); curY += 25; } if (this.EnableValue) { this.ValueText = this.Canvas.Add({ _: TK.Draw.Text, X: centerX, Y: curY, Fill: this.ColorValue ? this.ColorValue : "#000", Font: this.FontValue, Text: this.Value, Anchor: window.TK.Draw.AnchorCenter | window.TK.Draw.AnchorMiddle, }); } this.SetValue(this.Value); this.Canvas.Refresh(); }, SetValue: function (newValue) { var animation = window.TK.Draw.EaseExponential; if (newValue >= this.MaxValue) { animation = window.TK.Draw.EaseBounce; newValue = this.MaxValue; } else if (newValue <= this.MinValue) { animation = window.TK.Draw.EaseBounce; newValue = this.MinValue; } if (this.EnableValue) { this.ValueText.Text = this.TextValue ? this.TextValue : newValue; var activeRange = this.Ranges.Where(function (a) { return a.MinValue <= newValue && a.MaxValue >= newValue; }); if (activeRange.length > 0 && !this.ColorValue) { this.ValueText.Fill = activeRange[activeRange.length - 1].Color; } } if (this.Style == 0) { var valueAngle = this.StartAngle + (((newValue - this.MinValue) / this.Difference) * this.DifferenceAngles); this.Cursor.Animate("Rotate", valueAngle + 90, this.AnimationLength, animation); } else if (this.Style == 1) { var valueX = (((newValue - this.MinValue) / this.Difference) * this.Width - 2) + 1; this.Cursor.Animate("X", valueX, this.AnimationLength, animation); } // Move cursor this.Value = newValue; } };"use strict"; /* Minify Skip */ /* Minify Order(200) */ TK.ImageEditor = { className: "toolkitImageEditor", Data: null, SpecificSize: null, // TODO: Resize uploaded image to [x, y] and force aspect ratio ValueType: "dataUrl", // by default, a data:image/jpeg;base64 value is used. Mime: "image/jpeg", Quality: 0.9, StorageClientId: undefined, StorageContainer: undefined, StoragePath: undefined, onchange: null, // Auto resize when image is larger than width and/or height MaxWidth: null, MaxHeight: null, Init: function () { var obj = this; if (this.DataSettings) { if (this.DataSettings.Mime !== undefined) this.Mime = this.DataSettings.Mime; if (this.DataSettings.Quality !== undefined) this.Quality = this.DataSettings.Quality; if (this.DataSettings.ValueType !== undefined) this.ValueType = this.DataSettings.ValueType; if (this.DataSettings.StorageContainer !== undefined) this.StorageContainer = this.DataSettings.StorageContainer; if (this.DataSettings.StorageClientId !== undefined) this.StorageClientId = this.DataSettings.StorageClientId; if (this.DataSettings.StoragePath !== undefined) this.StoragePath = this.DataSettings.StoragePath; } if (this.Data == "loading") { this.Elements.DropArea.style.backgroundImage = ""; this.Elements.DropArea.innerHTML = "Uploading..."; } else if (this.Data) { if (window.Blob && this.Data instanceof Blob) { this.Elements.DropArea.style.backgroundImage = ""; var fileReader = new FileReader(); fileReader.onload = function (e) { obj.Elements.DropArea.style.backgroundImage = "url('"+e.target.result+"')"; }; fileReader.readAsDataURL(this.Data); } else { // Url or dataUrl this.Elements.DropArea.style.backgroundImage = "url('" + this.Data + "')"; } this.Elements.DropArea.innerHTML = " "; } else { this.Elements.DropArea.style.backgroundImage = ""; this.Elements.DropArea.innerHTML = Svg.Icons.Image; } }, StorageHandlers: { dataUrl: function (imageEditor, canvas, callBackValue) { callBackValue(canvas.toDataURL(imageEditor.Mine, imageEditor.Quality)); }, url: function (imageEditor, canvas, callBackValue) { if (!TK.ServerStorage) return; var serverStorage = TK.Initialize({ _: TK.ServerStorage, Container: imageEditor.StorageClientId, ClientId: imageEditor.ClientId }); // Turn canvas into [Blob] canvas.toBlob(function (blob) { console.log(blob); var fileName = imageEditor.StoragePath ? imageEditor.StoragePath : imageEditor.Mime.replace("/", "."); serverStorage.Store(fileName, blob, function (fileMetaData) { console.log(fileMetaData); if (fileMetaData && fileMetaData.url) callBackValue(fileMetaData.url); else alert('Error uploading image'); }); }, imageEditor.Mime, imageEditor.Quality); /*var fd = new FormData(); fd.append("file", blob, "hello.txt"); var xhr = new XMLHttpRequest(); xhr.open('POST', '/server.php', true); xhr.onload = function(){ alert('upload complete'); }; xhr.send(fd);*/ }, blob: function (imageEditor, canvas, callBackValue) { // Turn the value into a [Blob] canvas.toBlob(function (blob) { callBackValue(blob); }, imageEditor.Mime, imageEditor.Quality); } }, PopupTemplate: { _: TK.Popup, EnableResize: false, Title: "Edit image", Width: 600, Height: 500, EnableBackDrop: true, ImageEditorInstance: null, Template: { Elements: { CanvasContainer: { Elements: { Buttons: { style: { position: "relative", height: "30px" }, Elements: { Slider: { _: "input", type: "range", min: 0, max: 360, value: 0, step: 5, style: { width: "99%" }, onchange: function () { this.Parent.Parent.Elements.SelectionCanvas.Rotation = parseFloat(this.value.toString()); this.Parent.Parent.Elements.SelectionCanvas.UpdateBoxPosition(); this.Parent.Parent.Elements.SelectionCanvas.Refresh(); }, oninput: function () { this.onchange(); } } } }, SelectionCanvas: { _: TK.Draw, Width: 580, Height: 370, style: { backgroundColor: "#000" }, CropTL: [50, 50], CropBR: [200, 200], NavigationEnabled: true, ZoomEnabled: true, Rotation: 0, // 0 Normal, 1 CW, 2 Upside down, 3 CC Init: function () { // Draw bounding box var obj = this; this.Add({ _: TK.Draw.Image, X: 10, Y: 10, W: 100, H: 100, Anchor: TK.Draw.AnchorCenter | TK.Draw.AnchorMiddle }, "Image"); this.Add({ _: TK.Draw.Rect, Stroke: "#CCC", MouseDown: function (x, y) { this.Dragging = true; this.StartPos = [x, y]; }, MouseMove: function (x, y) { if (!this.Dragging) return; var differenceX = x - this.StartPos[0]; var differenceY = y - this.StartPos[1]; obj.CropTL[0] += differenceX; obj.CropTL[1] += differenceY; obj.CropBR[0] += differenceX; obj.CropBR[1] += differenceY; if (obj.CropBR[0] >= obj.Width) { obj.CropTL[0] -= (obj.CropBR[0] - obj.Width); obj.CropBR[0] = obj.Width; } if (obj.CropBR[1] >= obj.Height) { obj.CropTL[1] -= (obj.CropBR[1] - obj.Height); obj.CropBR[1] = obj.Height; } if (obj.CropTL[0] < 0) { obj.CropBR[0] += -obj.CropTL[0]; obj.CropTL[0] = 0; } if (obj.CropTL[1] < 0) { obj.CropBR[1] += -obj.CropTL[1]; obj.CropTL[1] = 0; } this.StartPos = [x, y]; obj.UpdateBoxPosition(); }, MouseUp: function (x, y) { this.Dragging = false; } }, "Box"); var boxSize = 15; var boxes = ["TL", "TR", "BL", "BR"]; for (var i = 0; i < boxes.length; i++) { this.Add({ _: TK.Draw.Rect, Box: boxes[i], Anchor: boxes[i] == "TL" ? TK.Draw.AnchorRight | TK.Draw.AnchorBottom : boxes[i] == "TR" ? TK.Draw.AnchorLeft | TK.Draw.AnchorBottom : boxes[i] == "BL" ? TK.Draw.AnchorRight | TK.Draw.AnchorTop : boxes[i] == "BR" ? TK.Draw.AnchorLeft | TK.Draw.AnchorTop : TK.Draw.AnchorCenter | TK.Draw.AnchorBottom, Fill: "#FFF", W: boxSize, H: boxSize, MouseDown: function (x, y) { this.Dragging = true; }, MouseMove: function (x, y) { this.Fill = "#999"; if (this.Box == "TL") obj.style.cursor = "nw-resize"; else if (this.Box == "TR") obj.style.cursor = "ne-resize"; else if (this.Box == "BL") obj.style.cursor = "sw-resize"; else if (this.Box == "BR") obj.style.cursor = "se-resize"; if (!this.Dragging) return; if (this.Box == "TL") { obj.CropTL[0] = x; obj.CropTL[1] = y; } else if (this.Box == "TR") { obj.CropBR[0] = x; obj.CropTL[1] = y; } else if (this.Box == "BL") { obj.CropTL[0] = x; obj.CropBR[1] = y; } else if (this.Box == "BR") { obj.CropBR[0] = x; obj.CropBR[1] = y; } obj.UpdateBoxPosition(); }, MouseOut: function () { this.Fill = "#FFF"; obj.style.cursor = "default"; }, MouseUp: function (x, y) { this.Dragging = false; } }, "Box"+boxes[i]); } this.UpdateBoxPosition(); this.Refresh(); }, UpdateBoxPosition: function () { this.Elements.Image.Rotate = this.Rotation; this.Elements.Box.X = this.CropTL[0]; this.Elements.Box.Y = this.CropTL[1]; this.Elements.Box.W = this.CropBR[0] - this.CropTL[0]; this.Elements.Box.H = this.CropBR[1] - this.CropTL[1]; this.Elements.BoxTL.X = this.CropTL[0]; this.Elements.BoxTL.Y = this.CropTL[1]; this.Elements.BoxTR.X = this.CropBR[0]; this.Elements.BoxTR.Y = this.CropTL[1]; this.Elements.BoxBL.X = this.CropTL[0]; this.Elements.BoxBL.Y = this.CropBR[1]; this.Elements.BoxBR.X = this.CropBR[0]; this.Elements.BoxBR.Y = this.CropBR[1]; }, } } } } }, Buttons: { Apply: function () { var popup = this.Parent.Parent; var canvas = popup.Elements.Content.Elements.CanvasContainer.Elements.SelectionCanvas; // Apply transformations to image, upload and return url popup.ImageEditorInstance.Data = "loading"; popup.ImageEditorInstance.Init(); setTimeout(function () { var resizeAndCropCanvas = document.createElement("CANVAS"); //canvas.Scale; resizeAndCropCanvas.width = (canvas.CropBR[0] - canvas.CropTL[0]) / canvas.Ratio; resizeAndCropCanvas.height = (canvas.CropBR[1] - canvas.CropTL[1]) / canvas.Ratio; var offsetX = ((canvas.Elements.Image.X - canvas.Elements.Image.W / 2) - canvas.CropTL[0]) / canvas.Ratio; var offsetY = ((canvas.Elements.Image.Y - canvas.Elements.Image.H / 2) - canvas.CropTL[1]) / canvas.Ratio; var context = resizeAndCropCanvas.getContext("2d"); context.imageSmoothingQuality = 'high'; // TODO: Rotation canvas.Elements.Image.Rotate if (canvas.Elements.Image.Rotate) { var translateX = offsetX + (canvas.Elements.Image.Img.width / 2); var translateY = offsetY + (canvas.Elements.Image.Img.height / 2); context.translate(translateX, translateY); context.rotate(canvas.Elements.Image.Rotate * Math.PI / 180); context.translate(-translateX, -translateY); //context.translate(offsetX, offsetY); } context.drawImage(canvas.Elements.Image.Img, offsetX, offsetY, canvas.Elements.Image.Img.width, canvas.Elements.Image.Img.height); var inst = popup.ImageEditorInstance; // Resize if needed if ((inst.MaxWidth !== null && canvas.Elements.Image.Img.width > inst.MaxWidth) || (inst.MaxHeight !== null && canvas.Elements.Image.Img.height > inst.MaxHeight)) { var ratioW = 9999; var ratioH = 9999; var newWidth = 0; var newHeight = 0; if (inst.MaxWidth !== null) ratioW = inst.MaxWidth / resizeAndCropCanvas.width ; // 200 / 800 = 0.25 if (inst.MaxHeight !== null) ratioH = inst.MaxHeight / resizeAndCropCanvas.height; // 200 / 1000 = 0.2 if (ratioW < ratioH) { newWidth = inst.MaxWidth; newHeight = Math.floor(ratioW * resizeAndCropCanvas.height); } else { newWidth = Math.floor(ratioH * resizeAndCropCanvas.width); newHeight = inst.MaxHeight; } var resizeAndCropCanvas2 = document.createElement("CANVAS"); resizeAndCropCanvas2.width = newWidth; resizeAndCropCanvas2.height = newHeight; var context2 = resizeAndCropCanvas2.getContext("2d"); context2.imageSmoothingQuality = 'high'; context2.drawImage(resizeAndCropCanvas, 0, 0, newWidth, newHeight); resizeAndCropCanvas = resizeAndCropCanvas2; } if (inst.ValueType && inst.StorageHandlers[inst.ValueType]) { inst.StorageHandlers[inst.ValueType](inst, resizeAndCropCanvas, function (value) { inst.Data = value; inst.Init(); if (inst.onchange) inst.onchange(); }); } else { var url = resizeAndCropCanvas.toDataURL(inst.Mine, inst.Quality); inst.Data = url; inst.Init(); if (inst.onchange) inst.onchange(); } }, 1); }, Cancel: function () { } }, ImageLoaded: function (dataUri) { var canvas = this.Elements.Content.Elements.CanvasContainer.Elements.SelectionCanvas; this.ImageUrl = dataUri; var img = new Image(); img.onload = function () { canvas.Elements.Image.Img = this; var padding = 20; // Fill image into selection canvas (with some padding at the sides) var maxWidth = canvas.Width - (padding * 2), maxHeight = canvas.Height - (padding * 2); var imgWidth = this.width, imgHeight = this.height; var widthRatio = maxWidth / imgWidth, heightRatio = maxHeight / imgHeight; var bestRatio = Math.min(widthRatio, heightRatio); var newWidth = imgWidth * bestRatio, newHeight = imgHeight * bestRatio; canvas.Ratio = bestRatio; canvas.Elements.Image.X = (canvas.Width / 2); canvas.Elements.Image.Y = (canvas.Height / 2); canvas.Elements.Image.W = newWidth; canvas.Elements.Image.H = newHeight; canvas.CropTL = [canvas.Elements.Image.X - (newWidth / 2), canvas.Elements.Image.Y - (newHeight / 2)]; canvas.CropBR = [canvas.Elements.Image.X + (newWidth / 2), canvas.Elements.Image.Y + (newHeight / 2)]; canvas.UpdateBoxPosition(); canvas.Refresh(); }; img.src = dataUri; } }, GetValue: function () { if (this.Data == "loading") return null; return this.Data; }, HandleFiles: function (files) { var file = files[0]; // TODO: Support multiple files in the future var popup = this.Add({ _: this.PopupTemplate, ImageEditorInstance: this }); var reader = new FileReader(); reader.onload = function (e2) { popup.ImageLoaded(e2.target.result); }; reader.readAsDataURL(file); }, Elements: { DropArea: { onclick: function () { this.Near("FileUploadField").click(); }, ondragover: function (ev) { this.style.borderColor = "#1d1d1d"; ev.preventDefault(); }, ondragexit: function (ev) { this.style.borderColor = ""; ev.preventDefault(); }, ondrop: function (ev) { ev.preventDefault(); if (!ev.dataTransfer || !ev.dataTransfer.files) return; this.Parent.HandleFiles(ev.dataTransfer.files); } }, FileUploadField: { _: "input", type: "file", accept:"image/*", style: { display: "none" }, onchange: function (e) { if (!e || !e.target || !e.target.files || e.target.files.length == 0) return; //alert(e.target.files[0].name); this.Parent.HandleFiles(e.target.files); } } } }; if (window.TK.Form) { window.TK.Form.DefaultTemplates.image = { _: TK.ImageEditor }; } if (window.TK.HtmlEditor) { window.TK.HtmlEditor.ButtonTemplates.InsertImage = { innerHTML: Svg.Icons.Image, title: "Insert image", onmousedown: function () { var randId = "img" + new Date().getTime(); var html = ""; this.Near("Editor").focus(); document.execCommand("insertHTML", false, html); var img = document.getElementById(randId); if (img) { var tempEditor = TK.Initialize({ _: TK.ImageEditor, style: { display: "none" }, onchange: function () { var dataUrl = this.GetValue(); if (dataUrl) { img.src = dataUrl; } } }); tempEditor.Elements.FileUploadField.click(); } } }; }"use strict"; /* Minify Order(200) */ TK.Interface = { CurrentUserTemplate: null, CurrentLogoTemplate: null, DefaultContent: "Index", className: "toolkitInterface", MenuIconSize: 50, MenuTextSize: 150, ResponsiveThreshold: 400, ResponsiveFullThreshold: 700, EnableMenu: true, Content: {}, HamburgerIcon: null, // data Embedded: false, ShowMenu: false, // Show menu in mobile view (Hamburger menu icon toggles this) Init: function () { var obj = this; if (!this.EnableMenu) { this.MenuIconSize = 0; this.MenuTextSize = 0; } var header = this.Add({ _Position: "0px,0px,,0px,,40px", style: { zIndex: "1000" }, Elements: { CurrentUser: { _Position: "0px,9px", style: { lineHeight: "40px" } }, CurrentLogo: { _Position: "0px,,,9px", style: { lineHeight: "40px" } } }, Init: function() { if (obj.EnableMenu) { this.Add({ innerHTML: obj.HamburgerIcon ? obj.HamburgerIcon : window.Svg && Svg.Icons && Svg.Icons.Hamburger ? Svg.Icons.Hamburger : "", onclick: function() { // Toggle menu obj.ShowMenu = !obj.ShowMenu; obj.ProcessResize(); }, _Position: "5px,,,5px,30px,30px" }, "HamburgerMenu"); } } }, "Header"); var menu = null; if (this.EnableMenu) { var menu = this.Add({ _: "ul", style: { listStyle: "none", margin: "0px", padding: "0px", zIndex: "1000", display: "none" }, _Position: "40px,,0px,0px," + (this.MenuIconSize + this.MenuTextSize) + "px," }, "Menu"); } var content = { _: TK.Navigator, _Position: "40px,0px,0px," + (this.MenuIconSize + this.MenuTextSize) +"px", style: { overflow: "auto" }, Templates: {}, DefaultHash: this.DefaultContent, Navigate: function(page) { if (!menu) return; var menuItems = obj.Elements.Menu.Elements.ToArray(); for (var i = 0; i < menuItems.length;i++) menuItems[i].className = menuItems[i].className.replace("toolkitActive", ""); if (menu.Elements["MenuItem" + page]) menu.Elements["MenuItem" + page].className += " toolkitActive"; } }; if (!this.Embedded) { content._Position = null; document.body.style.paddingTop = "40px"; document.body.style.paddingLeft = (this.MenuIconSize + this.MenuTextSize) + "px"; content.style.overflow = ""; header.style.position = "fixed"; if (menu) menu.style.position = "fixed"; } for (var name in this.Content) { if (!this.Content[name].Hidden && menu) { var menuItem = menu.Add({ _: "li", style: { position: "relative", //width: (this.MenuIconSize + this.MenuTextSize) + "px", height: this.MenuIconSize + "px", lineHeight: this.MenuIconSize + "px", textAlign: "left", overflow: "hidden" }, title: this.Content[name].Name, Hash: name, onclick: function () { if (obj.ShowMenu) { obj.ShowMenu = !obj.ShowMenu; obj.ProcessResize(); } window.location.hash = "#" + this.Hash; }, Elements: {} }, "MenuItem" + name); if (this.Content[name].Icon) { menuItem.Add({ innerHTML: this.Content[name].Icon, style: { position: "absolute", top: "5px", left: "5px", width: (this.MenuIconSize - 10) + "px", height: (this.MenuIconSize - 10) + "px" } }, "Icon"); } menuItem.Add({ _: "span", style: { position: "absolute", top: "0px", left: (this.MenuIconSize+5)+"px", right: "0px", height: this.MenuIconSize + "px" }, innerHTML: this.Content[name].Name ? this.Content[name].Name : name }, "Title"); } if (this.Content[name].Content) { // Divided in subpages content.Templates[name] = { _: "div", className: "toolkitInterfaceSubPages", Templates: {}, MenuTemplate: { _: "ul", style: { position: !this.Embedded ? "fixed" : "absolute", zIndex: "990", }, Elements: {} }, Elements: { SubContent: { _: TK.Navigator, Templates: {}, NavigatorLevel: 1, DefaultHash: this.Content[name].DefaultContent ? this.Content[name].DefaultContent : this.DefaultContent, Navigate: function (page) { if (obj.SubMenu) { obj.SubMenu.Remove(); } obj.SubMenu = obj.Add(this.Parent.MenuTemplate, "SubMenu"); var menuItems = obj.SubMenu.Elements.ToArray(); for (var i = 0; i < menuItems.length; i++) menuItems[i].className = menuItems[i].className.replace("toolkitActive", ""); if (obj.SubMenu.Elements["MenuItem" + page]) obj.SubMenu.Elements["MenuItem" + page].className += " toolkitActive"; obj.ProcessResize(); }, Destroy: function () { this.Navigate = null; window.removeEventListener("hashchange", this.onHashChangeHandler); if (obj.SubMenu) obj.SubMenu.Remove(); obj.ProcessResize(); } } } }; for (var subName in this.Content[name].Content) { var contentObj = this.Content[name].Content[subName]; if (!contentObj.Hidden) { var menuItemTemplate = { _: "li", className: "toolkitActive", innerHTML: contentObj.Name ? contentObj.Name : subName, Hash: name + "/" + subName, onclick: function () { window.location.hash = "#" + this.Hash; }, Elements: { } }; if (contentObj.Icon) { menuItemTemplate.className += " menuButtonWithIcon"; menuItemTemplate.Elements.Icon = { innerHTML: contentObj.Icon }; } content.Templates[name].MenuTemplate.Elements["MenuItem" + subName] = menuItemTemplate; } content.Templates[name].Elements.SubContent.Templates[subName] = contentObj.Template; } } else { content.Templates[name] = this.Content[name].Template; } } this.Add(content, "Content"); if (this.CurrentUserTemplate) { header.Elements.CurrentUser.Add(this.CurrentUserTemplate, "User"); } if (this.CurrentLogoTemplate) { header.Elements.CurrentLogo.Add(this.CurrentLogoTemplate, "Logo"); } this.OnResizeListener = function () { obj.ProcessResize(); }; window.addEventListener("resize", this.OnResizeListener); setTimeout(function () { obj.ProcessResize(); },1); }, ProcessResize: function () { var obj = this; var w = (this.Embedded ? this.offsetWidth : document.body.offsetWidth); if (w < this.ResponsiveThreshold) { // Small view //this.Elements.Menu.style.width = (this.MenuIconSize + this.MenuTextSize) + "px"; if (this.Elements.Menu) { this.Elements.Menu.style.width = "100%"; this.Elements.Menu.style.display = this.ShowMenu ? "" : "none"; if (this.Elements.Menu.className.indexOf("toolkitDocked") >= 0) { this.Elements.Menu.className = this.Elements.Menu.className.replace(/toolkitDocked/g, ""); } this.Elements.Header.Elements.HamburgerMenu.style.display = ""; this.Elements.Header.Elements.HamburgerMenu.className = "Element-HamburgerMenu " + (this.ShowMenu ? "toolkitActive" : ""); this.Elements.Header.Elements.CurrentLogo.style.left = "43px"; if (this.Embedded) { this.Elements.Content.style.left = "0px"; } else { document.body.style.paddingLeft = "0px"; } } if (this.className.indexOf("toolkitSizeMedium") >= 0) this.className = this.className.replace(/toolkitSizeMedium/g, ""); if (this.className.indexOf("toolkitSizeSmall") < 0) this.className += " toolkitSizeSmall"; if (this.Embedded) { this.Elements.Content.style.top = this.Elements.Header.style.height; } else { document.body.style.paddingTop = this.Elements.Header.style.height; } if (this.Elements.SubMenu) { this.Elements.SubMenu.style.top = ""; this.Elements.SubMenu.style.bottom = "0px"; this.Elements.SubMenu.style.left = "0px"; this.Elements.SubMenu.style.right = "0px"; if (this.Embedded) { this.Elements.Content.style.bottom = "60pt"; } else { document.body.style.paddingBottom = "60pt"; } } else { if (this.Embedded) { this.Elements.Content.style.bottom = "0px"; } else { document.body.style.paddingBottom = "0px"; } } this.CurrentScrollElement = this.Embedded ? this.Elements.Content : window; this.CurrentScrollPosition = this.CurrentScrollElement.scrollY === undefined ? this.CurrentScrollElement.scrollTop : this.CurrentScrollElement.scrollY; if (!this.OnScrollListener) { var scrollUpCounter = 0; var scrollDownCounter = 0; this.OnScrollListener = function () { obj.ShowMenu = false; if (obj.Elements.Menu) { obj.Elements.Menu.style.display = "none"; obj.Elements.Header.Elements.HamburgerMenu.className = "Element-HamburgerMenu"; } if (obj.Embedded) { var newScroll = obj.CurrentScrollElement.scrollTop; var oldScroll = obj.CurrentScrollPosition; obj.CurrentScrollPosition = newScroll; if (newScroll + obj.CurrentScrollElement.offsetHeight + 50 > obj.CurrentScrollElement.scrollHeight) return; if (newScroll < oldScroll) { scrollUpCounter += oldScroll - newScroll; scrollDownCounter = 0; } else { scrollUpCounter = 0; scrollDownCounter += newScroll - oldScroll; } if (scrollDownCounter > 40 && newScroll > 50) { // Hide menu obj.Elements.Header.style.height = "0px"; obj.Elements.Content.style.top = "0px"; if (obj.Elements.Menu) obj.Elements.Menu.style.top = "0px"; } else if (scrollUpCounter > 40) { obj.Elements.Header.style.height = "40px"; obj.Elements.Content.style.top = "40px"; if (obj.Elements.Menu) obj.Elements.Menu.style.top = "40px"; } } else { var newScroll = obj.CurrentScrollElement.scrollY; var oldScroll = obj.CurrentScrollPosition; obj.CurrentScrollPosition = newScroll; if (newScroll + window.innerHeight + 50 > document.documentElement.scrollHeight) return; if (newScroll < oldScroll) { scrollUpCounter += oldScroll - newScroll; scrollDownCounter = 0; } else { scrollUpCounter = 0; scrollDownCounter += newScroll - oldScroll; } if (scrollDownCounter > 40 && newScroll > 50) { // Hide menu obj.Elements.Header.style.height = "0px"; } else if (scrollUpCounter > 40) { obj.Elements.Header.style.height = "40px"; } } }; this.CurrentScrollElement.addEventListener("scroll", this.OnScrollListener); } } else { this.ShowMenu = false; var paddingLeft = 0; if (this.className.indexOf("toolkitSizeSmall") >= 0) this.className = this.className.replace(/toolkitSizeSmall/g, ""); if (w < this.ResponsiveFullThreshold) { // Medium view paddingLeft = (this.MenuIconSize); if (this.className.indexOf("toolkitSizeMedium") < 0) this.className += " toolkitSizeMedium"; } else { // Big/default view if (this.className.indexOf("toolkitSizeMedium") >= 0) this.className = this.className.replace(/toolkitSizeMedium/g, ""); paddingLeft = (this.MenuIconSize + this.MenuTextSize); } if (this.Elements.Menu) { this.Elements.Menu.style.display = ""; this.Elements.Header.Elements.HamburgerMenu.style.display = "none"; this.Elements.Header.Elements.CurrentLogo.style.left = "9px"; if (w < this.ResponsiveFullThreshold) { // Medium view // Just show icons if (this.Elements.Menu.className.indexOf("toolkitDocked") < 0) this.Elements.Menu.className += " toolkitDocked"; } else { // Show icons and text if (this.Elements.Menu.className.indexOf("toolkitDocked") >= 0) this.Elements.Menu.className = this.Elements.Menu.className.replace(/toolkitDocked/g, ""); } if (this.Embedded) { this.Elements.Content.style.left = paddingLeft + "px"; this.Elements.Content.style.top = "40px"; } else { document.body.style.paddingLeft = paddingLeft + "px"; document.body.style.paddingTop = "40px"; } this.Elements.Header.style.height = "40px"; this.Elements.Menu.style.top = "40px"; this.Elements.Menu.style.width = paddingLeft + "px"; } else { paddingLeft = 0; } if (this.Elements.SubMenu) { this.Elements.SubMenu.style.bottom = ""; this.Elements.SubMenu.style.top = "40px"; this.Elements.SubMenu.style.left = paddingLeft + "px"; this.Elements.SubMenu.style.right = "0px"; if (this.Embedded) { this.Elements.Content.style.top = "80px"; } else { document.body.style.paddingTop = "80px"; } } if (this.Embedded) { this.Elements.Content.style.bottom = "0px"; } else { document.body.style.paddingBottom = "0px"; } if (this.OnScrollListener) { this.CurrentScrollElement.removeEventListener("scroll", this.OnScrollListener); this.OnScrollListener = null; } } }, Destroy: function () { window.removeEventListener("resize", this.OnResizeListener); if (this.OnScrollListener) { this.CurrentScrollElement.removeEventListener("scroll", this.OnScrollListener); } if (!this.Embedded) { document.body.style.paddingTop = ""; document.body.style.paddingLeft = ""; } }, Elements: {} };"use strict"; /* Minify Order(200) */ window.TK.Slider = { TextBefore: "", TextAfter: "", Min: 0, Max: 100, Width: 200, StepSize: 0, // 0 to disable ShowSteps: false, Data: 0, className: "toolkitSlider", readOnly: false, disabled: false, onchange: function () { }, Init: function () { if (this.DataSettings) { var fields = ["TextBefore", "TextAfter", "Min", "Max", "StepSize", "ShowSteps"]; for (var i = 0; i < fields.length; i++) { if (this.DataSettings[fields[i]] !== undefined) this[fields[i]] = this.DataSettings[fields[i]]; } } this.Elements.SliderContainer.Elements.Steps.style.display = this.ShowSteps && this.StepSize > 0 ? "" : "none"; if (this.StepSize > 0) { var stepCount = Math.floor((this.Max - this.Min) / this.StepSize) + 1; this.Elements.SliderContainer.Elements.Steps.Clear(); for (var i = 0; i < stepCount; i++) { var stepDataValue = this.Min + (this.StepSize * i); this.Elements.SliderContainer.Elements.Steps.Add({ style: { left: this.DataValueToPX(stepDataValue) + "px" }, className: stepDataValue <= this.Data ? "stepActive" : "stepInactive" }, "Step" + i); } } else { this.Elements.SliderContainer.Elements.Steps.Clear(); } this.Elements.TextBefore.innerText = this.TextBefore; this.Elements.TextBefore.style.display = this.TextBefore ? "" : "none"; this.Elements.TextAfter.innerText = this.TextAfter; this.Elements.TextAfter.style.display = this.TextAfter ? "" : "none"; this.Elements.SliderContainer.style.width = this.Width + "px"; var curPx = this.DataValueToPX(this.Data); this.Elements.SliderContainer.Elements.Indicator.style.left = curPx + "px"; this.Elements.SliderContainer.Elements.FillBar.style.width = curPx + "px"; }, DataValueToPX: function (dataValue) { return Math.round(((dataValue - this.Min) / (this.Max - this.Min)) * this.Width); }, PXToDataValue: function (px, roundStepSize) { if (roundStepSize) { var tmp = ((px / this.Width) * (this.Max - this.Min)); tmp += (roundStepSize / 2); tmp = (tmp - (tmp % roundStepSize)) + this.Min; if (tmp > this.Max) tmp = this.Max; return tmp; } return ((px / this.Width) * (this.Max - this.Min)) + this.Min; }, GetValue: function () { return this.Data; }, Elements: { TextBefore: {}, SliderContainer: { onmousdown: function (ev) { ev.preventDefault(); }, ontouchstart: function (e) { ev.preventDefault(); }, Elements: { Steps: {}, FillBar: {}, Indicator: { ontouchstart: function (e) { this.onmousedown(e.touches[0]); e.stopPropagation(); }, onmousedown: function (e) { var x, y; try { x = e.clientX; y = e.clientY; } catch (errie) { var e2 = window.event; x = e2.clientX; y = e2.clientY; } var startPos = this.Parent.Parent.DataValueToPX(this.Parent.Parent.Data); var obj = this; window.onmousemove = function (e) { var x2, y2; try { x2 = e.clientX; y2 = e.clientY; } catch (errie) { var e2 = window.event; x2 = e2.clientX; y2 = e2.clientY; } var newPos = startPos + (x2 - x); if (newPos < 0) newPos = 0; else if (newPos > obj.Parent.Parent.Width) newPos = obj.Parent.Parent.Width; // Jump to the nearest step and calculate data value based on new pos obj.Parent.Parent.Data = obj.Parent.Parent.PXToDataValue(newPos, obj.Parent.Parent.StepSize); // Redraw elements if (obj.Parent.Parent.onchange) obj.Parent.Parent.onchange(); obj.Parent.Parent.Init(); }; window.onmouseup = function () { window.onmousemove = null; window.onmouseup = null; window.onselectstart = null; window.ontouchmove = null; window.ontouchend = null; }; window.ontouchmove = function (e) { if (window.onmousemove) window.onmousemove(e.touches[0]); e.stopPropagation(); }; window.ontouchend = function (e) { if (window.onmouseup) window.onmouseup(); e.stopPropagation(); }; window.onselectstart = function () { return false; }; if (e && e.preventDefault) e.preventDefault(); else window.event.returnValue = false; }, } } }, TextAfter: {}, } }; if (window.TK.Form) { window.TK.Form.DefaultTemplates.slider = { _: TK.Slider }; } "use strict"; /* Minify Order(200) */ window.TK.Switch = { TextBefore: "", TextAfter: "", Data: false, className: "toolkitSwitch", onchange: function () { }, readOnly: false, disabled: false, onclick: function () { this.Toggle(); }, Toggle: function () { if (this.readOnly || this.disabled) return; this.Data = !this.Data; if (this.onchange) this.onchange(); this.Init(); }, Init: function () { if (this.DataSettings) { if (this.DataSettings.TextBefore !== undefined) this.TextBefore = this.DataSettings.TextBefore; if (this.DataSettings.TextAfter !== undefined) this.TextAfter = this.DataSettings.TextAfter; } this.className = this.className.replace(/toolkitSwitchActive/g, "").replace(/toolkitSwitchInactive/g, "").replace(/toolkitSwitchDisabled/g, "").replace(/toolkitSwitchReadOnly/g, "") + " " + (this.disabled ? "toolkitSwitchDisabled" : "") + " " + (this.readOnly ? "toolkitSwitchReadOnly" : "") + " " + (this.Data ? "toolkitSwitchActive" : "toolkitSwitchInactive"); this.Elements.TextBefore.innerText = this.TextBefore; this.Elements.TextBefore.style.display = this.TextBefore ? "" : "none"; this.Elements.TextAfter.innerText = this.TextAfter; this.Elements.TextAfter.style.display = this.TextAfter ? "" : "none"; }, GetValue: function () { return this.Data; }, Elements: { TextBefore: {}, SwitchContainer: { Elements: { Indicator: { _: "div" } } }, TextAfter: {}, } }; if (window.TK.Form) { window.TK.Form.DefaultTemplates.switch = { _: TK.Switch }; }