if (!window.console) console = {log: function() {}}; //create base namespace var Aston = {}; Aston.Log = function (message) { if(Aston.Log.Display) console.log(message); } Aston.Log.Display = true; Aston.LogException = function (exception) { try { console.log("ERROR: " + exception) if (typeof exception != "string") { for (var key in exception) console.log("\t" + key + " = " + exception[key]); } console.log("exception.lineNumber = " + exception.lineNumber); var serverDetails = Aston.ServerCombinedFiles.lastOrDefault(function(item) { return exception.lineNumber >= item.lineNumber; }); if(serverDetails != null) console.log(serverDetails.fileName + " line " + (exception.lineNumber - serverDetails.lineNumber)); console.log("Stack:"); var limitStack = 0; var caller = arguments.callee.caller; while ((caller != undefined) && (caller != null) && (limitStack++ < Aston.LogException.StackReportLimit)) { console.log("\t" + (String.IsNullOrEmpty(caller.name) ? "[unknown]" : caller.name)); caller = caller.caller; } } catch (ex) { // well shit ... } if(!Aston.LogException.SupressThrow) throw exception; } Aston.LogException.StackReportLimit = 2; Aston.LogException.SupressThrow = false; Aston.Namespace = function (namespaceName) { try { var namespace = window; namespaceName.split(".").foreach(function (namespaceNamePart) { if (namespace[namespaceNamePart] == undefined) namespace[namespaceNamePart] = {}; namespace = namespace[namespaceNamePart]; //namespace = (namespace[namespaceNamePart] = namespace[namespaceNamePart] || {}); }); return namespace; } catch (ex) { Aston.LogException(ex); } }; Aston.Enum = function (enumDefinition) { try { var result = {}; for (var propertyName in enumDefinition) { result[propertyName] = function () { var stringValue = propertyName; var intValue = enumDefinition[propertyName]; return { getName: function () { return stringValue; }, getValue: function () { return intValue; }, toString: function () { return stringValue; }, OR: function (enumItem) { return intValue | enumItem.getValue(); }, AND: function (enumItem) { return intValue & enumItem.getValue(); } }; }(); result[propertyName].prototype = Aston.Enum; } return result; } catch (ex) { Aston.LogException(ex); } }; Aston.Enum.ParseInt = function (enumeration, intValues) { try { var enumItems = []; intValue.foreach(function (intValue) { for (var enumItem in enumeration) { if (enumItem.getValue() == intValue) enumItems.push(enumItem); } }); return enumItems; } catch (ex) { Aston.LogException(ex); } } Aston.BrowserTypes = Aston.Enum({ Unknown: 0, Firefox: 1, Chrome: 2, Safari: 3, Opera: 4, MSIE: 5 }); Aston.BrowserType = function () { try { if ( navigator.userAgent.indexOf("MSIE") > -1 || navigator.userAgent.indexOf("Trident") > -1 ) { return Aston.BrowserTypes.MSIE; } if (navigator.userAgent.indexOf("Chrome") > -1) { return Aston.BrowserTypes.Chrome; } if (navigator.userAgent.indexOf("Firefox") > -1) { return Aston.BrowserTypes.Firefox; } if (navigator.userAgent.indexOf("Safari") > -1) { return Aston.BrowserTypes.Safari; } if (navigator.userAgent.indexOf("Opera") > -1) { return Aston.BrowserTypes.Opera; } return Aston.BrowserTypes.Unknown; } catch (ex) { Aston.LogException(ex); } }(); Aston.BrowserVersion = function () { try { if(navigator.userAgent.indexOf("MSIE") == -1 && navigator.userAgent.indexOf("Trident") > -1) return 11; // return the first sequence of numbers after the browser name // allow a single dot var matchBrowserVersion = new RegExp(Aston.BrowserType.toString() + "\\D+(\\d+(?:\\.\\d+)?)"); var matches = matchBrowserVersion.exec(navigator.userAgent); if(matches != undefined && matches != null && matches.length > 1) return parseFloat(matches[1]); return -1; // a default } catch (ex) { Aston.LogException(ex); } }(); Aston.MapClass = function (mapTo, mapFrom) { for (var propertyName in mapFrom) { if ( (mapFrom.hasOwnProperty(propertyName)) && (propertyName.indexOf("_" != 0)) // avoid mapping protected functions ) { mapTo[propertyName] = (function (scope, propertyName) { return function () { try { return scope[propertyName].apply(scope, arguments); } catch (ex) { Aston.LogException(ex); } } })(mapFrom, propertyName); } } }; Aston.Cookies = (function () { var constructor = function () { var that = this; this.getCookie = function (cookieName) { try { var cookieValue = String.Empty; var start = document.cookie.indexOf(cookieName + "="); if (start != -1) { start += cookieName.length + 1; var end = document.cookie.indexOf(";", start); if (end == -1) end = document.cookie.length; cookieValue = unescape(document.cookie.substring(start, end)); } return cookieValue; } catch (ex) { Aston.LogException(ex); } } this.setCookie = function (cookieName, cookieValue, daysTilExpiry, path) { try { daysTilExpiry = daysTilExpiry || 1; path = path || "/"; var expiryDate = new Date(); expiryDate.setDate(expiryDate.getDate() + daysTilExpiry); document.cookie = cookieName + "=" + escape(cookieValue) + "; expires=" + expiryDate.toGMTString() + "; path=" + path; } catch (ex) { Aston.LogException(ex); } } this.deleteCookie = function (cookieName) { try { that.setCookie(cookieName, String.Empty, -1); } catch (ex) { Aston.LogException(ex); } } }; return new constructor(); })(); // ********************************************************************************************* /* Standard object prototypes / extensions */ // ********************************************************************************************* // FUNCTION Function.prototype.Inherits = function (baseClass) { try { if (baseClass != undefined) { var args = []; for (var i = 1; i < arguments.length; i++) args.add(arguments[i]); function BaseClass() { return baseClass.apply(this, args); } BaseClass.prototype = baseClass.prototype; this.prototype = new BaseClass(); this.prototype.constructor = this; } } catch (ex) { Aston.LogException(ex); } }; // ARRAY // IE8 has no indexOf - wtf IE // see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf // see http://stackoverflow.com/questions/3629183/why-doesnt-indexof-work-on-an-array-ie8 if (!Array.prototype.indexOf) { Array.prototype.indexOf = function(elt /*, from*/) { var len = this.length >>> 0; var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) from += len; for (; from < len; from++) { if (from in this && this[from] === elt) return from; } return -1; }; } Array.prototype.foreach = function (method, array) { try { array = array || this; for (var i = 0; i < array.length; i++) { //var value = method.call(Array.prototype.foreach.caller, array[i]); var value = method(array[i]); if (value != undefined) return value; } } catch (ex) { Aston.LogException(ex); } }; Array.prototype.contains = function (element) { try { return this.indexOf(element) >= 0; } catch (ex) { Aston.LogException(ex); } }; Array.prototype.add = function (element) { try { this.push(element); } catch (ex) { Aston.LogException(ex); } }; Array.prototype.insert = function (element, index) { try { this.splice(index, 0, element); } catch (ex) { Aston.LogException(ex); } }; Array.prototype.remove = function (element) { try { var index = this.indexOf(element); if (index >= 0) { this.splice(index, 1); return true; } return false; } catch (ex) { Aston.LogException(ex); } }; Array.prototype.removeAt = function (index) { try { var temp = this[index]; this.splice(index, 1); return temp; } catch (ex) { Aston.LogException(ex); } }; Array.prototype.clear = function () { try { this.length = 0; } catch (ex) { Aston.LogException(ex); } }; Array.prototype.clone = function () { try { return this.slice(0); } catch (ex) { Aston.LogException(ex); } }; Array.prototype.addRange = function (elements) { try { var that = this; elements.foreach(function(element) { that.add(element); }); } catch (ex) { Aston.LogException(ex); } }; Array.prototype.removeRange = function (elements) { try { for(var i = elements.length - 1; i >= 0; i--) { this.remove(elements[i]); } } catch (ex) { Aston.LogException(ex); } }; Array.prototype.count = function() { return this.length; } // ARRAY - LINQ Array.prototype.firstOrDefault = function (selector) { try { var item = this.foreach(function (item) { if (selector(item)) return item; }); return item == undefined ? null : item; } catch (ex) { Aston.LogException(ex); } }; Array.prototype.lastOrDefault = function (selector) { try { for (var i = this.length - 1; i >= 0; i--) { if (selector(this[i])) return this[i]; } return null; } catch (ex) { Aston.LogException(ex); } }; Array.prototype.where = function (selector) { try { var results = [] this.foreach(function (item) { if (selector(item)) results.add(item); }); return results; } catch (ex) { Aston.LogException(ex); } }; Array.prototype.select = function (selector) { try { var result = []; this.foreach(function (item) { result.add(selector(item)); }); return result; } catch (ex) { Aston.LogException(ex); } }; Array.prototype.any = function (selector) { try { var result = this.foreach(function (item) { if (selector(item)) return true; }); return result == undefined ? false : result; } catch (ex) { Aston.LogException(ex); } }; Array.prototype.all = function (selector) { try { var result = this.foreach(function (item) { if (!selector(item)) return false; }); return result == undefined ? true : result; } catch (ex) { Aston.LogException(ex); } }; Array.prototype.selectMany = function (selector) { try { var results = []; this.foreach(function(item) { results.addRange(selector(item)); }); return results; } catch (ex) { Aston.LogException(ex); } } // NUMBER Number.ParseNumber = function (value) { try { // NOTE: "0" == "", see http://stackoverflow.com/questions/462663/implied-string-comparison-0-but-1-1 if (value = "0") return 0; if((value == undefined) || (String.IsNullOrEmpty(value))) return NaN; var characters = value.toString().split(''); characters.foreach(function (character) { if (isNaN(parseInt(character))) return NaN; }); return parseInt(value); } catch (ex) { Aston.LogException(ex); } }; // STRING String.Empty = ""; String.IsNullOrEmpty = function (string) { return ((string == null) || (string == String.Empty)); }; String.prototype.endsWith = function (suffix) { return this.indexOf(suffix, this.length - suffix.length) !== -1; }; // DATE // getMonthName and getDayName (function () { var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; Date.prototype.getMonthName = function () { return months[this.getMonth()]; }; Date.prototype.getDayName = function () { return days[this.getDay()]; }; })(); // partly from http://stackoverflow.com/questions/13627308/add-st-nd-rd-and-th-suffix-to-a-number Date.prototype.getDateWithSupertextSuffix = function() { if ((this.getDate() % 10 == 1) && (this.getDate() != 11)) return this.getDate() + "st"; if ((this.getDate() % 10 == 2) && (this.getDate() != 12)) return this.getDate() + "nd"; if ((this.getDate() % 10 == 3) && (this.getDate() != 13)) return this.getDate() + "rd"; return this.getDate() + "th"; }; Date.prototype.addMilliseconds = function (milliseconds) { return new Date(this.valueOf() + (milliseconds)); }; Date.prototype.addSeconds = function (seconds) { return new Date(this.valueOf() + (seconds * 1000)); }; Date.prototype.addMinutes = function (minutes) { return new Date(this.valueOf() + (minutes * 60 * 1000)); }; Date.prototype.addHours = function (hours) { return new Date(this.valueOf() + (hours * 60 * 60 * 1000)); }; Date.prototype.addDays = function (days) { return new Date(this.valueOf() + (days * 24 * 60 * 60 * 1000)); }; Date.prototype.addMonths = function (months) { var result = new Date(this.valueOf()); result.setMonth(result.getMonth() + months); return result; }; Date.prototype.addYears = function (years) { var result = new Date(this.valueOf()); result.setFullYear(result.getFullYear() + years); return result; }; // ********************************************************************************************* /* CORE METHODS */ // ********************************************************************************************* Aston.Namespace("Aston.Core"); Aston.Core.getFunction = function (_function) { // probably a better check available // https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/String // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function if (_function == null) return null; // assume string else if (_function.constructor === String) return new Function(_function) else if (typeof(_function) == "function") // assume function return _function; return null; } Aston.Core.funcIf = function (_if, _then, _else) { if (_if) _then(); else if (_else instanceof Function) _else(); }; Aston.Core.blockCode = function () { for(var i = 0; i < arguments.length; i++) { var toRun = Aston.Core.getFunction(arguments[i]); if (toRun != null) toRun(); // arguments seems to include the THIS when a method is called //else //Aston.LogException("Block Code: i = " + i + ", toRun = " + toRun + " is not a function"); //Aston.Log("Block Code: i = " + i + ", toRun = " + toRun + " is not a function"); } }; // added to support c# => JS conversion with an empty expression Aston.Core.doNothing = function () { }; Aston.Core.asComposite = function (typeName, data) { //return { type : typeName, data : JSON.stringify(data) }; // no need to inner serialise this any more return { Type: typeName, Data: data }; } Aston.Core.newGuid = function () { //from: http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0; var v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } Aston.Core.equalTo = function (object1, object2) { //TODO: from IE9 var keys1 = Object.keys(object1); var keys2 = Object.keys(object2); if (keys1.length != keys2.length) return false; for (var i = 0; i < keys1.length; i++) { if (keys1[i] != keys2[i]) return false; if (object1[keys1[i]] != object2[keys2[i]]) return false; } return true; } Aston.Core.async = function (asyncReturn, continueMethod) { try { //Aston.Log("asyncMethod = " + asyncMethod); //Aston.Log(asyncMethod); //var asyncReturn = asyncMethod(); //asyncReturn.setContinueMethod(continueMethod); asyncReturn.setContinueMethod(continueMethod); } catch (ex) { Aston.LogException(ex); } } // ********************************************************************************************* /* CORE CLASSES */ // ********************************************************************************************* Aston.Core.Event = function () { // INNER CLASSES function ExecutionState(delegates, context, _arguments) { //FIELDS var that = this; var index = 0; var isPaused = false; // PUBLIC METHODS this.fire = function() { try { while((!isPaused) && (index < delegates.length)) { delegates[index].apply(context, _arguments); index++; } } catch (ex) { Aston.LogException(ex); } }; this.pause = function(includeCurrentDelegate) { if(includeCurrentDelegate) index--; isPaused = true; return function() { isPaused = false; that.fire(); }; }; this.isPaused = function() { return isPaused; }; } // FIELDS var delegates = []; var executionState = null // PUBIC METHODS this.add = function (method) { try { if (method != null) delegates.add(method); } catch (ex) { Aston.LogException(ex); } }; // if index is not supplied an index of 0 is assumed this.insert = function (method, index) { try { if (method != null) { index = index || 0; delegates.insert(method, index); } } catch (ex) { Aston.LogException(ex); } }; this.remove = function (method) { try { if (method != null) delegates.remove(method); } catch (ex) { Aston.LogException(ex); } }; this.count = function () { return delegates.length; }; this.clear = function () { delegates.clear(); }; // executes all delegates attached to this event this.fire = function () { try { if (delegates.length == 0) return; // if a running delegate removes itself from the list the itterator in foreach will skip over the next delegate // (or clears the event list!) // so take a copy of the delegates to execute first executionState = new ExecutionState(delegates.slice(0), this, arguments); executionState.fire(); executionState = null; } catch (ex) { Aston.LogException(ex); } }; // returns a clone of this event at the point of pausing // the cloned event included the event that called pause // cloned delegates use their original context and arguments // one the cloned event has completed it is cleared ro prevent further use // TODO: make the cloned event readonly this.pause = function (includeCurrentDelegate) { try { if(executionState == null) return null; //throw new Error("Event is not firing. Cannot pause"); includeCurrentDelegate = includeCurrentDelegate || false; return executionState.pause(includeCurrentDelegate); } catch (ex) { Aston.LogException(ex); } }; this.isExecuting = function() { try { if(executionState == null) return false else return !executionState.isPaused(); } catch (ex) { Aston.LogException(ex); } }; }; Aston.Core.AsyncReturn = function () { // allow these to be set in any order, in case a func return immediately (is not async) var returnValue = null; var continueMethod = null; this.setContinueMethod = function(method) { continueMethod = method; if (returnValue != null) { continueMethod(returnValue); reset(); } }; this.setValue = function (value) { returnValue = value; if (continueMethod != null) { continueMethod(returnValue); reset(); } }; var reset = function () { // allow for future use returnValue = null; continueMethod = null; }; }; // ********************************************************************************************* /* Author: ------- James Hall - www.astonprograms.co.uk Description: ------------ Uses: ----- Aston.Core.js Methods: -------- TODO: ------------ */ // ********************************************************************************************* /* TODO: Potentially force the use to remove the node from one parent before adding to another. */ Aston.Namespace("Aston.Dom"); // ENUMERATIONS Aston.Dom.Style = Aston.Enum( { marginLeft: Math.pow(2,0), marginRight: Math.pow(2,1), marginTop: Math.pow(2,2), marginBottom: Math.pow(2,3), paddingLeft: Math.pow(2,4), paddingRight: Math.pow(2,5), paddingTop: Math.pow(2,6), paddingBottom: Math.pow(2,7), borderLeftWidth: Math.pow(2,8), borderRightWidth: Math.pow(2,9), borderTopWidth: Math.pow(2,10), borderBottomWidth: Math.pow(2,11), display: Math.pow(2, 12), position: Math.pow(2, 13), width: Math.pow(2, 14), height: Math.pow(2, 15), overflowY: Math.pow(2, 16), overflowX: Math.pow(2, 17) }); // CONSTANTS Aston.Dom.IgnoreMargins = true; // STATIC METHODS Aston.Dom.PreventDefault = function(event) { try { if(Aston.BrowserType == Aston.BrowserTypes.MSIE && Aston.BrowserVersion < 11) event.returnValue = false; else event.preventDefault(); } catch (ex) { Aston.LogException(ex); }; }; Aston.Dom.StopPropagation = function(event) { try { if(Aston.BrowserType == Aston.BrowserTypes.MSIE && Aston.BrowserVersion < 11) window.event.cancelBubble = true; else event.stopPropagation(); } catch (ex) { Aston.LogException(ex); }; }; Aston.Dom.GetMousePosition = function(event) { try { return { x: event.clientX, y: event.clientY}; } catch (ex) { Aston.LogException(ex); }; }; Aston.Dom.GetPosition = function(node, fromParent) { try { fromParent = fromParent || false; var point = { x : 0, y: 0 }; var element = node; while(element != null) { point.x += element.offsetLeft; point.y += element.offsetTop; if(fromParent) element = null; else element = element.offsetParent; } return point; } catch (ex) { Aston.LogException(ex); }; }; Aston.Dom.GetDimensions = function(node, ignoreMargins) { try { if((ignoreMargins) && (ignoreMargins != null)) ignoreMargins = false; var dimensions = { width : node.offsetWidth, height : node.offsetHeight }; if(!ignoreMargins) { if(dimensions.width > 0) { dimensions.width += Aston.Dom.SumStyleValues(node, [ Aston.Dom.Style.marginLeft, Aston.Dom.Style.marginRight ]); } if(dimensions.height > 0) { dimensions.height += Aston.Dom.SumStyleValues(node, [ Aston.Dom.Style.marginTop, Aston.Dom.Style.marginBottom ]); } } return dimensions; } catch (ex) { Aston.LogException(ex); }; }; Aston.Dom.GetInnerDimensions = function(node) { try { var dimensions = { width : node.clientWidth, height : node.clientHeight }; if(dimensions.width >= 0) { dimensions.width -= Aston.Dom.SumStyleValues(node, [ Aston.Dom.Style.paddingLeft, Aston.Dom.Style.paddingRight, Aston.Dom.Style.borderLeftWidth, Aston.Dom.Style.borderRightWidth ]); } if(dimensions.height >= 0) { dimensions.height -= Aston.Dom.SumStyleValues(node, [ Aston.Dom.Style.paddingTop, Aston.Dom.Style.paddingBottom, Aston.Dom.Style.borderTopWidth, Aston.Dom.Style.borderBottomWidth ]); } return dimensions; } catch (ex) { Aston.LogException(ex); }; }; Aston.Dom.GetStyleValue = function(node, style) // expects a Aston.Dom.Style { try { var computedStyle; if (window.getComputedStyle) computedStyle = window.getComputedStyle(node, null); // TODO: no pseudo element here so will not match :hover else computedStyle = node.currentStyle; return computedStyle[style.toString()]; } catch (ex) { Aston.LogException(ex); }; }; Aston.Dom.SumStyleValues = function(node, styles) // expects an array of Aston.Dom.Style { try { var result = 0; var computedStyle; if (window.getComputedStyle) computedStyle = window.getComputedStyle(node, null); // TODO: no pseudo element here so will not match :hover else computedStyle = node.currentStyle; styles.foreach(function(style) { var value = parseInt(computedStyle[style.toString()]); if(isNaN(value)) value = 0; result += value; }); return result; } catch (ex) { Aston.LogException(ex); }; }; Aston.Dom.SetRemainingHeight = function (element) { //TODO: Investigate this! if(element.parentNode == null) return; // absolutely positioned elements that has css height 100% won't calculate propertly var parentHeight; if((element.parentNode.style.position == "absolute") && (element.parentNode.parentNode != null)) parentHeight = Aston.Dom.GetInnerDimensions(element.parentNode.parentNode).height; else parentHeight = Aston.Dom.GetInnerDimensions(element.parentNode).height; var childHeights = 0; for (var i = 0; i < element.parentNode.childNodes.length; i++) { if (element.parentNode.childNodes[i] != element) { var childHeight = Aston.Dom.GetDimensions(element.parentNode.childNodes[i]).height; if ( (!isNaN(childHeight)) && (childHeight > 0) && // nothing to do here anyway (Aston.Dom.GetStyleValue(element.parentNode.childNodes[i], Aston.Dom.Style.position) != "absolute") && (element.offsetTop != element.parentNode.childNodes[i].offsetTop) //TODO: rough hack to determine collision ) { childHeights += childHeight; } } } element.style.height = (parentHeight - childHeights) + "px"; } Aston.Dom.GetWindowDimensions = function() { return { width : (window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth), height : (window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight) }; } // ********************************************************************************************* /* Author: ------- James Hall - www.astonprograms.co.uk Description: ------------ Uses: ----- Aston.Core.js Aston.Dom.js Aston.Dom.Element.js Methods: -------- TODO: ------------ */ // ********************************************************************************************* Aston.Namespace("Aston.Dom"); // Text nodes, for example, are not elements so this is the base for everything Aston.Dom.Node = function(node) { //Allow inheritance without a node as the single inherited base class. if(node == undefined) return; // FIELDS var that = this; var parent = null; // Another Aston.Dom.Element var addedToDomNode = null; // The DOM node (not an Aston.Dom.Element) that this is added too, // cannot have both parent and this set, /// used for root Aston.Dom.Element nodes var addedToDomEvent = new Aston.Core.Event(); //bubbles down // PROTECTED METHODS this._getNode = function() { return node; }; this._setParent = function(parentNode) { parent = parentNode; }; this._fireAddedToDom = function() // node should only be passes by this funciton recursing { // we cannot gautentee that the node Has actually been added, not at the DOM end addedToDomEvent.fire(); addedToDomEvent.clear(); if(that.getChildNodes) { that.getChildNodes().foreach(function(child) { child._fireAddedToDom(); }); } }; // PUBLIC METHODS this.getParent = function() { return parent; }; this.addedToDom = function(method, onCapture) { return method == undefined ? addedToDomEvent : addedToDomEvent.add(method, onCapture); }; // Also removes this from the Aston.Dom hierarchy (if present) this.addToDom = function(parentElement, referenceElement) { try { if((parentElement == undefined) || (parentElement == null)) throw new Error("parentElement was null or undefined"); if ( (addedToDomNode == null) || ( (addedToDomNode != parentElement) && //TODO: this needs work, can be added to current parent if it's to be repositioned (parent.getChildNodes().item(parent.getChildNodes().count() - 1) != that) ) ) { if(parent != null) parent.getChildNodes.remove(that); if((referenceElement == undefined) || (referenceElement == null)) parentElement.appendChild(node); else parentElement.insertBefore(node, referenceElement); addedToDomNode = parentElement; // fire events that._fireAddedToDom(that); } } catch (ex) { Aston.LogException(ex); }; }; // Also removes this from the Aston.Dom hierarchy (if present) this.removeFromDom = function() { try { if(parent != null) parent.getChildNodes().remove(that); if(addedToDomNode != null) { addedToDomNode.removeChild(node); addedToDomNode = null; } } catch (ex) { Aston.LogException(ex); }; }; this.isAddedToDom = function() { try { if(addedToDomNode != null) return true; else if(parent != null) return parent.isAddedToDom(); return false; } catch (ex) { Aston.LogException(ex); }; } this.nextSibling = function() { try { if(parent != null) { var thisIndex = parent.getChildNodes().indexOf(that); if(thisIndex < parent.getChildNodes().count() - 1) return parent.getChildNodes().item(thisIndex + 1); else return null; } else return null; } catch (ex) { Aston.LogException(ex); }; }; this.previousSibling = function() { try { if(parent != null) { var thisIndex = parent.getChildNodes().indexOf(that); if(thisIndex > 0) return parent.getChildNodes().item(thisIndex - 1); else return null; } else return null; } catch (ex) { Aston.LogException(ex); }; }; this.isEventTarget = function(event) { return event.target == node; }; this.handlesNode = function(domNode) { return node == domNode; }; }; // ********************************************************************************************* /* Author: ------- James Hall - www.astonprograms.co.uk Description: ------------ Uses: ----- Aston.Core.js Aston.Dom.js Methods: -------- TODO: ------------ */ // ********************************************************************************************* Aston.Namespace("Aston.Dom"); /* TODO: Potentially force the use to remove the node from one parent before adding to another. */ Aston.Dom.Element = function (node) { //Allow inheritance without a node as the single inherited base class. if (node == undefined) return; Aston.Dom.Node.call(this, node); // INNER CLASSES var ChildNodeCollection = function () { var childNodes = new Array(); this.count = function () { return childNodes.length; }; this.item = function (index) { try { if ((index < 0) || (index >= childNodes.length)) return null; return childNodes[index]; } catch (ex) { Aston.LogException(ex); } }; this.add = function (element) { try { this.insert(element); } catch (ex) { Aston.LogException(ex); } }; this.insert = function (element, index) { try { if (!(element instanceof Aston.Dom.Node)) throw new Error("insert: Object not type of Aston.Dom.Node\n" + element); if ((index == undefined) || (index == null)) index = childNodes.length; //set parent var oldParent = element.getParent(); if (oldParent != null) oldParent.getChildNodes().remove(element); element._setParent(that); // set Dom if (index < childNodes.length) node.insertBefore(element._getNode(), childNodes[index]._getNode()); else node.appendChild(element._getNode()); childNodes.insert(element, index); // fire events // if parent is added to dom already then the child is too if (that.isAddedToDom()) { //Aston.Log("child nodes - insert - that.isAddedToDom " + that.isAddedToDom() + " that nodeName " + that._getNode().nodeName + " add add node name = " + element._getNode().nodeName); element._fireAddedToDom(); } childrenAddedEvent.fire(); if (that.getParent() != null) that.getParent().childrenAdded().fire(); } catch (ex) { Aston.LogException(ex); } }; this.remove = function (element) { try { if (!(element instanceof Aston.Dom.Node)) throw new Error("remove: Object not type of Aston.Dom.Node\n" + element); if (childNodes.remove(element)) { //set parent element._setParent(null); // set Dom if (element._getNode().parentNode !== null) node.removeChild(element._getNode()); // Stop altering heights Aston.Dom.RemainingHeightElements.remove(element._getNode()); } } catch (ex) { Aston.LogException(ex); } }; this.removeAt = function (index) { try { var element = childNodes.removeAt(index); //set parent element._setParent(null); // set Dom if (element._getNode().parentNode !== null) node.removeChild(element._getNode()); // Stop altering heights Aston.Dom.RemainingHeightElements.remove(element._getNode()); } catch (ex) { Aston.LogException(ex); } }; this.indexOf = function (element) { try { if (!(element instanceof Aston.Dom.Node)) throw new Error("indexOf: Object not type of Aston.Dom.Node\n" + element); return childNodes.indexOf(element); } catch (ex) { Aston.LogException(ex); } }; this.contains = function (element) { try { if (!(element instanceof Aston.Dom.Node)) throw new Error("contains: Object not type of Aston.Dom.Node\n" + element); return childNodes.contains(element) } catch (ex) { Aston.LogException(ex); } } this.foreach = function (method) { return childNodes.foreach(method); }; this.clear = function () { childNodes.foreach(function (element) { //set parent element._setParent(null); // set Dom if (element._getNode().parentNode !== null) node.removeChild(element._getNode()); }); childNodes = new Array(); } this.firstOrDefault = function (selector) { return childNodes.firstOrDefault(selector); }; this.where = function (selector) { return childNodes.where(selector); }; this.select = function (selector) { return childNodes.select(selector); } this.any = function (selector) { return childNodes.any(selector); } } function ClassCollection(element) { var classes = []; this.add = function (className) { // prevent adding the same class twice if (!classes.contains(className)) { classes.add(className); element.className = classes.join(" "); } }; this.remove = function (className) { classes.remove(className); element.className = classes.join(" "); }; this.clear = function () { classes.clear(); element.className = String.Empty; }; } // FIELDS var that = this //var node = this._getNode(); // using constructor paramter instead var childNodes = new ChildNodeCollection(); var classes = new ClassCollection(node); var isHidden = false; var isFakeHidden = false; var childrenAddedEvent = new Aston.Core.Event(); //bubbles up var click = new Aston.Core.Event(); var clickHold = new Aston.Core.Event(); var clickHoldTimeoutID = null; var clickIsHeld = false; var doubleClick = new Aston.Core.Event(); var lastClick = null; var doubleClicked = false; var scroll = new Aston.Dom.Event(node, "scroll"); var mouseOver = new Aston.Dom.Event(node, "mouseover"); var mouseOut = new Aston.Dom.Event(node, "mouseout"); var mouseEnter = new Aston.Dom.Event(node, "mouseenter"); var mouseLeave = new Aston.Dom.Event(node, "mouseleave"); var mouseMove = new Aston.Dom.Event(node, "mousemove"); var mouseDown = new Aston.Dom.Event(node, "mousedown"); var mouseUp = new Aston.Dom.Event(node, "mouseup"); var dragEnter = new Aston.Dom.Event(node, "dragenter"); var dragOver = new Aston.Dom.Event(node, "dragover"); var drop = new Aston.Dom.Event(node, "drop"); // NOTE: focus and blur only apply to inputs, see Aston.Dom.Nodes // PUBLIC METHODS // simple getters and setter will NOT have try catch blocks this.getChildNodes = function () { return childNodes; }; this.getID = function () { return node.id; }; this.setID = function (id) { node.id = id; }; this.getClasses = function () { return classes; }; this.getInnerHTML = function () { return node.innerHTML; }; this.setInnerHTML = function (innerHTML) { node.innerHTML = innerHTML; }; this.getInnerText = function () { return node.innerText ; }; this.setInnerText = function (innerText ) { node.innerText = innerText ; }; // - STYLING // TODO: consider moving into a styling object this.getZIndex = function () { return node.style.zIndex; }; this.setZIndex = function (zIndex) { node.style.zIndex = zIndex; }; this.getTitle = function () { return node.title; }; this.setTitle = function (title) { node.title = title; }; this.show = function () { try { if (!isHidden) return; if (isFakeHidden) { node.style.position = String.Empty; node.style.top = String.Empty; isFakeHidden = false; } node.style.display = String.Empty; isHidden = false; } catch (ex) { Aston.LogException(ex); }; }; this.hide = function (fakeHide) { try { if (isHidden) return; fakeHide = fakeHide || false; if (fakeHide) { node.style.position = "absolute"; node.style.top = "-9999px"; isFakeHidden = true; } else { node.style.display = "none"; } isHidden = true; } catch (ex) { Aston.LogException(ex); }; }; this.isHidden = function () { return isHidden; }; this.getScrollPosition = function () { return { x: node.scrollLeft, y: node.scrollTop }; }; this.setScrollPosition = function (position) { try { node.scrollLeft = position.x; node.scrollTop = position.y; } catch (ex) { Aston.LogException(ex); }; }; this.getScrollDimensions = function () { return { width: node.scrollWidth, height: node.scrollHeight }; }; this.getPosition = function (fromParent) { try { return Aston.Dom.GetPosition(node, fromParent); } catch (ex) { Aston.LogException(ex); }; }; this.setPosition = function (position) { try { if (!isNaN(position.x)) node.style.left = position.x + "px"; else node.style.left = position.x; if (!isNaN(position.y)) node.style.top = position.y + "px"; else node.style.top = position.y; } catch (ex) { Aston.LogException(ex); }; }; // total dimensions including margins, border and padding this.getDimensions = function (ignoreMargins) { try { return Aston.Dom.GetDimensions(node, ignoreMargins); } catch (ex) { Aston.LogException(ex); }; }; this.setDimensions = function (dimensions) { try { if (!isNaN(dimensions.width)) { dimensions.width -= Aston.Dom.SumStyleValues(node, [ Aston.Dom.Style.marginLeft, Aston.Dom.Style.marginRight, Aston.Dom.Style.borderLeftWidth, Aston.Dom.Style.borderRightWidth ]); node.style.width = dimensions.width + "px"; } else { node.style.width = dimensions.width; } if (!isNaN(dimensions.height)) { dimensions.height -= Aston.Dom.SumStyleValues(node, [ Aston.Dom.Style.marginTop, Aston.Dom.Style.marginBottom, Aston.Dom.Style.borderTopWidth, Aston.Dom.Style.borderBottomWidth ]); node.style.height = dimensions.height + "px"; } else { node.style.height = dimensions.height; } } catch (ex) { Aston.LogException(ex); }; }; // the dimensions of the content excluding margins, borders and padding this.getInnerDimensions = function () { try { return Aston.Dom.GetInnerDimensions(node); } catch (ex) { Aston.LogException(ex); }; }; this.setInnerDimensions = function (dimensions) { try { if (dimensions.width != undefined) { if (!isNaN(dimensions.width)) { dimensions.width += Aston.Dom.SumStyleValues(node, [ Aston.Dom.Style.paddingLeft, Aston.Dom.Style.paddingRight, Aston.Dom.Style.borderLeftWidth, Aston.Dom.Style.borderRightWidth ]); node.style.width = dimensions.width + "px"; } else { node.style.width = dimensions.width; } } if (dimensions.height != undefined) { if (!isNaN(dimensions.height)) { dimensions.height += Aston.Dom.SumStyleValues(node, [ Aston.Dom.Style.paddingTop, Aston.Dom.Style.paddingBottom, Aston.Dom.Style.borderTopWidth, Aston.Dom.Style.borderBottomWidth ]); node.style.height = dimensions.height + "px"; } else { node.style.height = dimensions.height; } } } catch (ex) { Aston.LogException(ex); }; }; this.getOpacity = function () { throw new Error("not yet implemented") }; this.setOpacity = function (opacity) { try { if (Aston.BrowserType == Aston.BrowserTypes.MSIE && Aston.BrowserVersion < 11) { if (opacity < 100) node.style.filter = "alpha(opacity=" + opacity + ")"; else node.style.filter = String.Empty; } else { node.style.opacity = (opacity / 100); } } catch (ex) { Aston.LogException(ex); }; }; this.getBackgroundColour = function () { return node.style.backgroundColor; }; this.setBackgroundColour = function (bgColour) { node.style.backgroundColor = bgColour; }; this.getForegroundColour = function () { return node.style.color; }; this.setForegroundColour = function (fgColour) { node.style.color = fgColour; }; // - EVENTS // The DomEvent methods should catch errors this.click = function (method, onCapture) { return method == undefined ? click : click.add(method, onCapture); }; this.clickHold = function (method, onCapture) { return method == undefined ? clickHold : clickHold.add(method, onCapture); }; this.doubleClick = function (method, onCapture) { return method == undefined ? doubleClick : doubleClick.add(method, onCapture); }; this.mouseOver = function (method, onCapture) { return method == undefined ? mouseOver : mouseOver.add(method, onCapture); } this.mouseOut = function (method, onCapture) { return method == undefined ? mouseOut : mouseOut.add(method, onCapture); }; this.mouseEnter = function (method, onCapture) { return method == undefined ? mouseEnter : mouseEnter.add(method, onCapture); }; this.mouseLeave = function (method, onCapture) { return method == undefined ? mouseLeave : mouseLeave.add(method, onCapture); }; this.mouseMove = function (method, onCapture) { return method == undefined ? mouseMove : mouseMove.add(method, onCapture); }; this.mouseDown = function (method, onCapture) { return method == undefined ? mouseDown : mouseDown.add(method, onCapture); }; this.mouseUp = function (method, onCapture) { return method == undefined ? mouseUp : mouseUp.add(method, onCapture); }; this.scroll = function (method, onCapture) { return method == undefined ? scroll : scroll.add(method, onCapture); }; this.childrenAdded = function (method, onCapture) { return method == undefined ? childrenAddedEvent : childrenAddedEvent.add(method, onCapture); }; this.dragEnter = function (method, onCapture) { return method == undefined ? dragEnter : dragEnter.add(method, onCapture); }; this.dragOver = function (method, onCapture) { return method == undefined ? dragOver : dragOver.add(method, onCapture); }; this.drop = function (method, onCapture) { return method == undefined ? drop : drop.add(method, onCapture); }; // CONSTRUCTOR try { mouseDown.add(function (event) { // Click and Hold if(clickHold.count() > 0) { clickHoldTimeoutID = setTimeout(function () { clickHold.fire(event); clickIsHeld = true; }, Aston.Dom.Element.Defaults.clickHoldTime); } // Double Click if(doubleClick.count() > 0) { var now = new Date().getTime(); if ( (lastClick != null) && (now - lastClick <= Aston.Dom.Element.Defaults.doubleClickTime) ) { doubleClick.fire(event); doubleClicked = true; } lastClick = now; } }); mouseUp.add(function (event) { if (clickHoldTimeoutID != null) clearTimeout(clickHoldTimeoutID); if (!clickIsHeld && !doubleClicked) click.fire(event); clickIsHeld = false; doubleClicked = false; }); } catch (ex) { Aston.LogException(ex); }; }; Aston.Dom.Element.Inherits(Aston.Dom.Node); Aston.Dom.Element.Defaults = { clickHoldTime : 550, doubleClickTime : 500 }; // ********************************************************************************************* /* Author: ------- James Hall - www.astonprograms.co.uk Description: ------------ Uses: ----- Aston.Core.js Aston.Dom.js Methods: -------- TODO: ------------ */ // ********************************************************************************************* Aston.Namespace("Aston.Dom"); //TODO: eventName should be an enum Aston.Dom.Event = function(node, eventName) { var count = 0; var events = []; var that = this; this.add = function(method, onCapture) { try { onCapture = ((onCapture) && (onCapture != null)) ? onCapture : true; if(Aston.BrowserType == Aston.BrowserTypes.MSIE && Aston.BrowserVersion < 11) node.attachEvent("on" + eventName, method, onCapture); else node.addEventListener(eventName, method, onCapture); count++; events.add({ method : method, onCapture : onCapture}); } catch (ex) { Aston.LogException(ex); } }; this.remove = function(method, onCapture) { try { onCapture = ((onCapture) && (onCapture != null)) ? onCapture : true; if(Aston.BrowserType == Aston.BrowserTypes.MSIE && Aston.BrowserVersion < 11) node.detachEvent("on" + eventName, method, onCapture) else node.removeEventListener(eventName, method, onCapture); count--; var eventToRemove = events.firstOrDefault(function(item) { return ( (item.method == method) && (item.onCapture = onCapture) ); }); events.remove(eventToRemove); } catch (ex) { Aston.LogException(ex); } }; this.fire = function(preventEventBubble) { try { preventEventBubble = preventEventBubble || false; if(Aston.BrowserType == Aston.BrowserTypes.MSIE && Aston.BrowserVersion < 11) node.fireEvent("on" + eventName); //TODO: this probably wont work else { var evt = document.createEvent("Event"); evt.initEvent(eventName, !preventEventBubble, true); node.dispatchEvent(evt); } } catch (ex) { Aston.LogException(ex); } }; this.count = function() { return count; } this.clear = function() { events.forEach(function(item) { that.remove(item.method, item.onCapture); }); }; }; // MOVE TO DOM extensions? // didn't seem right in the nodes namespace - but it uses them so it's here Aston.Dom.Document = (function() { var constructor = function() { Aston.Dom.Element.call(this, document); var keyUp = new Aston.Dom.Event(document, "keyup"); var keyDown = new Aston.Dom.Event(document, "keydown"); var fullHeightToRemainingHeight = false; this.keyUp = function(method, onCapture) { return method == undefined ? keyUp : keyUp.add(method, onCapture); }; this.keyDown = function(method, onCapture) { return method == undefined ? keyDown : keyDown.add(method, onCapture); } var disableTextSelection = function(event) { Aston.Dom.PreventDefault(event); }; this.allowTextSelection = function(allow) { if(allow) this.mouseDown().remove(disableTextSelection); else this.mouseDown().add(disableTextSelection); }; this.isAddedToDom = function() { return true; }; this.SetFullHeightToRemainingHeight = function(convert) { fullHeightToRemainingHeight = convert; }; this.GetFullHeightToRemainingHeight = function() { return fullHeightToRemainingHeight; }; var preventDetault = function(event) { Aston.Dom.PreventDefault(event); //Aston.Dom.StopPropagation(event); }; this.dragEnter(preventDetault); this.dragOver(preventDetault); this.drop(preventDetault); }; return new constructor(); })(); Aston.Dom.Document.Body = (function() { var constructor = function() { Aston.Dom.Element.call(this, document.body); }; return new constructor(); })(); // didn't seem right in the nodes namespace - but it uses them so it's here // NOTE: cant's create a reference to the body before it's been created new Aston.Dom.Event(window, "load").add(function() { Aston.Dom.Body = (function() { var constructor = function() { Aston.Dom.Element.call(this, document.body); this.isAddedToDom = function() { return true; }; }; return new constructor(); })(); }); //TODO: this does not belong here Aston.Dom.RemainingHeightElements = (function() { var constructor = function() { // FIELDS var that = this; var enabled = true; var elements = []; // PUBLIC METHODS this.setEnabled = function(newEnabled) { enabled = newEnabled; if(newEnabled) that.process(); } this.add = function(element) { //TODO: do these need sorting according to dependancies? if(!elements.contains(element)) elements.add(element); // cache this because it's expensive to calculate // WARNING: if JS is used to alter the overflow-y this will need to be reset element.remainingHeights_toProcess = Aston.Dom.GetStyleValue(element._getNode(), Aston.Dom.Style.overflowY) == "visible"; }; this.remove = function(element) { if(elements.contains(element)) { elements.remove(element); return true; } return false; }; this.process = function(addedNode) { if(!enabled) return; // we only need to alter the first parent node affected // setting the remaining height is an expensive DOM lookup method, better to do what we can here to reduce its use if(addedNode) { var parentNode = addedNode; while(parentNode != null) { if(elements.contains(parentNode)) { // this is the node we may need to adjust the height of, NOT all of them // no need to adjust overflow hidden or scroll because child elements cannot alter the height if((parentNode == addedNode) || (parentNode.remainingHeights_toProcess)) Aston.Dom.SetRemainingHeight(parentNode._getNode()); break; } parentNode = parentNode.getParent(); } } else { elements.foreach(function(element) { Aston.Dom.SetRemainingHeight(element._getNode()); }); } } // CONSTRUCTORS try{ new Aston.Dom.Event(window, "resize").add(function () { that.process(); }); } catch(ex) { Aston.LogException(ex); }; }; return new constructor(); })(); // ********************************************************************************************* /* Author: ------- James Hall - www.astonprograms.co.uk Description: ------------ Handles keyboard input sets Uses: ----- Aston.Core Aston.Dom Methods: -------- TODO: ------------ */ // ********************************************************************************************* // Aston.Core.Keyboard // ********************************************************************************************* Aston.Namespace("Aston.Core"); Aston.Core.Keyboard = (function() { var constructor = function() { // FIELDS var commandKeys = 0;//Aston.Core.Keyboard.CommandKeys.None.GetValue(); var keySets = []; // PRIVATE CLASSES var KeySet = function(object, allowFallThrough) { allowFallThrough = allowFallThrough == undefined ? true : allowFallThrough; var keyDownListeners = []; var keyUpListeners = []; this.getObject = function() { return object; }; this.getKeyDownListeners = function() { return keyDownListeners; }; this.getKeyUpListeners = function() { return keyUpListeners; }; this.allowFallThrough = function() { return allowFallThrough; }; }; var KeyListener = function(commandKey, key, method) { this.getCommandKey = function() { return commandKey; }; this.getKey = function() { return key; }; this.getMethod = function() { return method; }; }; // PUBIC METHODS // Adds a new set of keyboard listeners attached to the given object this.addKeySet = function(object, keyDownListeners, keyUpListeners, allowFallThrough) { try { var keySet = keySets.firstOrDefault(function(keySet) { return keySet.getObject() == object; }); if(keySet == null) { keySet = new KeySet(object, allowFallThrough); keySets.add(keySet); } if((keyDownListeners) && (keyDownListeners != null)) { keyDownListeners.foreach(function(listener) { var commandKey = validateCommandKey(listener.commandKey); var key = validateKey(listener.key); keySet.getKeyDownListeners().add(new KeyListener(commandKey, key, listener.action)); }); } if((keyUpListeners) && (keyUpListeners != null)) { keyUpListeners.foreach(function(listener) { commandKey = validateCommandKey(listener.commandKey); key = validateKey(listener.key); keySet.getKeyUpListeners().add(new KeyListener(commandKey, key, listener.action)); }); } } catch(ex) { Aston.LogException(ex); } }; // Removes an existing set of keyboard listeners attached to the given object // Optional Arguments: keyDownListeners, keyUpListeners this.removeKeySet = function(object, keyDownListeners, keyUpListeners) { var keySet = keySets.firstOrDefault(function(keySet) { return keySet.getObject() == object; }); if(keySet != null) { if((keyDownListeners == undefined) && (keyUpListeners == undefined)) { keySets.remove(keySet); } else { if(keyDownListeners != undefined) { keySet.getKeyDownListeners().removeRange(keySet.getKeyDownListeners().where(function(listener) { return keyDownListeners.contains(listener); })); } if(keyUpListeners != undefined) { keySet.getKeyUpListeners().removeRange(keySet.getKeyUpListeners().where(function(listener) { return keyUpListeners.contains(listener); })); } if((keySet.getKeyDownListeners().length == 0) && (keySet.getKeyUpListeners().length == 0)) keySets.remove(keySet); } } }; // PRIVATE METHODS var validateKey = function(key) { if (key == null) return null; // any key if(Number.ParseNumber(key) == key) return key; if(key.charCodeAt) key = key.charCodeAt(0); else if(key.getValue) key = key.getValue(); // handle the enum else throw new Error("Cannot recognise the key"); return key; } var validateCommandKey = function(commandKey) { //TODO: a better type checking for enum if(commandKey.getValue) commandKey = commandKey.getValue(); return commandKey; }; var keyPress = function(commandKey, keyCode, isKeyDown) { // find the actions that that match the given key parameters in most recently added keyset var listeners = []; var i = keySets.length - 1; while(i >= 0) { var upDownListeners = isKeyDown ? keySets[i].getKeyDownListeners() : keySets[i].getKeyUpListeners(); listeners.addRange(upDownListeners.where(function(listener) { return ( (listener.getCommandKey() == commandKey) && ((listener.getKey() == keyCode) || (listener.getKey() == null)) // any ); })); // if we found an exact response then stop here if(listeners.any(function(listener) { return listener.getKey() != null; })) break; if(!keySets[i].allowFallThrough()) // if there's no fall through then stop here anyway break; i--; } var result = false; //TODO: WTF, listeners.length can be 5 but then the foreach only shows 1? //Aston.Log("Number of listeners " + listeners.length); //listeners.foreach(function(listener) //{ // Aston.Log(listener.getMethod()); //}); listeners.foreach(function(listener) { var _preventDefault = listener.getMethod()(); if(_preventDefault == undefined) { // if the event was NOT resgistered to AnyKey (null) then we did take an action if(listener.getKey() != null) result = true; } else { result = _preventDefault; } }); return result; }; // CONSTRUCTOR try { //Alt - Tab causes a problem here because we track the ALT key then loose focus and it's never set off new Aston.Dom.Event(window, "blur").add(function(event) { if(event.target == window) commandKeys = 0; }); Aston.Dom.Document.keyDown(function(event) { try { // always use lower case characters and detect the shift key instead var keyCode; if(event.which != undefined) keyCode = event.which; else keyCode = event.keyCode; // capitals if((keyCode >= 65) && (keyCode <= 90)) keyCode += 32; // numpad // NOTE: WTF ??? //if ((keyCode >= 96) && (keyCode <= 105)) // keyCode -= 48; var takenAction = false; switch(event.keyCode) { case 16: commandKeys |= Aston.Core.Keyboard.CommandKeys.Shift.getValue(); break; case 17: commandKeys |= Aston.Core.Keyboard.CommandKeys.Ctrl.getValue(); break; case 18: commandKeys |= Aston.Core.Keyboard.CommandKeys.Alt.getValue(); break; default: takenAction = keyPress(commandKeys, keyCode, true); } if(takenAction) Aston.Dom.PreventDefault(event); } catch (ex) { Aston.LogException(ex); } }); Aston.Dom.Document.keyUp(function(event) { try { // always use lower case characters and detect the shift key instead var keyCode = event.which; if((keyCode >= 65) && (keyCode <= 90)) keyCode += 32; var takenAction = false; switch(event.keyCode) { case 16: commandKeys = commandKeys & ~Aston.Core.Keyboard.CommandKeys.Shift.getValue(); break; case 17: commandKeys = commandKeys & ~Aston.Core.Keyboard.CommandKeys.Ctrl.getValue(); break; case 18: commandKeys = commandKeys & ~Aston.Core.Keyboard.CommandKeys.Alt.getValue(); break; default: takenAction = keyPress(commandKeys, event.keyCode, false); } if(takenAction) Aston.Dom.PreventDefault(event); } catch (ex) { Aston.LogException(ex); } }); } catch (ex) { Aston.LogException(ex); }; }; return new constructor(); })(); Aston.Core.Keyboard.CommandKeys = Aston.Enum( { None : 0, Shift : Math.pow(2,0), Ctrl : Math.pow(2,1), Alt : Math.pow(2,2) }); Aston.Core.Keyboard.SpecialKeys = Aston.Enum( { Backspace : 8, Tab : 9, Enter : 13, Escape : 27, SpaceBar : 32, PageUp : 33, PageDown : 34, End : 35, Home : 36, Left : 37, Up : 38, Right : 39, Down : 40, Insert : 45, Delete : 46 }); // ********************************************************************************************* /* Author: ------- James Hall - www.astonprograms.co.uk Description: ------------ Uses: ----- Aston.Core.js Aston.Dom.js Aston.Dom.Element.js Aston.Dom.Nodes.Node.js Methods: -------- TODO: ------------ This is really Nodes & Elements */ // ********************************************************************************************* Aston.Namespace("Aston.Dom.Nodes"); // a helper method to add things to other classes Aston.Dom.Nodes.InputElement = function(node) { // FIELDS var valueChanged = new Aston.Core.Event(); var focus = new Aston.Dom.Event(node, "focus"); var blur = new Aston.Dom.Event(node, "blur"); // PUBLIC METHODS this.valueChanged = function(method, onCapture) { return method == undefined ? valueChanged : valueChanged.add(method, onCapture); }; this.focus = function(method, onCapture) { return method == undefined ? focus : focus.add(method, onCapture); }; this.blur = function(method, onCapture) { return method == undefined ? blur : blur.add(method, onCapture); }; this.setEnabled = function (isEnabled) { node.disabled = !isEnabled; }; this.setReadonly = function (isReadonly) { node.readOnly = isReadonly; }; this.setBlurred = function() { node.blur(); }; this.setFocused = function() { node.focus(); }; this.getName = function() { return node.name; }; this.setName = function(name) { node.name = name; }; this.getValue = function() { return node.value; }; this.setValue = function(value) { node.value = value; valueChanged.fire(); }; this.disableAutoComplete = function() { node.setAttribute("autocomplete","off"); }; this.mouseOver(function() { Aston.Dom.Document.allowTextSelection(true); }); this.mouseOut(function() { Aston.Dom.Document.allowTextSelection(false); }); // CONSTRUCTOR try { //TODO: hook up valueChanged var keyDownListeners = [ { commandKey : Aston.Core.Keyboard.CommandKeys.None, key : null, // any key action : valueChanged.fire }, { commandKey : Aston.Core.Keyboard.CommandKeys.Shift, key : null, // any key action : valueChanged.fire }, { commandKey : Aston.Core.Keyboard.CommandKeys.Alt, key : null, // any key action : valueChanged.fire } ]; focus.add(function() { Aston.Core.Keyboard.addKeySet ( node, keyDownListeners, keyDownListeners // and on the way up, so that this runs after the value has been set ); }); blur.add(function() { Aston.Core.Keyboard.removeKeySet ( node, keyDownListeners ); }); } catch (ex) { Aston.LogException(ex); } }; Aston.Dom.Nodes.DIV = function() { var node = document.createElement("DIV"); Aston.Dom.Element.call(this, node); this.setContentEditable = function(editable) { node.contentEditable = editable; if(editable) Aston.Dom.Nodes.InputElement.call(this, node); } }; Aston.Dom.Nodes.DIV.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.UL = function() { var node = document.createElement("UL"); Aston.Dom.Element.call(this, node); }; Aston.Dom.Nodes.UL.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.LI = function() { var node = document.createElement("LI"); Aston.Dom.Element.call(this, node); }; Aston.Dom.Nodes.LI.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.SPAN = function() { var node = document.createElement("SPAN"); Aston.Dom.Element.call(this, node); }; Aston.Dom.Nodes.SPAN.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.H1 = function() { var node = document.createElement("H1"); Aston.Dom.Element.call(this, node); }; Aston.Dom.Nodes.H1.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.H2 = function() { var node = document.createElement("H2"); Aston.Dom.Element.call(this, node); }; Aston.Dom.Nodes.H2.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.H3 = function() { var node = document.createElement("H3"); Aston.Dom.Element.call(this, node); }; Aston.Dom.Nodes.H3.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.P = function() { var node = document.createElement("P"); Aston.Dom.Element.call(this, node); }; Aston.Dom.Nodes.P.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.A = function() { var node = document.createElement("A"); Aston.Dom.Element.call(this, node); //var focus = new Event("focus"); //var blur = new Event("blur"); var focus = new Aston.Dom.Event(node, "focus"); var blur = new Aston.Dom.Event(node, "blur"); this.setBlurred = function() { node.blur(); }; this.setFocused = function() { node.focus(); }; this.getHref = function() { return node.href; }; this.setHref = function(href) { node.href = href; }; // Automatically prevent A tags from going to their href // NOTE: the click event created in element is actually a mousedown without mousedown and hold // NOTE: Only prevents links created this way from not redirecting the browser. // ie links already written in to the html will still function new Aston.Dom.Event(node, "click").add(function(event){ Aston.Dom.PreventDefault(event); }); this.focus = function(method, onCapture) { return method == undefined ? focus : focus.add(method, onCapture); }; this.blur = function(method, onCapture) { return method == undefined ? blur : blur.add(method, onCapture); }; }; Aston.Dom.Nodes.A.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.IMG = function() { var node = document.createElement("IMG"); Aston.Dom.Element.call(this, node); this.getSrc = function() { return node.src; }; this.setSrc = function(src) { node.src = src; }; this.getAltText = function() { return node.alt; }; this.setAltText = function(alt) { node.alt = alt; }; }; Aston.Dom.Nodes.IMG.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.INPUT = function() { var node = document.createElement("INPUT"); Aston.Dom.Element.call(this, node); Aston.Dom.Nodes.InputElement.call(this, node); this.getType = function() { if((Aston.BrowserType == Aston.BrowserTypes.MSIE) && (Aston.BrowserVersion < 9)) node.getAttribute("type"); else return node.type; }; this.setType = function(type) { if((Aston.BrowserType == Aston.BrowserTypes.MSIE) && (Aston.BrowserVersion < 9)) { if(node.parentNode != null) { var newNode = node.cloneNode(false); newNode.setAttribute("type", type); node.parentNode.replaceChild(newNode, node); node = newNode; } else { node.setAttribute("type", type); } } else { node.type = type; } }; }; Aston.Dom.Nodes.INPUT.Inherits(Aston.Dom.Element); //TODO: Create enumeration of types Aston.Dom.Nodes.TEXTAREA = function() { var node = document.createElement("TEXTAREA"); Aston.Dom.Element.call(this, node); Aston.Dom.Nodes.InputElement.call(this, node); }; Aston.Dom.Nodes.TEXTAREA.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.LABEL = function() { var node = document.createElement("LABEL"); Aston.Dom.Element.call(this, node); this.getFor = function() { return node.htmlFor; }; this.setFor = function(htmlFor) { node.htmlFor = htmlFor; }; }; Aston.Dom.Nodes.LABEL.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.TEXT = function(text) { var isHtml = false; text = text || String.Empty; // deal with TEXT c# objects if (typeof text != "string" && !(text instanceof String)) { isHtml = text.isHtml; text = text.text; } if(!isHtml) { var node = document.createTextNode(text); Aston.Dom.Node.call(this, node); // Note this is a Node, not an Element this.getText = function() { return node.nodeValue; }; this.setText = function(text) { node.nodeValue = text; }; } else { var node = document.createElement("DIV"); Aston.Dom.Node.call(this, node); // Note this is a Node, not an Element node.innerHTML = text; } /* this.splitNode = function(splitPoint) { var newNode = new Aston.Dom.Nodes.TEXT(node.nodeValue.substring(splitPoint)); node.nodeValue = node.nodeValue.substring(0, splitPoint) if(startNode.nextSibling != null) parentNode.insertBefore(splitNode, startNode.nextSibling); else parentNode.appendChild(splitNode); }; */ }; Aston.Dom.Nodes.TEXT.Inherits(Aston.Dom.Node); // Note this is a Node, not an Element // See Aston.Dom.Components for the full table component description Aston.Dom.Nodes.TABLE = function() { var node = document.createElement("TABLE"); Aston.Dom.Element.call(this, node); }; Aston.Dom.Nodes.TABLE.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.SELECT = function() { var node = document.createElement("SELECT"); Aston.Dom.Element.call(this, node); Aston.Dom.Nodes.InputElement.call(this, node); // possibly add on change selection }; Aston.Dom.Nodes.SELECT.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.OPTION = function() { var node = document.createElement("OPTION"); Aston.Dom.Element.call(this, node); Aston.Dom.Nodes.InputElement.call(this, node); var text = new Aston.Dom.Nodes.TEXT(); this.getChildNodes().add(text); this.getText = function() { return text.getText(); }; this.setText = function(value) { text.setText(value); }; this.getSelected = function() { return node.selected; }; this.setSelected = function(value) { node.selected = value; }; }; Aston.Dom.Nodes.OPTION.Inherits(Aston.Dom.Element); Aston.Dom.Nodes.IFRAME = function(src) { var node = document.createElement("IFRAME"); Aston.Dom.Node.call(this, node); if(node.src != undefined) node.src = src; this.getSrc = function() { return node.src; }; this.setSrc = function(src) { node.src = src; }; this.setDimensions = function(dimensions) { try { node.width = dimensions.width; node.height = dimensions.height; } catch (ex) { Aston.LogException(ex); } }; var load = new Aston.Dom.Event(node, "load"); this.load = function(method, onCapture) { return method == undefined ? load : load.add(method, onCapture); }; this.show = function () { try { node.style.display = String.Empty; } catch (ex) { Aston.LogException(ex); }; }; this.hide = function () { try { node.style.display = "none"; } catch (ex) { Aston.LogException(ex); }; }; }; Aston.Dom.Nodes.IFRAME.Inherits(Aston.Dom.Node); Aston.Dom.Nodes.BUTTON = function() { var node = document.createElement("BUTTON"); Aston.Dom.Element.call(this, node); }; Aston.Dom.Nodes.BUTTON.Inherits(Aston.Dom.Element); /** Author: ------- James Hall - www.astonprograms.co.uk Description: ------------ OSX style menu - Base List (should be abstract) Uses: ----- Aston.Core Aston.Dom Methods: -------- TODO: ------------ */ // ********************************************************************************************* Aston.Namespace("Aston.Dom.Components"); // ********************************************************************************************* // TOGGLE SWITCH // ********************************************************************************************* Aston.Dom.Components.ToggleSwitch = function (initialState, specialValue) { // BASE CONSTRUCTOR Aston.Dom.Nodes.SPAN.call(this); // FIELDS var that = this; var value = initialState || false; var valueChanged = new Aston.Core.Event(); var readonly = false; // PUBLIC METHODS this.valueChanged = function(method, onCapture) { return method == undefined ? valueChanged : valueChanged.add(method, onCapture); }; this.toggle = function (event) { that.setValue(!value); }; this.setValue = function (newValue) { if(!readonly) { value = newValue; that.getClasses().remove(Aston.Dom.Components.ToggleSwitch.Defaults.getToggleSwitchClassNameState(!value)); that.getClasses().add(Aston.Dom.Components.ToggleSwitch.Defaults.getToggleSwitchClassNameState(value)); valueChanged.fire(); } }; this.getValue = function () { if(specialValue) return specialValue; return value; }; this.setReadonly = function(_readonly) { readonly = _readonly; }; //CONSTRUCTORS try { this.click(function (event) { that.toggle(event); }); this.setValue(value); this.getClasses().add(Aston.Dom.Components.ToggleSwitch.Defaults.ToggleSwitchClassName); } catch (ex) { Aston.LogException(ex); } }; Aston.Dom.Components.ToggleSwitch.Inherits(Aston.Dom.Nodes.SPAN); Aston.Dom.Components.ToggleSwitch.Defaults = { ToggleSwitchClassName: "aston_dom_components_toggleSwitch", getToggleSwitchClassNameState: function (value) { return "aston_dom_components_toggleSwitch_" + value; } }; // TODO: construct with data Aston.Dom.Components.Table = function(tableData) { // BASE CONSTRUCTOR Aston.Dom.Nodes.TABLE.call(this); // INNER CLASSES var THead = function() { var node = document.createElement("THEAD"); Aston.Dom.Element.call(this, node); }; THead.Inherits(Aston.Dom.Element); var TBody = function() { var node = document.createElement("TBODY"); Aston.Dom.Element.call(this, node); }; TBody.Inherits(Aston.Dom.Element); var TR = function (data) { var node = document.createElement("TR"); Aston.Dom.Element.call(this, node); var that = this; var csTypes = null; var isSelectable = false; var wasVisible = false; var isSelected = false; var className = String.Empty; var selectedClassName = String.Empty; var selected = new Aston.Core.Event(); var deselected = new Aston.Core.Event(); var visible = new Aston.Core.Event(); // PUBLIC METHODS this.getName = function () { return name; }; this.isSelectable = function () { return isSelectable; }; this.getCSTypes = function () { return csTypes; }; this.isSelected = function () { return isSelected; }; this.selected = function (method, onCapture) { return method == undefined ? selected : selected.add(method, onCapture); }; this.deselected = function (method, onCapture) { return method == undefined ? deselected : deselected.add(method, onCapture); }; this.visible = function (method, onCapture) { return method == undefined ? visible : visible.add(method, onCapture); }; // CONSTRUCTOR try { if (data != undefined) { if ((data.className) && (!String.IsNullOrEmpty(data.className))) that.getClasses().add(data.className); if (data.colSpan) that.setColSpan(data.colSpan); if (data.click) { data.click.foreach(function (action) { that.click(Aston.Core.getFunction(action)); }); } if (data.clickHold) { data.clickHold.foreach(function (action) { that.clickHold(Aston.Core.getFunction(action)); }); } if (data.doubleClick) { data.doubleClick.foreach(function (action) { that.doubleClick(Aston.Core.getFunction(action)); }); } if (data.mouseOver) { data.mouseOver.foreach(function (action) { that.mouseOver(Aston.Core.getFunction(action)); }); } if (data.mouseOut) { data.mouseOut.foreach(function (action) { that.mouseOut(Aston.Core.getFunction(action)); }); } if (data.csTypes) csTypes = data.csTypes; if (className != String.Empty) that.getClasses().remove(className); if ((data.className) && (!String.IsNullOrEmpty(data.className))) { className = data.className; that.getClasses().add(className); } if ((data.selectedClassName) && (!String.IsNullOrEmpty(data.selectedClassName))) selectedClassName = data.selectedClassName; if (data.isSelected == true) // NOTE: an item can be non-selectable, but start as selected that.select(); } } catch (ex) { Aston.LogException(ex); } }; TR.Inherits(Aston.Dom.Element); var Cell = function (node, data) { //Allow inheritance without a node as the single inherited base class. if (node == undefined) return; // BASE CLASS Aston.Dom.Element.call(this, node); // FIELDS var that = this; // PUBLIC METHODS this.getColSpan = function () { return node.colSpan; }; this.setColSpan = function (colSpan) { node.colSpan = colSpan; }; // CONSTRUCTOR try { if ((data.className) && (!String.IsNullOrEmpty(data.className))) that.getClasses().add(data.className); if (data.colSpan) that.setColSpan(data.colSpan); if (data.click) { data.click.foreach(function (action) { that.click(Aston.Core.getFunction(action)); }); } if (data.clickHold) { data.clickHold.foreach(function (action) { that.clickHold(Aston.Core.getFunction(action)); }); } if (data.doubleClick) { data.doubleClick.foreach(function (action) { that.doubleClick(Aston.Core.getFunction(action)); }); } if (data.mouseOver) { data.mouseOver.foreach(function (action) { that.mouseOver(Aston.Core.getFunction(action)); }); } if (data.mouseOut) { data.mouseOut.foreach(function (action) { that.mouseOut(Aston.Core.getFunction(action)); }); } if (data.innerViewModels != undefined) { data.innerViewModels.foreach(function (innerViewModel) { var toConstruct = Aston.Namespace(innerViewModel.type); var innerItem = null; try { innerItem = new toConstruct(innerViewModel.data); that.getChildNodes().add(innerItem); } catch (ex) { Aston.LogException(ex); Aston.Log("tileData = "); Aston.Log(tileData); Aston.Log("toConstruct = "); Aston.Log(toConstruct); } }); } } catch (ex) { Aston.LogException(ex); } }; Cell.Inherits(Aston.Dom.Element); var TH = function (data) { var node = document.createElement("TH"); Cell.call(this, node, data); }; TH.Inherits(Cell); var TD = function (data) { var node = document.createElement("TD"); Cell.call(this, node, data); }; TD.Inherits(Cell); // FIELDS var that = this; var table, thead, tbody; // PUBLIC METHODS this.addHeadRow = function (headData) { try { var tr; var cells; if (headData instanceof Array) { tr = new TR(); cells = headData; } else { tr = new TR(headData); cells = headData.cells; } cells.foreach(function (thData) { var th; if(thData instanceof Aston.Dom.Node) { th = new TH({}); th.getChildNodes().add(thData); } else { th = new TH(thData); } tr.getChildNodes().add(th); }); thead.getChildNodes().add(tr); } catch (ex) { Aston.LogException(ex); } }; this.addBodyRow = function(bodyData) { try { var tr = new TR(bodyData); bodyData.cells.foreach(function(tdData) { // if is Node then create with no data // and add to TD // otherwise create from data var td; if(tdData instanceof Aston.Dom.Node) { td = new TD({}); td.getChildNodes().add(tdData); } else { td = new TD(tdData); } tr.getChildNodes().add(td); }); tbody.getChildNodes().add(tr); } catch (ex) { Aston.LogException(ex); } }; this.clearBody = function () { tbody.getChildNodes().clear(); }; this.findRowByName = function (name) { return tbody.getChildNodes().firstOrDefault(function (row) { return row.name == name; }); }; this.removeRowByName = function (name) { var row = that.findRowByName(name); if(row != null) tbody.getChildNodes().remove(row); else Aston.Log("Table.removeRowByName: Cannot find table row with name " + name); } this.getBodyRows = function () { return tbody.getChildNodes(); } // PRIVATE METHODS // CONSTRUCTORS try { thead = new THead(); this.getChildNodes().add(thead); tbody = new TBody(); this.getChildNodes().add(tbody); if (tableData) { if (tableData.columnHeadings) that.addHeadRow(tableData.columnHeadings); if (tableData.rows instanceof Array) { tableData.rows.foreach(function (row) { that.addBodyRow(row); }); } } } catch (ex) { Aston.LogException(ex); } }; Aston.Dom.Components.Table.Inherits(Aston.Dom.Nodes.TABLE); Aston.Dom.Components.Select = function() { // BASE CONSTRUCTOR Aston.Dom.Nodes.SELECT.call(this); // FIELDS var that = this; // PUBLIC METHODS this.addOption = function (text, value, isSelected) { try { if(isSelected == undefined) isSelected = false; var option = new Aston.Dom.Nodes.OPTION(); option.setValue(value); option.setSelected(isSelected); var label = new Aston.Dom.Nodes.TEXT(text); option.getChildNodes().add(label); that.getChildNodes().add(option); } catch (ex) { Aston.LogException(ex); } }; //CONSTRUCTORS try { } catch (ex) { Aston.LogException(ex); } }; Aston.Dom.Components.Select.Inherits(Aston.Dom.Nodes.SELECT); //NOTE: Remember that this isn't the opened list, but the textbox that links to the opened list Aston.Dom.Components.DataChooser = function(data) { // BASE CONSTRUCTOR Aston.Dom.Nodes.INPUT.call(this); // FIELDS var that = this; // can register events on a selected item being of the choosable type var canChoose = new Aston.Core.Event(); // event fired when an item is chosen var chosen = new Aston.Core.Event(); var selectedItem = null; var value = String.Empty; // PUBLIC METHODS this.canChoose = function(method, onCapture) { return method == undefined ? canChoose : canChoose.add(method, onCapture); }; this.chosen = function(method, onCapture) { return method == undefined ? chosen : chosen.add(method, onCapture); }; this.getValue = function () { return value; } this.getJSONValue = function () { return JSON.stringify(value); } this.setText = function (text) { that.setValue(text); }; this.dispose = function() { try { //TODO: get these form the server - probably in the data object Aston.Arc.Tiles.dataItemChoosers.remove(select); Aston.Arc.Tiles.anyTileAnyItemSelected().remove(chooseItem); Aston.Core.Keyboard.removeKeySet(that); } catch (ex) { Aston.LogException(ex); } } // PRIVATE METHODS var chooseItem = function(tile, item) { if ((item != null) && (item.getCSTypes() != null) && (item.getCSTypes().contains(data.chooseCSType))) { selectedItem = item; canChoose.fire.call(that); if(item.click().isExecuting()) { item.click().pause(); select(); } } else { selectedItem = null; } }; //NOTES // allow for multiple CS type, an Array of them to hanlde inheritance // always return the most derived type // make value a composite type of ID & Type (consider Type[]) // server side .. yeah should be fine in the data mapper // use enum to determin which mode is desired, default is jsut ID var select = function() { if ((selectedItem != null) && (selectedItem.getCSTypes() != null) && (selectedItem.getCSTypes().contains(data.chooseCSType))) { if(!data.includeType) value = selectedItem.getName(); // value used - set this first before the input.valueChanged fires else value = { ID: selectedItem.getName(), Type: selectedItem.getCSTypes()[0] }; if(selectedItem.getText) that.setValue(selectedItem.getText()); // value shown else that.setValue(selectedItem.getName()); // value shown and used chosen.fire.call(that); } // clear else { // set this first before the input.valueChanged fires if (!data.includeType) value = String.Empty; else value = { ID: String.Empty, Type: String.Empty }; that.setValue(String.Empty); } }; //CONSTRUCTORS try { // SETUP that.setType("text"); that.setReadonly(true); if(data.text) that.setValue(data.text); // value shown if(data.value) value = data.value; // value used // DEFAULTS that.click(function() { //TODO: get from server (probably in data object) Aston.Arc.Tiles.dataItemChoosers.add(select); Aston.Arc.Tiles.anyTileAnyItemSelected().add(chooseItem); // keyb shortcut to select // TODO: get from server Aston.Core.Keyboard.addKeySet ( that, [ { commandKey : Aston.Core.Keyboard.CommandKeys.None, key : Aston.Core.Keyboard.SpecialKeys.Enter, action : select } ] ); }); chosen.add(function() { that.dispose(); }); // RIG EVENTS if(data.canChoose) { data.canChoose.foreach(function(action) { canChoose.add(Aston.Core.getFunction(action)); }); } if(data.chosen) { data.chosen.foreach(function(action) { chosen.add(Aston.Core.getFunction(action)); }); } } catch (ex) { Aston.LogException(ex); } }; Aston.Dom.Components.DataChooser.Inherits(Aston.Dom.Nodes.INPUT); // ********************************************************************************************* /* References: Aston.Core.js */ // ********************************************************************************************* /* TODO: Potentially force the use to remove the node from one parent before adding to another. */ // ********************************************************************************************* // Aston.Dom // ********************************************************************************************* Aston.Namespace("Aston.Dom"); Aston.Dom.Selection = function(container) { // FIELDS var selection; // PRIVATE METHODS // Find an Aston.Dom.Nodes.Node that matched a DOM obj toFind // NOTE: this is quite expensive var findNode = function(node) { try { var recurse = function(container, node) { if(container.handlesNode(node)) return container; if(container.getChildNodes) { container.getChildNodes().foreach(function(child) { var childResult = recurse(child, node); if(childResult != null) return childResult; }); }; return null; } return recurse(container, node); } catch (ex) { Aston.LogException(ex); }; } // PUBLIC METHODS //NOTE: This is an inefficient way of producing code and will result in double spans on some occassions this.setSelectionClassName = function(className) { try { var range = selection.getRangeAt(0); var newNode = document.createElement("SPAN"); newNode.appendChild(range.extractContents()); range.insertNode(newNode); newNode.className = className; range.selectNode(newNode); } catch (ex) { Aston.LogException(ex); }; } // CONSTRUCTORS try { selection = window.getSelection(); } catch (ex) { Aston.LogException(ex); }; } /** Author: ------- James Hall - www.astonprograms.co.uk Description: ------------ Drop down menu Uses: ----- Aston.Core Aston.Dom Constructor: ------------ new Aston.Menus.DropDown(menuJSON, options) options - Collection of JSON root parameters menuData: { collapseDelay - The time between mousing out of the menu and the dropdown disappearing default: 1000ms dropdownIDFunc - A function to apply an ID to the DIVs used for the drop down menus func params: rank default: function(rank) { return "Aston_Menus_DropDown_" + rank; } hasChildrenClass - The class name assigned to all popup menu divs default: hasChildren parentSelectedClass - The class name assigned to links with a selected child default: parentSelected setPositionFunc - The function called to position the sub menus default: function() { return this.getPosition(); } menu - The data to build the menu from default: (none) } menu : [ { isImmutable : false, name : "", innerHTML : "", href : "", className : "", click : function() { //do something called as the link }, menu : [ ... ] }, { ... } ] Methods: -------- .addToDom(node) - Add all menus to the Dom node given .removeItemsByName - Removes all menu items with the given name .addToMenu - Adds new JSON to a menu item with the given name, or the root is no name is given / found */ // ********************************************************************************************* // ********************************************************************************************* //TODO: consider adding a text field to the JSON and using name for guids (or other identifier) Aston.Namespace("Aston.Menus"); Aston.Menus.DropDown = function(menuData, tooltipHandler, options) { // FIELDS var that = this; options = options || {}; var collapseDelay; var hasChildrenClass; var parentSelectedClass; var spanText; var dropdownIDFunc; var setPositionFunc; var menuDirection; var useMiniMode; var rootMenu = null; var menuContainerNode = new Aston.Dom.Nodes.DIV; var expandedItems = []; var timer = null; // PUBLIC METHODS this.addToDom = function(parentElement, referenceElement) { try { menuContainerNode.addToDom(parentElement, referenceElement); } catch (ex) { Aston.LogException(ex); }; }; this.updateMenu = function(menuData) { try { if(menuData.collapseDelay) collapseDelay = menuData.collapseDelay; if((menuData.hasChildrenClass) && (!String.IsNullOrEmpty(menuData.hasChildrenClass))) hasChildrenClass = menuData.hasChildrenClass; if((menuData.parentSelectedClass) && (!String.IsNullOrEmpty(menuData.parentSelectedClass))) parentSelectedClass = menuData.parentSelectedClass; if(menuData.dropdownIDFunc) dropdownIDFunc = new Function(menuData.dropdownIDFunc); if((menuData.parentSelectedClass) && (!String.IsNullOrEmpty(menuData.parentSelectedClass))) setPositionFunc = new Function(menuData.setPositionFunc); if(menuData.addItems) addItems.call(this, menuData.addItems); if(menuData.removeItems) removeItems.call(this, menuData.removeItems); if(menuData.updateItems) updateItems.call(this, menuData.updateItems); //TODO: add insert } catch (ex) { Aston.LogException(ex); }; }; this.clearMenu = function() { try { for(var i = rootMenu.getChildNodes().count() - 1; i >= 0; i--) { if(!rootMenu.getChildNodes().item(i).isImmutable) rootMenu.getChildNodes().removeAt(i); } } catch (ex) { Aston.LogException(ex); } }; this.actionByNames = function (names) { try { var item = getItemByNames(names); if (item != null) item.link.clickFunction(); } catch (ex) { Aston.LogException(ex); } }; this.actionByName = function (name) { try { var item = getItemByName(name); if (item != null) item.link.clickFunction(); } catch (ex) { Aston.LogException(ex); } } this.expandMenu = function (name) { try { var item = getItemByName(name); if (item != null) { showMenu.call(item); hideMenu.call(item); } } catch (ex) { Aston.LogException(ex); } }; // PRIVATE METHODS function showMenu(e) // called where this == the item { try { if (timer != null) clearTimeout(timer); for (var i = expandedItems.length - 1; i >= this.getRank() ; i--) { menuContainerNode.getChildNodes().remove(expandedItems[i].menu); expandedItems[i].link.getClasses().remove(parentSelectedClass); expandedItems.pop(); } if (this.menu != undefined) { expandedItems.push(this); menuContainerNode.getChildNodes().add(this.menu); // center the child items around the parent // TODO: limit to within the bounds of the window // NOTE: complete hack for the moment //var linkPosition = this.getPosition().x + (this.getDimensions().width / 2); var linkPosition = this.getPosition().x; var itemDimensions = this.menu.getChildNodes().item(0).getDimensions(); var offset = 0; if(menuDirection == Aston.Menus.DropDown.Direction.Horizontal) offset = linkPosition - (this.menu.getChildNodes().count() / 2 * itemDimensions.width); for (var i = 0; i < this.menu.getChildNodes().count() ; i++) { if(menuDirection == Aston.Menus.DropDown.Direction.Horizontal) this.menu.getChildNodes().item(i).setPosition({ x: offset + itemDimensions.width * i, y: 0 }); else if(menuDirection == Aston.Menus.DropDown.Direction.Vertical) this.menu.getChildNodes().item(i).setPosition({ x: linkPosition, y: 0 }); }; if (parentSelectedClass != null) this.link.getClasses().add(parentSelectedClass); } } catch (ex) { Aston.LogException(ex); }; } function hideMenu(e) { try { if (timer != null) clearTimeout(timer); timer = setTimeout(function () { for (var i = expandedItems.length - 1; i >= 0; i--) { expandedItems[i].link.getClasses().remove(parentSelectedClass); menuContainerNode.getChildNodes().remove(expandedItems[i].menu); expandedItems.pop(); } }, collapseDelay); } catch (ex) { Aston.LogException(ex); }; } // removes all expanded menus from the Dom // NOTE: the root menu is never removed function collapseMenus(e) { for (var i = expandedItems.length - 1; i >= 0; i--) { menuContainerNode.getChildNodes().remove(expandedItems[i].menu); expandedItems.pop(); } } //returns the menu item given by name in submenus given by name. Assumes root start point // eg. [ "subMenuName", "itemName" ] function getItemByNames(names) { if (!(names instanceof Array)) names = [names]; if (names == null) return rootMenu; var menu = rootMenu; for (var i = 0; i < names.length; i++) { var found = false; var j = 0; var items = menu.getChildNodes(); while ((j < items.count()) && (!found)) { if (items.item(j).name == names[i]) { if (i == names.length - 1) menu = items.item(j); else menu = items.item(j).menu; found = true; } j++; } if (!found) return null; } return menu; } //returns the menu item given by name in most recently expended menu // eg. [ "subMenuName", "itemName" ] function getItemByName(name) { if (name == null) return rootMenu; var root = expandedItems.length == 0 ? rootMenu : expandedItems[expandedItems.length - 1]; var result = root.getChildNodes().foreach(function (item) { if (item.name == name) return item; }); return result || null; } function addItems(toAdd, parentMenu) { var context = this; toAdd.foreach(function (element) { if (parentMenu == undefined) { if ((element.parent) && (!String.IsNullOrEmpty(element.parent))) parentMenu = getItemByNames(element.parent) else parentMenu = rootMenu; } var item = new Aston.Dom.Nodes.LI; if ((element.href) && (!String.IsNullOrEmpty(element.href))) { item.link = new Aston.Dom.Nodes.A; item.link.setHref(element.href); // because A nodes disbale this by default to allow for double click and click + hold actions item.link.click(function() { window.document.location.href = element.href; }); } else { item.link = new Aston.Dom.Nodes.SPAN; } item.isImmutable = element.isImmutable; item.link.getClasses().add(element.className); item.name = element.name; item.tooltip = element.tooltip || String.Empty; item.getRank = function () { return parentMenu.rank; }; if(element.enabled == false) item.hide(); //TODO: probably should just remove from DOM if ((element.innerHTML) && (!String.IsNullOrEmpty(element.innerHTML))) { if (spanText) { item.text = new Aston.Dom.Nodes.SPAN; item.link.getChildNodes().add(item.text); } else { item.text = item.link; } item.text.setInnerHTML(decodeURIComponent(element.innerHTML)); } if(!useMiniMode) { // these are outside of the if statement on submenus because mousing over another element should always collapse any previously expanded menu item.link.mouseOver(function () { try { showMenu.call(item); if((tooltipHandler != undefined) && (tooltipHandler != null)) tooltipHandler.setTooltip(item.tooltip); } catch (ex) { Aston.LogException(ex); }; }); item.link.mouseOut(function () { try { hideMenu.call(item); if((tooltipHandler != undefined) && (tooltipHandler != null)) tooltipHandler.clearTooltip(); } catch (ex) { Aston.LogException(ex); }; }); } if ((element.click) && (element.click != null)) { var clickFunction = Aston.Core.getFunction(element.click); var contextFunction = function() { clickFunction.call(context); }; item.link.click(contextFunction); item.link.clickFunction = contextFunction; // so it can be removed later item.link.click(collapseMenus); //This is useful for debugging //item.link.setTitle(clickFunction); } item.link.click(function (e) { try { if(item.getRank() >= expandedItems.length) { collapseMenus.call(item); showMenu.call(item); } else { collapseMenus.call(item); } if (item.link instanceof Aston.Dom.Nodes.A) item.link.blur() Aston.Dom.PreventDefault(e); Aston.Dom.StopPropagation(e); } catch (ex) { Aston.LogException(ex); }; }); if ((element.menu != undefined) && (element.menu.length > 0)) { if (hasChildrenClass != null) item.link.getClasses().add(hasChildrenClass); item.menu = new Aston.Dom.Nodes.UL; item.menu.rank = parentMenu.rank + 1; item.menu.setID(dropdownIDFunc(item.menu.rank)); addItems.call(context, element.menu, item.menu); item.menu.mouseOver(function () { if (timer != null) clearTimeout(timer); }); item.menu.mouseOut(function () { hideMenu(); }); } //if this is immutable, group to the front of this menu(sub menu) item.getChildNodes().add(item.link); if ((item.isImmutable) && (parentMenu.getChildNodes().length > 0)) { var insertionPoint = 0; for (; insertionPoint < parentMenu.getChildNodes().length; insertionPoint++) { if (!parentMenu.getChildNodes()[insertionPoint].isImmutable) break; } parentMenu.getChildNodes().insert(item, insertionPoint + 1); } else { parentMenu.getChildNodes().add(item); } }); } // to remove an entire menu remove its parent item function removeItems(toRemoveData) { toRemoveData.foreach(function (toRemoveNames) { var toRemove = getItemByNames(toRemoveNames); if (toRemove != null) toRemove.removeFromDom(); }); } function updateItems(toUpdateData) { var toAdd = [] toUpdateData.foreach(function (item) { var toUpdate = getItemByNames(item.name); if (toUpdate == null) { toAdd.add(item); } else { if ((item.className) && (!String.IsNullOrEmpty(item.className))) { toUpdate.link.getClasses().clear(); toUpdate.link.getClasses().add(item.className); } if ((item.href) && (!String.IsNullOrEmpty(item.href))) toUpdate.link.setHref(item.href); if(item.enabled == false) toUpdate.hide(); //TODO: probably should just remove from DOM else toUpdate.show(); //TODO: probably should just remove from DOM if ((item.innerHTML) && (!String.IsNullOrEmpty(item.innerHTML))) toUpdate.text.setInnerHTML(decodeURIComponent(item.innerHTML)); toUpdate.tooltip = item.tooltip || String.Empty; if ((item.click) && (item.click != null)) { if (toUpdate.link.clickFunction) toUpdate.link.click().remove(toUpdate.link.clickFunction); var clickFunction = Aston.Core.getFunction(item.click); toUpdate.link.click(clickFunction); toUpdate.link.clickFunction = clickFunction; } } }); addItems(toAdd); } function setMiniMode(menuData) { var mainMenu = null; if(menuData.addItems) mainMenu = menuData.addItems; else if(menuData.menu) mainMenu = menuData.menu; var home = mainMenu[0]; var innerMenu = mainMenu.slice(1); return { menu: [ home, { innerHTML: "Menu ☰", menu: innerMenu } ] } } // CONSTRUCTOR try { if(menuData == null) menuData = {}; // prevent the code below from having to check for null menuContainerNode.setID(options.ID || menuData.ID || Aston.Menus.DropDown.Defaults.ID); menuContainerNode.getClasses().add(Aston.Menus.DropDown.Defaults.className); collapseDelay = options.collapseDelay || menuData.collapseDelay || Aston.Menus.DropDown.Defaults.collapseDelay; menuDirection = options.menuDirection || menuData.menuDirection || Aston.Menus.DropDown.Defaults.menuDirection; hasChildrenClass = options.hasChildrenClass || menuData.hasChildrenClass || Aston.Menus.DropDown.Defaults.hasChildrenClass; parentSelectedClass = options.parentSelectedClass || menuData.parentSelectedClass || Aston.Menus.DropDown.Defaults.parentSelectedClass; spanText = options.spanText || menuData.spanText || Aston.Menus.DropDown.Defaults.spanText; useMiniMode = options.useMiniMode || menuData.useMiniMode || Aston.Menus.DropDown.Defaults.useMiniMode; dropdownIDFunc = options.dropdownIDFunc || menuData.dropdownIDFunc || Aston.Menus.DropDown.Defaults.dropdownIDFunc; if(dropdownIDFunc == menuData.dropdownIDFunc) dropdownIDFunc = new Function(dropdownIDFunc); setPositionFunc = options.setPositionFunc || menuData.setPositionFunc || Aston.Menus.DropDown.Defaults.setPositionFunc; if(setPositionFunc == menuData.setPositionFunc) setPositionFunc = new Function(setPositionFunc); var rootMenu = new Aston.Dom.Nodes.UL; rootMenu.rank = 0; rootMenu.setID(dropdownIDFunc(rootMenu.rank)); menuContainerNode.getChildNodes().add(rootMenu); if(useMiniMode) menuData = setMiniMode(menuData); if(menuData.addItems) addItems(menuData.addItems, rootMenu); else if(menuData.menu) addItems(menuData.menu, rootMenu); // support easier recusion from CreateDataFromDom // release resources menuData = null; } catch (ex) { Aston.LogException(ex); }; }; Aston.Menus.DropDown.Direction = Aston.Enum({ Horizontal : 0, Vertical : 1 }); Aston.Menus.DropDown.Defaults = { ID : String.Empty, className : "Aston_Menus_DropDown", collapseDelay : 1000, dropdownIDFunc : function(rank) { return "Aston_Menus_DropDown_" + rank; }, hasChildrenClass : "hasChildren", parentSelectedClass : "parentSelected", spanText : false, setPositionFunc : function() { return { x : this.getPosition().x, y : 0 }; }, menuDirection : Aston.Menus.DropDown.Direction.Horizontal, useMiniMode : false }; Aston.Menus.DropDown.CreateDataFromDom = function(rootNode, menuTagNames, itemTagNames, linkTagNames) // links can also be SPANs { try { function CreateDataFromDom(data, rootNode) { for(var i = 0; i < rootNode.childNodes.length; i++) { var child = rootNode.childNodes[i]; if(child.nodeType == 1) { if(menuTagNames.contains(child.nodeName)) { data.menu = []; CreateDataFromDom(data.menu, child); } else if(itemTagNames.contains(child.nodeName)) { data.add({}); CreateDataFromDom(data[data.length - 1], child); } else if(linkTagNames.contains(child.nodeName)) { data.innerHTML = encodeURIComponent(child.innerHTML); data.tooltip = child.title; data.className = child.className; data.href = child.href; } } }; } var data = {}; if(menuTagNames.contains(rootNode.nodeName)) { data.addItems = []; CreateDataFromDom(data.addItems, rootNode); } else { CreateDataFromDom(data, rootNode); } return data; } catch (ex) { Aston.LogException(ex); }; } /* TODO: set a maximum fps allow for fps regrowth (atm it can only go down as the average reduces) - consider the mouseover extra regrowth effect store the avg between calls to start and reset - restart with the previous average */ Aston.Namespace("Aston.Gfx"); // A graphics timer to help schedule frames when rendering a moving object Aston.Gfx.Timer = function() { // INNER CLASSES function RollingAverage(rollLength, initialFPS) { var times; var front; this.addNow = function() { if(++front == rollLength) front = 0; times[front] = new Date().getTime(); } this.getAvg = function() { if(times.length < 2) return Math.round(1000 / initialFPS); //get sum //most recent time : times[front] //least recent time: either position times[0] or times[front + 1] var sum; if( (times.length == rollLength) && (front < times.length - 1) ) sum = times[front] - times[front + 1]; else sum = times[front] - times[0]; if(isNaN(sum)) Aston.Log("\t-----\tRollingAverage sum is NaN: times.length = " + times.length + " front = " + front); //Aston.Log(Math.round(1000 / (sum / times.length))); return Math.round(sum / times.length); } this.reset = function() { front = -1; times = []; } } // FIELDS var that = this; var timeout = null; var rollingAvg = new RollingAverage(Aston.Gfx.Timer.Defaults.rollLength, Aston.Gfx.Timer.Defaults.initialFPS); var maxFPS = Math.round(1000 / Aston.Gfx.Timer.Defaults.initialFPS); var isStarted = false; // PUBLIC METHODS //NOTE: the mehod is called with the #remaining frames if a transition time is given or the current framerate if it is not this.start = function(transitionTime, method) { try { that.stop(); isStarted = true; var endTime; if(transitionTime > 0) endTime = new Date().getTime() + transitionTime; var timeRemaining = -1; var framesRemaining = -1; var tick = function() { try { rollingAvg.addNow(); var interval = rollingAvg.getAvg(); if(endTime >= 0) { timeRemaining = endTime - new Date().getTime(); framesRemaining = Math.max(0, Math.floor(timeRemaining / interval)); // max of 0 in case the time remaining is -ve method(framesRemaining); } else { // report as if there were 1 second to go, ie the current framerate method(Math.floor(1000 / interval)); } if((isStarted) && (Math.abs(framesRemaining) > 0)) // NOTE: the Math.Abs here: frameRemaining = -1 will return true on this line // go no quicker than the maxFPS // attempt to regain lost fps, slowly timeout = setTimeout(tick, Math.max(interval - Aston.Gfx.Timer.Defaults.fpsGain, maxFPS)); } catch (ex) { Aston.LogException(ex); }; } tick(); } catch (ex) { Aston.LogException(ex); }; }; this.stop = function() { if(timeout != null) window.clearTimeout(timeout); isStarted = false; rollingAvg.reset(); }; this.isStarted = function() { return isStarted; }; // CONSTRUCTORS try { } catch (ex) { Aston.LogException(ex); }; }; Aston.Gfx.Timer.Defaults = { initialFPS : 60, fpsGain : 2, rollLength : 50 }; Aston.Namespace("Aston.Gfx.Timer.Timeout"); Aston.Gfx.Timer.Timeout.Infitite = -1; Aston.Namespace("Aston.Gfx.Physics"); Aston.Gfx.Physics.CalcAcceleration = function(velocity, force, mass, dragCoefficient) { var result = force; // apply drag coefficient if(velocity > 0) result -= dragCoefficient * Math.pow(velocity, 2) / 2; else if(velocity < 0) result += dragCoefficient * Math.pow(velocity, 2) / 2; // prevent deceleration being greater than initial velocity, can happen with low framerates if(Math.abs(result) > Math.abs(velocity) && force == 0) result = 0 - velocity; // stabalise small numbers when returning to zero motion if(force == 0) { if( (result < Aston.Gfx.Physics.CalcAcceleration.Defaults.linearDrag) && (result > Aston.Gfx.Physics.CalcAcceleration.Defaults.linearDrag * -1) ) { result = velocity * -1; } else { // apply a small linear drag to help cut the volocity to 0 from already low numbers if(velocity > 0) result -= Aston.Gfx.Physics.CalcAcceleration.Defaults.linearDrag; else if(velocity < 0) result += Aston.Gfx.Physics.CalcAcceleration.Defaults.linearDrag; } } // convert force to acceleration result /= mass; // reduce to given precision - ish considering JSs floating point issues result = result.toFixed(Aston.Gfx.Physics.CalcAcceleration.Defaults.precision); return parseFloat(result); }; Aston.Gfx.Physics.CalcAcceleration.Defaults = { precision : 4, linearDrag : 10 }; /** Author: ------- James Hall - www.astonprograms.co.uk Description: ------------ Drop down menu Uses: ----- Aston.Core Aston.Dom TODO: ------------ */ // ********************************************************************************************* // ********************************************************************************************* Aston.Namespace("Aston.Gfx"); Aston.Gfx.Slideshow = function(slidesData, options) { // FIELDS var that = this; options = options || {}; var autoStart = options.autoStart || slidesData.autoStart || Aston.Gfx.Slideshow.Defaults.autoStart; var slideZIndex = options.slideZIndex || slidesData.slideZIndex || Aston.Gfx.Slideshow.Defaults.slideZIndex; var startingSlide = options.startingSlide || slidesData.startingSlide || Aston.Gfx.Slideshow.Defaults.startingSlide; var transitionPause = options.transitionPause || slidesData.transitionPause || Aston.Gfx.Slideshow.Defaults.transitionPause; var transitionTime = options.transitionTime || slidesData.transitionTime || Aston.Gfx.Slideshow.Defaults.transitionTime; var hasTransparency = options.hasTransparency || slidesData.hasTransparency || Aston.Gfx.Slideshow.Defaults.hasTransparency; var slideshow = new Aston.Dom.Nodes.DIV; var slides = []; var timer = new Aston.Gfx.Timer(); var isPlaying = false; var pauseTimer = null; var frontSlide = -1; // set in reset var backSlide = -1;// set in reset // PUBLIC METHODS this.addToDom = function(parentElement, referenceElement) { try { slideshow.addToDom(parentElement, referenceElement); if(autoStart) that.pausePlay(); } catch (ex) { Aston.LogException(ex); }; }; this.update = function(slidesData) { try { that.reset(); if(slidesData.ID) slideshow.setID(slidesData.ID); if(slidesData.transitionPause) transitionPause = slidesData.transitionPause; if(slidesData.transitionTime) transitionTime = slidesData.transitionTime; if(slidesData.addItems) addItems(slidesData.addItems); if(slidesData.removeItems) removeItems(slidesData.removeItems); if(slidesData.updateItems) updateItems(slidesData.updateItems); if(slidesData.reorderItems) reorderItems(slidesData.reorderItems); that.reset(); } catch (ex) { Aston.LogException(ex); }; }; this.pausePlay = function() { try { if(slides.length > 1) { if(pauseTimer != null) window.clearTimeout(pauseTimer); isPlaying = !isPlaying; if(isPlaying) pauseTimer = setTimeout(startTransitions, transitionPause); } } catch (ex) { Aston.LogException(ex); }; } this.reset = function() { try { isPlaying = false; if(slides.length > 0) setSlideByIndex(startingSlide); } catch (ex) { Aston.LogException(ex); }; } this.next = function() { try { if(isPlaying) that.pausePlay(); if(slides.length > 1) setSlideByIndex(frontSlide + 1); } catch (ex) { Aston.LogException(ex); }; } this.previous = function() { try { if(isPlaying) that.pausePlay(); if(slides.length > 1) setSlideByIndex(frontSlide - 1); } catch (ex) { Aston.LogException(ex); }; } //todo: goto slide and pause (for 60 secs?) this.goto = function(index) { if(isPlaying) that.pausePlay(); if(slides.length > 1) setSlideByIndex(index); } // PRIVATE METHODS function getSlideByName(name) { for(var i = 0; i < slides.length; i++) { if(slides[i].name == name) return slides[i]; } return null; } function addItems(toAddData) { toAddData.foreach(function(item) { var slide = new Aston.Dom.Nodes.DIV; slide.setZIndex(slideZIndex); if(item.className) slide.getClasses().add(item.className); if(item.imageURL) { var link = new Aston.Dom.Nodes.A; var image = new Aston.Dom.Nodes.IMG; image.setSrc(item.imageURL); if(item.altText) { image.setAltText(item.altText); image.setTitle(item.altText); } if(item.click) image.click(Aston.Core.getFunction(item.click)); link.getChildNodes().add(image); slide.getChildNodes().add(link); slide.image = image; } if(item.text) { var text = new Aston.Dom.Nodes.DIV; text.setInnerHTML(decodeURIComponent(item.text)); slide.getChildNodes().add(text); slide.text = text; } slide.opacity = 100; slides.add(slide); }); } function removeItems(toRemoveData) { toRemoveData.foreach(function(toRemoveName) { slides.remove(getSlideByNames(toRemoveName)); }); } function updateItems(toUpdateData) { var toAdd = [] toUpdateData.foreach(function(item) { var toUpdate = getSlideByName(item.name); if(toUpdate == null) { toAdd.add(item); } else { //TODO - unlikely to ever happen, jsut remove and re-add //if(!Object.isNullUndefinedOrEmpty(item.src)) // toUpdate.image.setSrc(item.src); if(!Object.isNullUndefinedOrEmpty(item.altText)) { toUpdate.image.setAltText(item.altText); toUpdate.image.setTitle(item.altText); } // TODO: remove existing listenrs first - somehow //if(!Object.isNullUndefinedOrEmpty(item.click)) // image.click(new Function(item.click)); if(!Object.isNullUndefinedOrEmpty(item.text)) toUpdate.text.setInnerHTML(decodeURIComponent(item.text)); } }); addItems(toAdd); } function reorderItems(reorderedIndexes) { if(reorderedIndexes.length != slides.length) throw new Error("reordered sequence does not match current slides"); var reorderedSlides = []; reorderedIndexes.foreach(function(index) { reorderedSlides.add(slides[index]); }); slides = reorderedSlides; } function startTransitions() { timer.start(transitionTime, transitionSlides); } function setSlideByIndex(index) { if(index < 0) // deal with transitions backward index = slides.length - 1; if(frontSlide != index) // because there'd be no point in changing { var front, back; // do not run with that initial position of having nothing on the page if((frontSlide >= 0) && (frontSlide < slides.length)) { front = slides[frontSlide]; // reset and remove front slide slideshow.getChildNodes().remove(front); front.opacity = 100; front.setOpacity(front.opacity); } // set new slide positions frontSlide = index; if(frontSlide >= slides.length) frontSlide = 0; backSlide = frontSlide + 1; if(backSlide >= slides.length) backSlide = 0; // set up new front slide front = slides[frontSlide]; if(front.opacity != 100) { front.opacity = 100; front.setOpacity(front.opacity); } front.setZIndex(slideZIndex + 1); if(!slideshow.getChildNodes().contains(front)) slideshow.getChildNodes().add(front); // set up new back slide - if there's more than one slide if(front != back) { back = slides[backSlide]; setCss(front, back); if((hasTransparency) && (back.opacity != 0)) { back.opacity = 0; back.setOpacity(back.opacity); } back.setZIndex(slideZIndex); if(!slideshow.getChildNodes().contains(back)) slideshow.getChildNodes().add(back); } } } function transitionSlides(framesRemaining) { //Aston.Log("framesRemaining = " + framesRemaining) var back = slides[backSlide]; var front = slides[frontSlide]; setCss(front, back); if(framesRemaining > 0) { front.opacity -= (front.opacity / framesRemaining); if(hasTransparency) back.opacity += ((100 - back.opacity) / framesRemaining); } else { front.opacity = 0; if(hasTransparency) back.opacity = 100; } front.setOpacity(front.opacity); if(hasTransparency) back.setOpacity(back.opacity); if(framesRemaining == 0) { setSlideByIndex(frontSlide + 1); if(isPlaying) pauseTimer = setTimeout(startTransitions, transitionPause); } } function setCss(front, back) { front.getClasses().add("front"); front.getClasses().remove("back"); back.getClasses().add("back"); back.getClasses().remove("front"); } // CONSTRUCTOR try { addItems(slidesData.addItems); this.reset(); // release resources slidesData = null; } catch (ex) { Aston.LogException(ex); }; }; Aston.Gfx.Slideshow.Defaults = { autoStart : true, startingSlide : 0, slideZIndex : 100, // and therefore 101 too transitionPause : 3000, transitionTime : 750, hasTransparency : true }; //TODO Allow for onclick as well as href Aston.Gfx.Slideshow.CreateJSONFromDom = function(rootNode, slideTagNames, textTagNames) // links can also be SPANs { try { function getChildNodes(node) { if(node.hasChildNodes()) { var children = []; for(var i = 0; i < node.childNodes.length; i++) children.add(node.childNodes[i]); var result = children.selectMany(function(child) { return getChildNodes(child); }); result.insert(node, 0); return result; } else { return [ node ]; } }; function CreateDataFromDom(data, rootNode) { for(var i = 0; i < rootNode.childNodes.length; i++) { var child = rootNode.childNodes[i]; if((child.nodeType == 1) && (slideTagNames.contains(child.nodeName))) { var slide = {}; slide.className = child.className; var leaves = getChildNodes(child); //NOTE: Was leaves, isn't now, can't be assed to rename var img = leaves.firstOrDefault(function(leave) { return leave.nodeName == "IMG"; }); if(img != null) { slide.imageURL = img.src; slide.altText = img.alt; } var link = leaves.firstOrDefault(function(leave) { return leave.nodeName == "A"; }); if(link != null) { slide.click = "document.location.href='" + link.href + "'"; } if(textTagNames) { leaves.foreach(function(leave) { if(textTagNames.contains(leave.nodeName)) { if(slide.text) slide.text += leave.outerHTML; else slide.text = leave.outerHTML } }); } data.add(slide); } } } var data = {}; data.addItems = []; CreateDataFromDom(data.addItems, rootNode); return data; } catch (ex) { Aston.LogException(ex); }; }; /** Author: ------- James Hall - www.astonprograms.co.uk Description: ------------ Drop down menu Uses: ----- Aston.Core Aston.Dom TODO: ------------ */ // ********************************************************************************************* // ********************************************************************************************* Aston.Namespace("Aston.Gfx"); Aston.Gfx.Slider = function(slidesData) { // FIELDS var that = this; var startingSlide = slidesData.startingSlide || Aston.Gfx.Slider.Defaults.StartingSlide; var initialVelocity = slidesData.initialVelocity || Aston.Gfx.Slider.Defaults.InitialVelocity; var dragCoefficient = slidesData.dragCoefficient || Aston.Gfx.Slider.Defaults.DragCoefficient; var cruiseForce = slidesData.cruiseForce || Aston.Gfx.Slider.Defaults.CruiseForce; var slider = new Aston.Dom.Nodes.DIV; var slides = []; var totalSlidesWidth = 0; var timer = new Aston.Gfx.Timer(); var offset = 0; var velocity = 0; var actingForce = 0; var sliding = false; var previousMousePosition = null; // PUBLIC FUNCTIONS this.addToDom = function(parentElement, referenceElement) { try { slider.addToDom(parentElement, referenceElement); // for some reason this can be 0 - perhaps because it hasn't really added yet? slider.width = slider.getDimensions(true).width; //ignoreMargins setSlideDimensions(); slide(); // initial slide adds the slide to the dom actingForce = cruiseForce; velocity = initialVelocity; startSliding(); } catch (ex) { Aston.LogException(ex); }; }; this.applyForce = function(force) { force = force || 0; if(force == 0) actingForce = cruiseForce; else actingForce = force; startSliding(); }; this.update = function(slidesData) { try { if(slidesData.ID) slider.setID(slidesData.ID); if(slidesData.addItems) addItems(slidesData.addItems); if(slidesData.removeItems) removeItems(slidesData.removeItems); if(slidesData.updateItems) updateItems(slidesData.updateItems); if(slidesData.reorderItems) reorderItems(slidesData.reorderItems); } catch (ex) { Aston.LogException(ex); }; }; // PRIVATE FUNCTIONS function setSlideDimensions() { // all slides need to be added to the dom to calculate their dimensions //slides.forEach(function(slide) slides.foreach(function(slide) // fix for ie 8 :( { slider.getChildNodes().add(slide); }); totalSlidesWidth = 0; for(var i = 0; i < slides.length; i++) { if(i == 0) slides[i].left = 0; else slides[i].left = slides[i - 1].left + slides[i - 1].width; slides[i].width = slides[i].getDimensions().width; totalSlidesWidth += slides[i].width; }; //slides.forEach(function(slide) slides.foreach(function(slide) // fix for ie 8 :( { slider.getChildNodes().remove(slide); }); } function getSlideByName(name) { return slides.firstOrDefault(function(slide) { return slide.name == name; }); } //TODO: add insert function addItems(toAddData) { var initialLength = slides.count(); toAddData.foreach(function(item) { var slide = new Aston.Dom.Nodes.DIV; if(item.className) slide.getClasses().add(item.className); var link = new Aston.Dom.Nodes.A; var image = new Aston.Dom.Nodes.IMG; image.setSrc(item.imageURL); if(item.altText) { image.setAltText(item.altText); image.setTitle(item.altText); } if(item.click) image.click(Aston.Core.getFunction(item.click)); link.getChildNodes().add(image); slide.getChildNodes().add(link); slide.image = image; slides.add(slide); }); if(slider.isAddedToDom()) setSlideDimensions(); } function removeItems(toRemoveData) { toRemoveData.foreach(function(toRemoveName) { slides.remove(getSlideByNames(toRemoveName)); }); if(slider.isAddedToDom()) setSlideDimensions(); } function updateItems(data) { var toAdd = [] data.foreach(function(item) { var toUpdate = getSlideByName(item.name); if(toUpdate == null) { toAdd.add(item); } else { if(item.altText) { toUpdate.image.setAltText(item.altText); toUpdate.image.setTitle(item.altText); } } }); addItems(toAdd); } function reorderItems(reorderedIndexes) { //NOTE: slides is not an array but a childNodes if(reorderedIndexes.length != slides.count()) throw new Error("reordered sequence does not match current slides"); var reorderedSlides = []; reorderedIndexes.foreach(function(index) { reorderedSlides.add(slides.item(index)); }); slides.clear(); reorderedSlides.foreach(function(slide) { slides.add(slide); }); if(slider.isAddedToDom()) setSlideDimensions(); } function slide(frameRate) { frameRate = frameRate || 1; var dVelocity = Aston.Gfx.Physics.CalcAcceleration((velocity / frameRate), (actingForce / frameRate), 1, dragCoefficient); velocity += dVelocity; //Aston.Log("actingForce = " + actingForce + " velocity = " + velocity + " dVelocity " + dVelocity + " frameRate = " + frameRate); if(dVelocity > 0) offset = offset + Math.ceil(velocity); else offset = offset + Math.floor(velocity); // wrap offset if(offset > totalSlidesWidth) offset -= totalSlidesWidth; if(offset < 0) offset += totalSlidesWidth; // where [] is a slide // where {} is the view // one view // [],{[],[],[]},[],[] var views = []; if(offset + slider.width <= totalSlidesWidth) { // single view of the items views.add({from: offset, to: offset + slider.width}); } else { // split view of the items // {[]},[],[],[],{[],[]} views.add({from: offset, to: totalSlidesWidth}); views.add({from: 0, to: (offset + slider.width) - totalSlidesWidth}); } // remove all slides from view - we'd want to to speed up scrolling anyway slides.foreach(function(slide) { slider.getChildNodes().remove(slide); }); // if a slide is in view then add it to the slider at the correct location for(var i = 0; i < views.length; i++) { var view = views[i]; slides.foreach(function(slide) { if(slide.left + slide.width >= view.from && slide.left <= view.to) { // is in view var previewViewWidth = 0; if(i > 0) previewViewWidth = views[i - 1].to - views[i - 1].from; slide.setPosition({x: previewViewWidth + slide.left - view.from, y : 0}); slider.getChildNodes().add(slide); } }); }; // added round to take care of floating point precision errors if((Math.round(velocity * 100) == 0) && (Math.round(actingForce) == 0)) // * 100 gives us a little more precision { timer.stop(); sliding = false; } } function startSliding() { if((!sliding) && (totalSlidesWidth > slider.width)) { sliding = true; timer.start(Aston.Gfx.Timer.Timeout, slide); } } function stopSliding() { if(sliding) { timer.stop(); velocity = 0; sliding = false; } } // CONSTRUCTOR try { this.update(slidesData); slider.mouseEnter(function(event) { stopSliding(); }, false); slider.mouseLeave(function(event) { previousMousePosition = null; // to avoid odd numbers when re-entering startSliding(); }, false); slider.mouseMove(function(event) { var mousePosition = Aston.Dom.GetMousePosition(event); var now = new Date().getTime(); if(previousMousePosition != null) { //var dX = previousMousePosition.x - mousePosition.x; var dX = mousePosition.x - previousMousePosition.x ; // so you can mouse between items easier var dTime = Math.min(previousMousePosition.time - now, -1); offset += dX; velocity = 0; //negate drag slide(); velocity = dX / dTime * -1;//allow sliding after mousing out } //TODO: does this cause problems at midnight? previousMousePosition = { x: mousePosition.x, y: mousePosition.y, time : now }; }); } catch (ex) { Aston.LogException(ex); }; }; Aston.Gfx.Slider.Defaults = { StartingSlide : 0, InitialVelocity : 10, DragCoefficient : 15, CruiseForce : 0 }; Aston.Gfx.Slider.CreateDataFromDom = function(rootNode) // only works on img tags { try { var slideTagNames = [ "IMG" ]; function CreateDataFromDom(data, rootNode) { for(var i = 0; i < rootNode.childNodes.length; i++) { var child = rootNode.childNodes[i]; if((child.nodeType == 1) && (slideTagNames.contains(child.nodeName))) { data.add( { className: child.className, imageURL: child.src, altText: child.alt, onclick: child.getAttribute('onclick') }); } if(child.hasChildNodes()) CreateDataFromDom(data, child); } } var data = { addItems: [] }; CreateDataFromDom(data.addItems, rootNode); return data; } catch (ex) { Aston.LogException(ex); }; }; /** Author: ------- James Hall - www.astonprograms.co.uk Description: ------------ AJAX Connection Uses: ----- Methods: -------- TODO: ----- */ // ********************************************************************************************* // ********************************************************************************************* //mypostrequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded") Aston.Namespace("Aston.Arc"); Aston.Arc.AJAX = function(serverLocation, method, contentType) { // FIELDS var that = this; var httpRequest = null; var onReceive = new Aston.Core.Event(); // PRIVATE METHODS var onChange = function() { if (httpRequest.readyState === 4) { onReceive.fire(httpRequest.responseText); Aston.Arc.AJAX.OnAnyReceive.fire(httpRequest.responseText); httpRequest.abort(); // Clear xmlhttp-session to avoid exception error NS_ERROR_NOT_INITIALIZED } } // PUBLIC METHOD this.request = function(content) { try { if(content) httpRequest.send(content); else httpRequest.send(null); } catch (ex) { Aston.LogException(ex); }; } this.onReceive = function(method, onCapture) { return method == undefined ? onReceive : onReceive.add(method, onCapture); }; // CONSTRUCTOR try { if (window.XMLHttpRequest) httpRequest = new XMLHttpRequest(); else if (window.ActiveXObject) httpRequest = new ActiveXObject("Microsoft.XMLHTTP"); httpRequest.onreadystatechange = onChange; if((method == undefined) || (method == null)) method = Aston.Arc.AJAX.Methods.GET; httpRequest.open(method, serverLocation, true); if((contentType == undefined) || (contentType == null)) contentType = Aston.Arc.AJAX.ContentTypes.JSON; switch(contentType) { case Aston.Arc.AJAX.ContentTypes.JSON: httpRequest.setRequestHeader("Content-Type", "application/json"); break; case Aston.Arc.AJAX.ContentTypes.Form: httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); break; case Aston.Arc.AJAX.ContentTypes.HTML: httpRequest.setRequestHeader("Content-Type", "text/html"); break; } } catch (ex) { Aston.LogException(ex); }; } Aston.Arc.AJAX.Methods = Aston.Enum({ GET : 0, POST: 1, PUT: 1 }); Aston.Arc.AJAX.ContentTypes = Aston.Enum({ JSON : 0, Form: 1, HTML: 2 }); Aston.Arc.AJAX.Defaults = { mode : Aston.Arc.AJAX.ContentTypes.JSON }; Aston.Arc.AJAX.OnAnyReceive = new Aston.Core.Event(); Aston.ServerCombinedFiles = [{ fileName : 'Aston.Arc.Resources.JavaScript.Aston.Core.js', lineNumber : 0 },{ fileName : 'Aston.Arc.Resources.JavaScript.Aston.Dom.js', lineNumber : 979 },{ fileName : 'Aston.Arc.Resources.JavaScript.Aston.Dom.Node.js', lineNumber : 1254 },{ fileName : 'Aston.Arc.Resources.JavaScript.Aston.Dom.Element.js', lineNumber : 1449 },{ fileName : 'Aston.Arc.Resources.JavaScript.Aston.Dom.Event.js', lineNumber : 2078 },{ fileName : 'Aston.Arc.Resources.JavaScript.Aston.Core.Keyboard.js', lineNumber : 2341 },{ fileName : 'Aston.Arc.Resources.JavaScript.Aston.Dom.Nodes.js', lineNumber : 2686 },{ fileName : 'Aston.Arc.Resources.JavaScript.Aston.Dom.Components.js', lineNumber : 3088 },{ fileName : 'Aston.Arc.Resources.JavaScript.Aston.Dom.Selection.js', lineNumber : 3772 },{ fileName : 'Aston.Arc.Resources.JavaScript.Aston.Menus.DropDown.js', lineNumber : 3853 },{ fileName : 'Aston.Arc.Resources.JavaScript.Aston.Gfx.js', lineNumber : 4557 },{ fileName : 'Aston.Arc.Resources.JavaScript.Aston.Gfx.Slideshow.js', lineNumber : 4764 },{ fileName : 'Aston.Arc.Resources.JavaScript.Aston.Gfx.Slider.js', lineNumber : 5254 },{ fileName : 'Aston.Arc.Resources.JavaScript.Aston.Arc.AJAX.js', lineNumber : 5674 }];