// SMEnvironment /// /// Sitemagic environment information /// SMEnvironment = function() { } /// /// Returns path to files directory /// SMEnvironment.GetFilesDirectory = function() { return ((window.SMClientEnvironmentInfo !== undefined) ? SMClientEnvironmentInfo.Dirs.Files : "files"); } /// /// Returns path to data directory /// SMEnvironment.GetDataDirectory = function() { return ((window.SMClientEnvironmentInfo !== undefined) ? SMClientEnvironmentInfo.Dirs.Data : "data"); } /// /// Returns path to images directory /// SMEnvironment.GetImagesDirectory = function() { return ((window.SMClientEnvironmentInfo !== undefined) ? SMClientEnvironmentInfo.Dirs.Images : "images"); } /// /// Returns path to templates directory /// SMEnvironment.GetTemplatesDirectory = function() { return ((window.SMClientEnvironmentInfo !== undefined) ? SMClientEnvironmentInfo.Dirs.Templates : "templates"); } /// /// Returns path to extensions directory /// SMEnvironment.GetExtensionsDirectory = function() { return ((window.SMClientEnvironmentInfo !== undefined) ? SMClientEnvironmentInfo.Dirs.Extensions : "extensions"); } /// /// Returns flag indicating whether current site is a subsite /// SMEnvironment.IsSubSite = function() { return ((window.SMClientEnvironmentInfo !== undefined) ? SMClientEnvironmentInfo.IsSubSite : false); } // SMLanguageHandler /// /// Language support for client API /// SMLanguageHandler = function() { } /// /// Returns translation for specified language string - returns empty string if not found /// Specify language key for desired translation /// SMLanguageHandler.GetTranslation = function(translation) { return SMStringUtilities.UnicodeDecode(((SMClientLanguageStrings[translation] !== undefined) ? SMClientLanguageStrings[translation] : "")); } // SMStringUtilities /// /// String manipulation functionality not provided natively by JavaScript /// function SMStringUtilities() { } /// /// Returns True if string starts with specified search expression, otherwise False /// String to search /// String to search for /// SMStringUtilities.StartsWith = function(str, search) { return (str.indexOf(search) === 0); } /// /// Returns True if string ends with specified search expression, otherwise False /// String to search /// String to search for /// SMStringUtilities.EndsWith = function(str, search) { return (str.length >= search.length && str.substring(str.length - search.length) === search); } /// /// Returns provided string excluding whitespaces in beginning and end of string /// String to trim /// SMStringUtilities.Trim = function(str) { var whitespaces = new Array(" ", "\n", "\r", "\t"); var changed = true; while (changed === true) { changed = false; for (var i = 0 ; i < whitespaces.length ; i++) { while (str.substring(0, 1) === whitespaces[i]) { str = str.substring(1, str.length); changed = true; } while (str.substring(str.length - 1, str.length) === whitespaces[i]) { str = str.substring(0, str.length - 1); changed = true; } } } return str; } /// /// Replace all occurences of given value within a string and return the result /// String to replace within /// String to replace /// Replacement string /// SMStringUtilities.ReplaceAll = function(str, search, replace) { return str.split(search).join(replace); } /// /// Replace single occurence of given value after a given offset within a string, and return the result /// String to replace within /// String to replace /// Replacement string /// Perform replacement after specified offset /// SMStringUtilities.Replace = function(str, search, replace, offset) { var startText = str.substring(0, offset); var endText = str.substring(offset); endText = endText.replace(search, replace); return startText + endText; } /// /// /// Encodes Unicode/UTF-8 characters into HEX entities. For instance the Euro symbol becomes &#8364;. /// /// String to encode /// SMStringUtilities.UnicodeEncode = function(str) // Also works with Windows-1252 (HTML5 document) - encodes e.g. Euro symbol as expected { // Browsers convert characters not compatible with document encoding into HEX entities. // Unfortunately there are two drawbacks: // 1) Windows-1252 specific characters are not being encoded into HEX entities when the // document encoding is set to ISO-8859-1, because the browser assumes we want Windows-1252. // http://en.wikipedia.org/wiki/Windows-1252: // "Most modern web browsers and e-mail clients treat the MIME charset ISO-8859-1 as Windows-1252" // 2) Conversion only takes place on post back, meaning we cannot get the value client side that // gets posted to the server. // This function allows us to encode characters not part of ISO-8859-1 into HEX entities client side. // Encode characters that are not between code point 0 (Unicode HEX 0000) and code point 255 (Unicode HEX 0100). // Notice that although Windows-1252 specific characters are found between code point 128 and 159 in the // Windows-1252 character table (http://en.wikipedia.org/wiki/Windows-1252), the browser internally handles // characters as Unicode, which is the reason why the code below works - Windows-1252 specific characters // are found outside of code point 0 - 255 in Unicode. // Unicode/UTF-8 is backward compatible with ASCII, meaning code point 128-159 is empty - so no // Unicode specific characters in this space is left unhandled. // Also be aware that JavaScript (ECMAScript prior to version 6) have problems dealing with Unicode Characters // outside of Basic Multilingual Plane (BMP). Fortunately BMP covers all the popular character sets. However, // popular symbols like Emojis may not work as expected. The following article describes it well: // https://mathiasbynens.be/notes/javascript-unicode // A really simple example of how JavaScript incorrectly handles symbols outside of BMP is to evaluate // e.g. "x".length in a browser running ECMAScript 5. Replace "x" with the symbol "CLOSED LOCK WITH KEY": // https://unicode-table.com/en/search/?q=CLOSED+LOCK+WITH+KEY // Rather than returning a length of 1, it will return a length of 2. // Because of this, the replace logic below will cause the callback to be invoked twice for symbols // outside of BMP. "CLOSED LOCK WITH KEY" will produce �� (Surrogate pair). This will be turned // back to the "CLOSED LOCK WITH KEY" symbol if injected into an Input field, but unfortunately not if injected // into an ordinary DOM element such as a . Instead it displays two question marks. // The lock symbol can actually be represented by a HEX entity (🔐) that works when injected into // the DOM, but that on the other hand will not work when injection into an Input field. // Basically it's a problem that JS and DOM has different representations for the same thing. So supporting // characters outside of BMP is not realistic when using HEX entities to represent Unicode characters - at // least not with ECMAScript prior to version 6. //return str.replace(/[^\u0000-\u0100]/g, function(character) { /*console.log("Encoding: " + character);*/ return "&#" + character.charCodeAt(0) + ";" }); // Encodes all characters extending ASCII - ASCII is compatible with UTF-8, ISO-8859-1 is not. return str.replace(/[^\x00-\x7F]/g, function(character) { /*console.log("Encoding: " + character);*/ return "&#" + character.charCodeAt(0) + ";" }); // The example below encodes Windows-1252 specific characters only. // The Unicode code points are found on the Windows-1252 character table on http://en.wikipedia.org/wiki/Windows-1252 // Notice how Windows-1252 specific characters are highlighted with thick green borders. // A string consisting of these characters are found in this pastebin: http://pastebin.com/ZR2SW2dL //return str.replace(/[\u20AC|\u201A|\u0192|\u201E|\u2026|\u2020|\u2021|\u02C6|\u2030|\u0160|\u2039|\u0152|\u017D|\u2018|\u2019|\u201C|\u201D|\u2022|\u2013|\u2014|\u02DC|\u2122|\u0161|\u203A|\u0153|\u017E|\u0178]/g, function(character) { console.log("Encoding: " + character); return "&#" + character.charCodeAt(0) + ";" }); } /// /// /// Decodes string containing Unicode HEX entities into ordinary text and returns the result. /// /// String to decode /// SMStringUtilities.UnicodeDecode = function(str) { return str.replace(/&#\d+;/g, function(entity) // Match &# followed by one or more digits followed by semicolon. (?<=&#)\d+(?=;) would be better, but JS only supports Look Ahead, so let's just substring &# and the semicolon away. { /*console.log("Decoding: " + entity);*/ return String.fromCharCode(entity.substring(2, entity.length - 1)); }); } // SMColor function SMColor() { } /// /// Convert RGB colors into HEX color string - returns null in case of invalid RGB values /// Color index for red /// Color index for green /// Color index for blue /// SMColor.RgbToHex = function(r, g, b) { if (typeof(r) !== "number" || typeof(g) !== "number" || typeof(b) !== "number") return null; if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) return null; var rHex = r.toString(16); var gHex = g.toString(16); var bHex = b.toString(16); return ("#" + ((rHex.length === 1) ? "0" : "") + rHex + ((gHex.length === 1) ? "0" : "") + gHex + ((bHex.length === 1) ? "0" : "") + bHex).toUpperCase(); } /// /// Convert HEX color string into RGB color object, e.g. { Red: 150, Green: 30, Blue: 185 } - returns null in case of invalid HEX value /// HEX color string, e.g. #C0C0C0 (hash symbol is optional) /// SMColor.ParseHex = function(hex) { var result = hex.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i); if (result !== null) return { Red: parseInt(result[1], 16), Green: parseInt(result[2], 16), Blue: parseInt(result[3], 16) }; return null; } /// /// /// Parses RGB(A) from string and turns result into RGB(A) color object, e.g. /// { Red: 100, Green: 100, Blue: 100, Alpha: 0.3 } - returns null in case of invalid value. /// /// RGB(A) color string, e.g. rgba(100, 100, 100, 0.3) or simply 100,100,200,0.3 /// SMColor.ParseRgb = function(val) { // Parse colors from rgb[a](r, g, b[, a]) string var result = val.match(/\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(\s*,\s*(\d*.*\d+))*/); // http://regex101.com/r/rZ7rO2/9 if (result === null) return null; var c = {}; c.Red = parseInt(result[1]); c.Green = parseInt(result[2]); c.Blue = parseInt(result[3]); c.Alpha = ((result[5] !== undefined) ? parseFloat(result[5]) : 1.00); return c; } // SMCore /// /// Features extending the capabilities of native JavaScript /// function SMCore() { } /// /// /// Iterates through elements in array and passes each value to the provided callback function. /// /// Array containing values to iterate through /// /// Callback function accepting values from the array, passed in turn. /// Return False from callback to break loop. /// /// /// /// /// Iterates through object properties and passes each property name to the provided callback function. /// /// Object containing properties to iterate through /// /// Callback function accepting properties from the object, passed in turn. /// Return False from callback to break loop. /// /// SMCore.ForEach = function(obj, callback) { if (obj instanceof Array || typeof(obj.length) === "number") // Array or DOMNodeList { for (var i = 0 ; i < obj.length ; i++) if (callback(obj[i]) === false) break; } else // Object { for (var i in obj) if (callback(i) === false) break; } } /// /// /// Iterates through given array to find specified object or /// value, and return its index. Returns -1 if entry could not be found. /// /// Array to search for given value or object /// Object or value to find index for /// SMCore.GetIndex = function(arr, obj) { if (arr instanceof Array) { for (var i = 0 ; i < arr.length ; i++) if (SMCore.IsEqual(arr[i], obj) === true) return i; } return -1; } /// /// /// Clone JavaScript object. Supported object types and values: /// String, Number, Boolean, Date, Array, (JSON) Object, Function, Undefined, Null, NaN. /// Variables defined as undefined are left out of clone, /// since an undefined variable is equal to a variable defined as undefined. /// Notice that Arrays and Objects can contain supported object types and values only. /// Functions are considered references, and as such the cloned object will reference /// the same functions. /// Custom properties set on native JS objects (e.g. Array.XYZ) are not cloned, only /// values are. Naturally custom (JSON) objects will be fully cloned, including all /// properties. Both arrays and custom (JSON) objects are cloned recursively. /// Be aware of self referencing variables and circular structures, which /// will cause an infinite loop, and eventually a stack overflow exception. /// DOM objects and window/frame instances are not supported. /// /// JS object to clone /// SMCore.Clone = function(obj) { // TODO - Known problem: // var a = new SomeClass(); // var b = (a instanceOf SomeClass); // var c = (SMCore.Clone(a) instanceOf SomeClass); // Variable b is True as expected, while variable c is False! // TODO: Restore/preserve support for instanceof! // TEST CASE: Example below is supposed to return: TRUE! /*var f1 = function() { alert("Hello"); } var x = { str: "Hello world", num: 123, dec: 123.321, date: new Date("2014-12-01 13:02:23"), bool: true, bool2: false, arr: [100, 200, 250, 400], arr2: ["Hello", "world"], arr3: [123, "hello", true, false, new Date("1990-01-20"), [1,2,3], { x: { "hapsen": f1, "hello": new Array(1,2,3) } }], obj: { a: 123, b: 123.321, c: true, d: false, e: new Date("1993-06-25"), f: "hello", g: null, h: undefined } }; var y = SMCore.Clone(x); console.log("Is equal: " + SMCore.IsEqual(x, y));*/ // Clone object by serializing it into a JSON string, and parse it back into a JS object var serialized = JSON.stringify(obj); // Returns undefined if obj is either undefined or a function (these are not serialized) var clone = ((serialized !== undefined) ? JSON.parse(serialized) : serialized); // parse(..) throws error if argument is undefined // Fixes // - Dates are serialized into strings - turn back into Date instances. // - Functions are not serialized (discarded) - add function reference to clone // - Number variables with a value of NaN is serialized into Null - convert to NaN var fixClone = null; fixClone = function(org, clo) { if (org instanceof Date) // Dates are turned into string representations - turn back into Date instances { return new Date(org.getTime()); } else if (typeof(org) === "function") // Functions are not serialized - use same reference as original object { return org; } else if (typeof(org) === "number" && isNaN(org) === true) // NaN is turned into Null - turn back into NaN { return parseInt(""); } else if (org && typeof(org) === "object") // Recursively fix children (object/array) { for (var p in org) clo[p] = fixClone(org[p], clo[p]); } return clo; }; clone = fixClone(obj, clone); // Done, clone is now identical to original object - SMCore.IsEqual(obj, clone) should return True return clone; } /// /// /// Compare two JavaScript objects to determine whether they are identical. /// Returns True if objects are identical (equal), otherwise False. /// Supported object types and values: /// String, Number, Boolean, Date, Array, (JSON) Object, Function, Undefined, Null, NaN. /// Notice that Arrays and Objects can contain supported object types and values only. /// Functions are compared by reference, not by value. /// Custom properties set on native JS objects (e.g. Array.XYZ) are not compared, only /// values are. Naturally custom (JSON) objects will be fully compared, including all /// properties. Both arrays and custom (JSON) objects are compared recursively. /// Be aware of self referencing variables and circular structures, which /// will cause an infinite loop, and eventually a stack overflow exception. /// DOM objects and window/frame instances are not comparable. /// /// JS object to compare agains second JS object /// JS object to compare agains first JS object /// SMCore.IsEqual = function(jsObj1, jsObj2) { // TEST CASE: Example below is supposed to return: TRUE! /*var f1 = function() { alert("Hello"); } var f2 = f1; SMCore.IsEqual( { str: "Hello world", num: 123, dec: 123.321, date: new Date("2014-12-01 13:02:23"), bool: true, bool2: false, arr: [100, 200, 250, 400], arr2: ["Hello", "world"], arr3: [123, "hello", true, false, new Date("1990-01-20"), [1,2,3], { x: { "hapsen": f1, "hello": new Array(1,2,3) } }], obj: { a: 123, b: 123.321, c: true, d: false, e: new Date("1993-06-25"), f: "hello", g: null, h: undefined } }, { str: "Hello world", num: 123, dec: 123.321, date: new Date("2014-12-01 13:02:23"), bool: true, bool2: false, arr: [100, 200, 250, 400], arr2: ["Hello", "world"], arr3: [123, "hello", true, false, new Date("1990-01-20"), [1,2,3], { x: { "hapsen": f2, "hello": new Array(1,2,3) } }], obj: { a: 123, b: 123.321, c: true, d: false, e: new Date("1993-06-25"), f: "hello", g: null, h: undefined } });*/ if (typeof(jsObj1) !== typeof(jsObj2)) return false; if ((jsObj1 === undefined && jsObj2 === undefined) || (jsObj1 === null && jsObj2 === null)) { return true; } else if (typeof(jsObj1) === "string" || typeof(jsObj1) === "boolean") { return (jsObj1 === jsObj2); } else if (typeof(jsObj1) === "number") { if (isNaN(jsObj1) === true && isNaN(jsObj2) === true) // NaN variables are not comparable! return true; else return (jsObj1 === jsObj2); } else if (jsObj1 instanceof Date && jsObj2 instanceof Date) { return (jsObj1.getTime() === jsObj2.getTime()); } else if (jsObj1 instanceof Array && jsObj2 instanceof Array) { if (jsObj1.length !== jsObj2.length) return false; for (var i = 0 ; i < jsObj1.length ; i++) { if (SMCore.IsEqual(jsObj1[i], jsObj2[i]) === false) return false; } return true; } else if (typeof(jsObj1) === "object" && typeof(jsObj2) === "object" && jsObj1 !== null && jsObj2 !== null) // typeof(null) returns "object" { for (var k in jsObj1) if (SMCore.IsEqual(jsObj1[k], jsObj2[k]) === false) return false; return true; } else if (typeof(jsObj1) === "function" && typeof(jsObj2) === "function") { // Returns True in the following situation: // var f1 = function() { alert("Hello"); } // var f2 = f1; // SMCore.IsEqual(f1, f2); // Returns False in the following situation: // var f1 = function() { alert("Hello"); } // var f2 = function() { alert("Hello"); } // SMCore.IsEqual(f1, f2); return (jsObj1 === jsObj2); } return false; } // SMDom /// /// DOM (Document Object Model) manipulation and helper functionality /// function SMDom() { } /// /// Add CSS class to element if not already found /// Element on which CSS class is to be added /// CSS class name /// SMDom.AddClass = function(elm, cls) { if (SMDom.HasClass(elm, cls) === false) elm.className += ((elm.className !== "") ? " " : "") + cls; } /// /// Remove CSS class from element if found /// Element from which CSS class is to be removed /// CSS class name /// SMDom.RemoveClass = function(elm, cls) { var arr = elm.className.split(" "); var newCls = ""; SMCore.ForEach(arr, function(item) { if (item !== cls) newCls += ((newCls !== "") ? " " : "") + item; }); elm.className = newCls; } /// /// Check whether given DOMElement has specified CSS class registered - returns True if found, otherwise False /// Element for which CSS class may be registered /// CSS class name /// SMDom.HasClass = function(elm, cls) { var arr = elm.className.split(" "); var found = false; SMCore.ForEach(arr, function(item) { if (item === cls) { found = true; return false; // Stop loop } }); return found; } /// /// /// Get style value applied after stylesheets have been loaded. /// An empty string may be returned if style has not been defined, or Null if style does not exist. /// Element which contains desired CSS style value /// CSS style property name /// SMDom.GetComputedStyle = function(elm, style) { var res = null; if (window.getComputedStyle) { res = window.getComputedStyle(elm)[style]; } else if (elm.currentStyle) { res = elm.currentStyle[style]; } return (res !== undefined ? res : null); } /// /// Returns inner HTML value of DOM element with specified ID - Null if not found /// Unique element ID /// SMDom.GetInnerValue = function(elmId) { var elm = this.GetElement(elmId); if (elm === null) return null; return elm.innerHTML; } /// /// /// Set attribute value on specified DOM element. /// This function is used to ensure that attributes are handled identically by different browsers. /// Returns True on success, otherwise False. /// /// Unique element ID /// Attribute name /// Attribute value /// SMDom.SetAttribute = function(elmId, attr, value) { var elm = this.GetElement(elmId); if (elm === null) return false; // Firefox/Safari changes the value of the 'value' and 'checked' attributes, not the visible // values which IE does. This makes sure the actual values are changed for all browsers. if (attr.toLowerCase() === "value") elm.value = value; else if (attr.toLowerCase() === "checked") elm.checked = ((value === "true") ? true : false); else elm.setAttribute(attr, value); return true; } /// /// /// Returns given attribute value for specified DOM element. /// This function is used to ensure that attributes are handled identically /// by different browsers. Returns attribute value on success, otherwise Null. /// /// Unique element ID /// Attribute name /// SMDom.GetAttribute = function(elmId, attr) { var elm = this.GetElement(elmId); if (elm === null) return null; // Firefox/Safari returns the value of the 'value' and 'checked' attributes, not the visible // values which IE does. This makes sure the actual values are returned for all browsers. if (attr.toLowerCase() === "value") return elm.value; else if (attr.toLowerCase() === "checked") return ((elm.checked === true) ? "true" : "false"); return elm.getAttribute(attr); } /// /// /// Set style property on specified DOM element. /// This function is used to ensure that style properties are properly applied /// by different browsers. Returns True on success, otherwise False. /// /// Unique element ID /// Style property name /// Property value /// SMDom.SetStyle = function(elmId, property, value) { var elm = this.GetElement(elmId); if (elm === null) return false; if (SMBrowser.GetBrowser() === "MSIE" && SMBrowser.GetVersion <= 7) // IE8 properly supports the display property if a DocType is set, which we assume it is { if (property.toLowerCase() === "display" && (value === "inherit" || value === "inline-table" || value === "run-in" || value === "table" || value === "table-caption" || value === "table-cell" || value === "table-column" || value === "table-column-group" || value === "table-row" || value === "table-row-group")) value = "block"; } elm.style[property] = value; return true; } /// /// Returns given style property for specified DOM element if found, otherwise Null /// Unique element ID /// Style property name /// SMDom.GetStyle = function(elmId, property) { var elm = this.GetElement(elmId); if (elm === null) return null; return elm.style[property]; } /// /// Returns True if specified DOM element exists, otherwise False /// Unique element ID /// SMDom.ElementExists = function(id) { var elm = document.getElementById(id); if (elm !== null) return true; else return false; } /// /// Returns specified DOM element if found, otherwise returns Null /// Unique element ID /// SMDom.GetElement = function(id) { var elm = document.getElementById(id); if (elm === null) { //alert("Unable to get element '" + id + "' - not found"); return null; } return elm; } /// /// Wraps element in container element while preserving position in DOM /// Element to wrap /// Container to wrap element within /// SMDom.WrapElement = function(elementToWrap, container) { var parent = elementToWrap.parentNode; var nextSibling = elementToWrap.nextSibling; container.appendChild(elementToWrap); // Causes elementToWrap to be removed from existing container if (nextSibling === null) parent.appendChild(container); else parent.insertBefore(container, nextSibling); } // SMEventHandler /// /// Event handler functionality /// function SMEventHandler() { } SMEventHandler.Internal = {}; SMEventHandler.Internal.PageLoaded = false; /// /// Registers handler for specified event on given DOMElement /// DOMElement on to which event handler is registered /// Event name without 'on' prefix (e.g. 'load', 'mouseover', 'click' etc.) /// JavaScript function delegate /// SMEventHandler.AddEventHandler = function(element, event, eventFunction) { if (element.addEventListener) // W3C { element.addEventListener(event, eventFunction, false); // false = event bubbling (reverse of event capturing) } else if (element.attachEvent) // IE { if (event.toLowerCase() === "domcontentloaded" && SMBrowser.GetBrowser() === "MSIE" && SMBrowser.GetVersion() <= 8) { // DOMContentLoaded not supported on IE8. // Using OnReadyStateChange to achieve similar behaviour. element.attachEvent("onreadystatechange", function(e) { if (element.readyState === "complete") { eventFunction(e); // NOTICE: Event argument not identical to argument passed to modern browsers using the real DOMContentLoaded event! } }); } else { element.attachEvent("on" + event, eventFunction); } } // Fire event function for onload event if document in window/iframe has already been loaded. // Notice that no event argument is passed to function since we don't have one. if (event.toLowerCase() === "load" && element.nodeType === 9 && element.readyState === "complete") // Element is a Document (window.document or iframe.contentDocument) eventFunction(); else if (event.toLowerCase() === "load" && element.contentDocument && element.contentDocument.readyState === "complete") // Element is an iFrame eventFunction(); else if (event.toLowerCase() === "load" && element === window && SMEventHandler.Internal.PageLoaded === true) // Element is the current Window instance eventFunction(); } ;(function() { SMEventHandler.AddEventHandler(window, "load", function() { SMEventHandler.Internal.PageLoaded = true; }); })(); // SMCookie /// /// Cookie functionality /// function SMCookie() { } /// /// Create or update cookie - returns True on success, otherwise False /// Unique cookie name /// Cookie value (cannot contain semi colon!) /// Expiration time in seconds /// SMCookie.SetCookie = function(name, value, seconds) { if (value.indexOf(';') > -1) { //alert("Unable to set cookie - value contains illegal character: ';'"); return false; } var date = new Date(); date.setTime(date.getTime() + (seconds * 1000)); var path = location.pathname.match(/^.*\//)[0]; // Examples: / OR /Sitemagic/ OR /Sitemagic/sites/demo/ - https://regex101.com/r/aU8iW6/1 if (SMEnvironment.IsSubSite() === false) { // Unfortunately cookies on main site will be accessible by subsites, and also cause naming conflicts. // Therefore a prefix is made part of the cookie key for the main site. // This is not necessary for subsites since the cookie path prevent cookies from being shared. // Example: // - /Sitemagic: Cookies are available to every sub folder // - /Sitemagic/sites/demo: Cookies are available to every sub folder, but not parent folders, and therefore not to e.g. /Sitemagic/sites/example // NOTICE: "SM#/#" prefix MUST be identical to prefix used in SMEnvironment server side! name = "SM#/#" + name; } document.cookie = name + "=" + value + "; expires=" + date.toGMTString() + "; path=" + path; return true; } /// /// Returns cookie value if found, otherwise Null /// Unique cookie name /// SMCookie.GetCookie = function(name) { if (SMEnvironment.IsSubSite() === false) { // Use cookie prefix for main site to prevent conflicts with cookies on subsites. // NOTICE: "SM#/#" prefix MUST be identical to prefix used in SMEnvironment server side! name = "SM#/#" + name; } var name = name + "="; var cookies = document.cookie.split(";"); var cookie = null; for (i = 0 ; i < cookies.length ; i++) { cookie = cookies[i]; while (cookie.charAt(0) === " ") cookie = cookie.substring(1, cookie.length); if (cookie.indexOf(name) === 0) return cookie.substring(name.length, cookie.length); } return null; } /// /// Return names of all cookies /// SMCookie.GetCookies = function() { var cookies = document.cookie.split(";"); var cookie = null; var info = null; var names = []; for (i = 0 ; i < cookies.length ; i++) { cookie = cookies[i]; while (cookie.charAt(0) === " ") cookie = cookie.substring(1, cookie.length); info = cookie.split("="); if (SMEnvironment.IsSubSite() === true && info[0].indexOf("SM#/#") === 0) // Exclude main site cookies on subsites continue; names.push(((info[0].indexOf("SM#/#") === 0 ? info[0].substring(5) : info[0]))); } return names; } /// /// Remove cookie - returns True on success, otherwise False /// Unique cookie name /// SMCookie.RemoveCookie = function(name) { return this.SetCookie(name, "", -1); } // SMMessageDialog /// /// Message and dialog functionality /// function SMMessageDialog() { } /// /// Display message dialog /// Content of message dialog /// SMMessageDialog.ShowMessageDialog = function(content) { alert(content); } /// /// Display message dialog - postpone until onload event is fired /// Content of message dialog /// SMMessageDialog.ShowMessageDialogOnLoad = function(content) { SMEventHandler.AddEventHandler(window, "load", function() { alert(content); }); } /// /// Display confirmation dialog. Returns True if user clicks the OK button, False otherwise. /// Content of confirmation dialog /// SMMessageDialog.ShowConfirmDialog = function(content) { return confirm(content); } /// /// Display input dialog. Returns user input as string. Null is returned if user cancels input dialog. /// Content of confirmation dialog /// Initial value in input field /// SMMessageDialog.ShowInputDialog = function(content, value) { return prompt(content, value); } /// /// Display password dialog. Password entered is passed to callback function. /// /// JavaScript function delegate fired when user sends password or closes password dialog. /// Function must take one arguments (string) which is the password entered. /// The argument will be Null if the password dialog was canceled. /// /// Optional password dialog title /// Optional password dialog description /// SMMessageDialog.ShowPasswordDialog = function(callback, caption, description) { var title = (caption !== undefined ? caption : SMLanguageHandler.GetTranslation("EnterPassword")); var msg = (description !== undefined ? description : ""); // Dialog markup var content = ""; content += ""; content += ""; content += ""; content += " Password"; content += " "; content += ""; content += ""; content += "

Password

"; content += " "; content += "
 
"; content += ""; content += ""; var smwin = new SMWindow(SMRandom.CreateGuid()); smwin.SetSize(400, ((msg !== "") ? 160 : 125)); smwin.SetContent(content); smwin.SetOnShowCallback(function() { // Get elements var win = smwin.GetInstance(); var lblHeadline = win.document.getElementsByTagName("h1")[0]; var lblMsg = win.document.getElementsByTagName("div")[0]; var txtPass = win.document.getElementsByTagName("input")[0]; var cmdOk = win.document.getElementsByTagName("input")[1]; var cmdCancel = win.document.getElementsByTagName("input")[2]; // Assign labels (language) win.document.title = title; lblHeadline.innerHTML = title; lblHeadline.style.marginBottom = ((msg !== "") ? "15px" : "20px"); lblMsg.innerHTML = SMStringUtilities.ReplaceAll(SMStringUtilities.ReplaceAll(msg, "\r", ""), "\n", "
"); cmdOk.value = SMLanguageHandler.GetTranslation("Ok"); cmdCancel.value = SMLanguageHandler.GetTranslation("Cancel"); // Event listeners SMEventHandler.AddEventHandler(cmdOk, "click", function() { callback(txtPass.value); smwin.Close(); }); SMEventHandler.AddEventHandler(cmdCancel, "click", function() { callback(null); smwin.Close(); }); SMEventHandler.AddEventHandler(txtPass, "keydown", function(e) { e = (win.event ? win.event : e); if (e.keyCode === 13) { callback(txtPass.value); smwin.Close(); } else if (e.keyCode === 27) { callback(null); smwin.Close(); } }); SMEventHandler.AddEventHandler(window, "beforeunload", function() { smwin.Close(); }); txtPass.focus(); }); smwin.SetOnCloseCallback(function() { var win = smwin.GetInstance(); // null if already closed, e.g. by using OK/Cancel buttons or ENTER/ESC keys if (win !== null) callback(null); }); smwin.Show(); } // SMWindow /// /// Window/dialog functionality. /// /// // Example of opening a picture in a window /// var smw = new SMWindow("MyPictureViewer"); /// smw.SetUrl("files/images/Harbour.jpg"); /// smw.SetSize(400, 300); /// smw.SetOnShowCallback(function() { alert("Window visible"); }); /// smw.SetOnCloseCallback(function() { alert("Window closed"); }); /// smw.Show(); /// /// NOTICE: Sitemagic 2015 introduced some changes to the SMWindow /// class which might require developers to adjust existing code. /// /// The new version uses dialogs (inline pop ups) rather than /// old browser pop ups which are often being blocked, and are /// broken in many respects. Also, old browser windows are implemented /// very differently across different browsers, so some options will work /// on one browser, and not on other browsers. /// The new Dialog Mode (which is default) is cross browser compatible and /// will not be blocked. /// /// It is possible to enable Legacy Mode to keep using the old browser windows, and by /// that either avoid or minimize changes to existing code. Using Legacy Mode is not /// recommended though. /// Legacy Mode can be enabled globally using the config.xml.php configuration file, /// or per SMWindow instance with a small change to existing code. /// /// How to globally enable Legacy Mode (always prefer old browser windows): /// 1) Open config.xml.php for editing /// 2) Add the following entry and save the file: /// <entry key="SMWindowLegacyMode" value="True" /> /// /// How to enable Legacy Mode per SMWindow instance in code (use old browser window): /// - Replace smw.Show() with smw.Show(true) - smw being an instance of SMWindow. /// /// Code changes that might be required to fully support the new SMWindow class /// in both Legacy Mode and Dialog Mode: /// /// - Rather than using window.opener to reference the parent window, use the following /// snippet to support both the new Dialog Mode and Legacy Mode: /// var parentWindow = window.opener || window.top; /// /// - Calling window.close() will not work in Dialog Mode. To work properly in both /// Dialog Mode and Legacy Mode use the following approach instead: /// var parentWindow = window.opener || window.top; /// var smwin = parentWindow.SMWindow.GetInstance(window.name); // SMWindow instance /// smwin.Close(); /// Be aware that this only works for pages loaded from the same domain (Same-Origin Policy). /// If the page is loaded from a foreign domain and it needs to be able to close itself, Legacy Mode /// in conjunction with window.close() must be used. /// /// - Pages loaded inside an instance of SMWindow cannot reliably use the OnBeforeUnload event since it is only fully /// supported in Legacy Mode. A page displayed in Dialog Mode can be unloaded by either navigating within the dialog, /// by closing the dialog (which does not fire OnBeforeUnload), or by reloading the page containing the dialog. This /// does not map well to the old OnBeforeUnload event. Also the OnBeforeUnload event is not widely and consistently /// supported by all browsers, and should only be used to ask the user to consider staying on the page. Alternative /// mechanisms such as persisting data on the fly might be a better solution to prevent users from loosing data when /// a window is closed. If the OnBeforeUnload event is required to solve a specific problem, forcing Legacy Mode will /// be necessary. /// /// - Calling smwin.GetInstance() immediately after calling smwin.Show() is no longer reliable since the window /// instance may not be immediately ready in Dialog Mode. Instead use the following approach: /// smwin.SetOnShowCallback(function() { var win = smwin.GetInstance(); /* ...... */ }); /// Use SetOnLoadCallback(..) instead if the DOM document needs to be manipulated. /// /// Also be aware that the following options are only being used in Legacy Mode: /// SetDisplayToolBar, SetDisplayLocation, SetDisplayMenuBar, SetDisplayStatusBar. /// Many browsers, however, do not honor these options anymore. It is recommended to /// simply avoid using them. Some browsers do not honor these options either in /// Legacy Mode (e.g. Chrome): SetResizable, SetDisplayScrollBars. They work as /// expected in Dialog Mode. /// /// /// Constructor - creates instance of SMWindow /// Unique instance ID /// function SMWindow(identifier) { // Properties this.id = (identifier ? identifier : SMRandom.CreateGuid()); this.url = ""; this.content = ""; this.width = 320; this.height = 240; this.displayToolBar = false; // Legacy Mode only, not honored by all browsers this.displayLocation = false; // Legacy Mode only, not honored by all browsers this.displayMenuBar = false; // Legacy Mode only, not honored by all browsers this.displayStatusBar = false; // Legacy Mode only, not honored by all browsers this.displayScrollBars = true; // Not honored by all browsers this.resizable = true; // Not honored by all browsers this.positionLeft = 0; this.positionTop = 0; this.centerWindow = true; this.preferModal = false; this.showCallback = null; this.closeCallback = null; this.loadCallback = null; this.loadCallbackFired = false; this.instance = null; // Browser window / iFrame Content Window this.dialog = null; // jQuery dialog (null in Legacy Mode) // Close existing instance if opened with same identifier if (SMWindow.instances[this.id] !== undefined) SMWindow.instances[this.id].Close(); // Add instance to static container which allows us to // resolve it using the static SMWindow.GetInstance(..) function SMWindow.instances[this.id] = this; // Functions /// /// Get instance ID /// this.GetId = function() { return this.id; } /// /// /// Point browser window to specified URL. /// Specifying a URL to an image (jpg, jpeg, png, or gif) turns the SMWindow instance into /// an Image Preview Mode which makes the image stretch to the size of the window, and disable scrollbars. /// /// Valid URL /// this.SetUrl = function(url) { if (SMStringUtilities.EndsWith(url.toLowerCase(), ".jpg") === true || SMStringUtilities.EndsWith(url.toLowerCase(), ".jpeg") === true || SMStringUtilities.EndsWith(url.toLowerCase(), ".png") === true || SMStringUtilities.EndsWith(url.toLowerCase(), ".gif") === true) { // Make an image fit within window/dialog, disable scroll, and have image scale when resizing this.content = ""; this.displayScrollBars = false; } else { this.url = url; } } /// /// Set browser window content /// Browser window content (can be HTML) /// this.SetContent = function(content) { this.content = content; } /// /// Set browser window dimensions (width and height) /// Width in pixels /// Height in pixels /// this.SetSize = function(width, height) { this.width = width; this.height = height; } /// /// /// Determines whether to display browser toolbar or not. /// Only supported in Legacy Mode, but not by all browsers. /// Relying on this feature is not recommended. /// /// Set True to enable toolbar, False not to /// this.SetDisplayToolBar = function(value) { this.displayToolBar = value; } /// /// /// Determines whether to display browser location bar or not. /// Only supported in Legacy Mode, but some browsers will not /// allow you to disable this for security reasons. /// Relying on this feature is not recommended. /// /// Set True to enable location bar, False not to /// this.SetDisplayLocation = function(value) { this.displayLocation = value; } /// /// /// Determines whether to display browser menu or not. /// Only supported in Legacy Mode, but not by all browsers. /// Relying on this feature is not recommended. /// /// Set True to enable menu, False not to /// this.SetDisplayMenuBar = function(value) { this.displayMenuBar = value; } /// /// /// Determines whether to display browser status bar or not. /// Only supported in Legacy Mode, but some browsers will not /// allow you to disable this for security reasons. /// Relying on this feature is not recommended. /// /// Set True to enable status bar, False not to /// this.SetDisplayStatusbar = function(value) { this.displayStatusbar = value; } /// /// /// Determines whether to display scrollbars or not. /// Some browsers will not allow you to disable this in Legacy Mode. /// /// Set True to enable scrollbars, False not to /// this.SetDisplayScrollBars = function(value) { this.displayScrollBars = value; } /// /// /// Determines whether user will be able to change size of browser window or not. /// Some browsers will not allow you to disable this in Legacy Mode. /// /// Set True to enable resizing, False not to /// this.SetResizable = function(value) { this.resizable = value; } /// /// Determines X,Y position of browser window when opened /// Pixels from left /// Pixels from top /// this.SetPosition = function(pixelsLeft, pixelsTop) { this.positionLeft = pixelsLeft; this.positionTop = pixelsTop; this.centerWindow = false; } /// /// Determines whether to center browser window or not /// Set True to center window, False not to /// this.SetCenterWindow = function(value) { this.centerWindow = value; } /// /// Determines whether to make dialog modal or not - this is ignored in Legacy Mode /// Set True to make dialog modal, False not to /// this.SetModal = function(value) { this.preferModal = value; } /// /// Set OnShow callback function to be executed when window is opened /// Event handler function to execute - takes no arguments /// this.SetOnShowCallback = function(cb) { if (typeof(cb) !== "function") throw "Callback must be a function"; this.showCallback = cb; } /// /// /// Set OnLoad callback function to be executed when page has been loaded. /// Notice that this does not work for foreign domains in Legacy Mode (Same-Origin Policy). /// /// Event handler function to execute - takes no arguments /// this.SetOnLoadCallback = function(cb) { // Browser bug: Will not fire in IE (Legacy Mode) if page is being redirected (e.g. domain.com/demo => domain.com/demo/ or index.php => / or index.html => /). if (typeof(cb) !== "function") throw "Callback must be a function"; this.loadCallback = cb; } /// /// Set OnClose callback function to be executed when window is closed /// Event handler function to execute - takes no arguments /// this.SetOnCloseCallback = function(cb) { if (typeof(cb) !== "function") throw "Callback must be a function"; this.closeCallback = cb; } /// /// /// Open browser window/dialog. /// /// Pages opened within the window can use the following approach to access the parent window: /// var parentWindow = window.opener || window.top; // Browser window instance /// The page can also access its own instance of SMWindow like so: /// var smwin = parentWindow.SMWindow.GetInstance(window.name); // SMWindow instance /// This is only possible for pages loaded on the same domain due to the Same-Origin Policy. /// /// /// Set True to force use of native browser window (likely to initially be blocked by browser) - not recommended. /// Set False to force use of Dialog Mode, in case Legacy Mode has been globally enabled, but is not desired for this specific instance. /// In general it is not recommended to force either Dialog Mode or Legacy Mode. /// /// this.Show = function(legacyMode) { // Close window if already opened (prevent same instance from being opened in both Dialog Mode and Legacy Mode // if switching legacyMode flag in Show(..) function), and makes sure window always open on top - if window has // already been opened in Legacy Mode, the window will simply just reload and not emerge on the screen (gain focus). this.Close(); this.loadCallbackFired = false; var me = this; // Make "this" scope available to callbacks // --------------------------------------- // jQuery UI dialog // --------------------------------------- if (legacyMode === false || (legacyMode !== true && SMWindow.LegacyMode !== true)) // Use jQuery dialog unless Legacy Mode is enabled for this window or globally in config.xml.php { // Load jQuery SMResourceManager.Jquery.LoadInternal(function($) { // Create dialog var dialog = null; var iframe = null; dialog = $("
").html("").dialog( { dialogClass: "Sitemagic SMWindow", /* jQuery UI theme uses the Sitemagic class as a scope for styling */ autoOpen: false, modal: (me.preferModal === true), title: "", width: me.width, height: me.height + 28, // Add 28px which is the height of the title panel (not very flexibile - might be changed by custom CSS!) position: ((me.centerWindow === false) ? [me.positionLeft, me.positionTop] : null), // null = centered resizable: me.resizable, closeOnEscape: false, open: function() { if (me.showCallback !== null) fireEvent("OnShow", me.showCallback); }, close: function(event, ui) { if (me.closeCallback !== null) fireEvent("OnClose", me.closeCallback); me.instance = null; me.dialog = null; dialog.dialog("destroy").remove(); }, resizeStart: function(ev, ui) { $(ev.target.parentNode).addClass("SMWindowTransparent"); }, resizeStop: function(ev, ui) { $(ev.target.parentNode).removeClass("SMWindowTransparent"); }, dragStart: function(ev, ui) { $(ev.target.parentNode).addClass("SMWindowTransparent"); }, dragStop: function(ev, ui) { $(ev.target.parentNode).removeClass("SMWindowTransparent"); } }); iframe = dialog[0].firstChild; me.instance = iframe.contentWindow; // Register OnLoad handler SMEventHandler.AddEventHandler(iframe, "load", function() { if (me.loadCallback !== null && me.loadCallbackFired === false) { me.loadCallbackFired = true; // Only fire first time a page is loaded, to mimic behaviour of browser pop up (not fired when navigating) fireEvent("OnLoad", me.loadCallback); } }); // Load content if (me.content !== "") { iframe.contentWindow.document.write(me.content); iframe.contentWindow.document.close(); } else { iframe.src = me.url; } // Open dialog dialog.dialog("open"); me.dialog = dialog; // Make dialog available to SMWindow.Close() }); return; } // --------------------------------------- // LEGACY MODE - Old browser pop up window // --------------------------------------- if (this.centerWindow === true) { this.positionLeft = Math.floor((screen.width / 2) - (this.width / 2)); this.positionTop = Math.floor((screen.height / 2) - (this.height / 2)); } var options = "width=" + this.width + ",height=" + this.height; options += ",toolbar=" + ((this.displayToolBar === true) ? "yes" : "no"); options += ",location=" + ((this.displayLocation === true) ? "yes" : "no"); options += ",menubar=" + ((this.displayMenuBar === true) ? "yes" : "no"); options += ",status=" + ((this.displayStatusBar === true) ? "yes" : "no"); options += ",scrollbars=" + ((this.displayScrollBars === true) ? "yes" : "no"); options += ",resizable=" + ((this.resizable === true) ? "yes" : "no"); options += ",left=" + this.positionLeft; options += ",top=" + this.positionTop; this.instance = window.open(((this.content === "") ? this.url : ""), this.id, options); if (this.instance === null || this.instance === undefined) { alert("Your browser prevented this website from opening a window - please enable pop up windows"); this.instance = null; return; } if (this.content !== "") { this.instance.document.write(this.content); this.instance.document.close(); } // Event handlers // Notice: // OnLoad is a bit tricky. For most browsers it fires, but not always for earlier versions of IE. // If static content is set, it doesn't fire, unless it contains references to external resources // such as images. That causes OnLoad to be triggered. // Therefore OnLoad is registered but also triggered manually in case the browser doesn't trigger it. // A simple boolean flag ensure that the event is only fired once. // It seems reasonable to let the OnLoad event fire immediately (when done manually) since the // document content has already been set above and the document instance closed. try // Browser might throw Access Denied error for foreign domains (e.g. Safari and IE), while other browsers just ignore this (e.g. Chrome) { SMEventHandler.AddEventHandler(this.instance, "load", function() { if (me.loadCallback !== null && me.loadCallbackFired === false) { me.loadCallbackFired = true; fireEvent("OnLoad", me.loadCallback); } }); } catch (err) { if (window.console) { console.log(err.message); console.log(err.stack); console.log(err); if (this.loadCallback !== null) console.log("Unable to register OnLoad event handler - access denied - most likely due to Same-Origin Policy"); } } // OnLoad not always fired when static content is set (see explaination above) - make sure it fires by doing so manually if (this.content !== "" && this.loadCallback !== null && this.loadCallbackFired === false) { this.loadCallbackFired = true; fireEvent("OnLoad", this.loadCallback); } // Using Interval to support OnClose event - OnBeforeUnload only works if page is loaded from the same domain (Same-Origin Policy) var iId = null; iId = setInterval(function() { if (me.instance.closed === true) /*me.instance === null*/ { // Known limitation in JS: Callback handlers set from within dialog window will not work in Legacy Mode. Example: // (window.opener || window.top).SMWindow.GetInstance(window.name).SetOnCloseCallback(function() { /* ... */ }); // Callback must be defined in window which created dialog window. This is most likely due to the // fact that the callback's execution context (scope/closure) is destroyed once the window no longer exists, // which prevents the callback from working. // Registering the callback on the parent window is not sufficient either since the execution context still belongs // to the dialog window. Therefore the example below won't work: // var parentWin = (window.opener || window.top); // parentWin.MyCallback = function() { /* ... */ }; // parentWin.SMWindow.GetInstance(window.name).SetOnCloseCallback(parentWin.MyCallback); // BUT, it IS possible to have the parent window register a callback defined by the dialog window by using the // eval() function of the parent window (at least if the two pages are hosted on the same domain (Same-Origin)). // Example: // var parentWin = (window.opener || window.top); // parentWin.eval("SMWindow.GetInstance('" + window.name + "').SetOnCloseCallback(function() { location.href = location.href; });"); // It's a bit messy, but the best we can do to work around this limitation in JavaScript. The problem is not related to SMWindow. // If the solution above is not acceptable, avoid using Legacy Mode - use Dialog Mode instead! if (me.closeCallback !== null) fireEvent("OnClose", me.closeCallback); me.instance = null; clearInterval(iId); } }, 200); if (this.showCallback !== null) fireEvent("OnShow", this.showCallback); } /// /// /// Close window/dialog. /// This is also possible from within the window using the following approach: /// (window.opener || window.top).SMWindow.GetInstance(window.name).Close(); /// /// this.Close = function() { // NOTICE: Do not pass Close function directly as callback to e.g. setTimeout as it changes 'this' // to the browser window instance (Legacy Mode) or the iFrame content window instance (Dialog Mode). // Instead wrap it in an anonymous function like so: setTimeout(function() { smwin.Close(); }, 2000); if (this.instance === null) return; if (this.dialog !== null) { this.dialog.dialog("close"); } else { this.instance.close(); } } /// /// /// Returns internal browser window instance if open, otherwise Null. /// The instance provides access to e.g. the window document: /// var doc = smwin.GetInstance().document; /// Notice: This is only possible with pages loaded from the same domain (Same-Origin Policy). /// Also be aware that manipulating the document instance should not take place until during /// or after OnLoad (see SetOnLoadCallback(..)). /// /// this.GetInstance = function() { return this.instance; // Do NOT do console.log(smwin.GetInstance())! It sometimes crashes Chrome when debugging } function fireEvent(eventName, cb) // Fires event with error handling { try { cb(); } catch (err) { if (window.console) { console.log("Error occurred executing " + eventName + " event handler"); console.log(err.message); console.log(err.stack); console.log(err); } } } } SMWindow.instances = {}; /// /// Returns SMWindow instance by ID if found, otherwise Null /// SMWindow instance ID /// SMWindow.GetInstance = function(id) { return ((SMWindow.instances[id] !== undefined) ? SMWindow.instances[id] : null); } // SMHttpRequest /// /// Asynchronous HTTP request functionality (AJAX). /// /// // Example code /// /// var http = new SMHttpRequest("CreateUser.php", true); /// /// http.SetData("username=Jack&password=Secret"); /// http.SetStateListener(function() /// { ///      if (this.GetCurrentState() === 4 && this.GetHttpStatus() === 200) ///          alert("User created - server said: " + this.GetResponseText()); /// }); /// /// http.Start(); /// /// /// Constructor - creates instance of SMHttpRequest /// URL to request /// Value indicating whether to perform request asynchronous or not /// function SMHttpRequest(url, async) // url, true|false { this.url = url; this.async = async; this.httpRequest = getHttpRequestObject(); this.customHeaders = {}; this.data = null; /// /// /// Add header to request. /// Manually adding headers will prevent the SMHttpRequest instance from /// manipulating headers. This is done to provide full control with the headers. /// You will in this case most likely need to add the following header for a POST request: /// Content-type : application/x-www-form-urlencoded /// /// Header key /// Header value /// this.AddHeader = function(key, value) { this.customHeaders[key] = value; } /// /// Set data to post - this will change the request method from GET to POST /// Data to send /// this.SetData = function(data) { this.data = data; } /// /// Invoke request /// this.Start = function() { var method = ((this.data === null || this.data === "") ? "GET" : "POST"); this.httpRequest.open(method, this.url, this.async); var usingCustomHeaders = false; for (var header in this.customHeaders) { this.httpRequest.setRequestHeader(header, this.customHeaders[header]); usingCustomHeaders = true; } if (method === "POST" && usingCustomHeaders === false) this.httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); this.httpRequest.send(this.data); } /// /// /// Returns result from request as XML or HTML document. /// Return value will only be as expected if GetCurrentState() returns a value of 4 /// (request done) and GetHttpStatus() returns a value of 200 (request successful). /// /// this.GetResponseXml = function() { return this.httpRequest.responseXML; } /// /// /// Returns text result from request. /// Return value will only be as expected if GetCurrentState() returns a value of 4 /// (request done) and GetHttpStatus() returns a value of 200 (request successful). /// /// this.GetResponseText = function() { return this.httpRequest.responseText; } /// /// /// Returns result from request as JSON object, Null if no response was returned. /// Return value will only be as expected if GetCurrentState() returns a value of 4 /// (request done) and GetHttpStatus() returns a value of 200 (request successful). /// /// this.GetResponseJson = function() { return ((this.httpRequest.responseText !== "") ? JSON.parse(this.httpRequest.responseText) : null); } /// /// /// Set delegate to invoke when request state is changed. /// Use GetCurrentState() to read the state at the given time. /// /// JavaScript function invoked when state changes /// this.SetStateListener = function(func) { this.httpRequest.onreadystatechange = func; } /// /// /// Get current request state. /// 0 = Unsent /// 1 = Opened /// 2 = Headers received /// 3 = Loading /// 4 = Done (response is ready for processing) /// /// this.GetCurrentState = function() // 0 = unsent, 1 = opened, 2 = headers received, 3 = loading, 4 = done { return this.httpRequest.readyState; } /// /// /// Returns HTTP status. Common return values are: /// 200 = OK (successful request) /// 304 = Forbidden (access denied) /// 404 = Not found /// 408 = Request time out /// 500 = Internal server error /// 503 = Service unavailable /// /// this.GetHttpStatus = function() { return this.httpRequest.status; } function getHttpRequestObject() { if (window.XMLHttpRequest) // Firefox, IE7, Chrome, Opera, Safari return new XMLHttpRequest(); else if (window.ActiveXObject) // IE5, IE6 return new ActiveXObject("Microsoft.XMLHTTP"); else { //alert("Http Request object not supported"); return null; } } } // SMBrowser /// /// Provides access to various browser information. /// /// // Example code /// /// var browserName = SMBrowser.GetBrowser(); /// var browserVersion = SMBrowser.GetVersion(); /// var browserLanguage = SMBrowser.GetLanguage(); /// /// if (browserName === "MSIE" && browserVersion < 7) /// { ///      if (browserLanguage === "da") ///          alert("Opgrader venligst til IE7 eller nyere"); ///      else ///          alert("Please upgrade to IE7 or newer"); /// } /// function SMBrowser() { } /// /// Returns browser name. Possible values are: Chrome, Safari, MSIE, Firefox, Opera, Unknown /// SMBrowser.GetBrowser = function() { var agent = navigator.userAgent; if (agent.indexOf("Chrome") > -1) return "Chrome"; if (agent.indexOf("Safari") > -1) return "Safari"; if (agent.indexOf("MSIE") > -1 || agent.indexOf("Trident") > -1) return "MSIE"; if (agent.indexOf("Firefox") > -1) return "Firefox"; if (agent.indexOf("Opera") > -1) return "Opera"; return "Unknown"; } /// /// Returns major version number for known browsers, -1 for unknown browsers /// SMBrowser.GetVersion = function() { var start = 0; var end = 0; var agent = navigator.userAgent; if (SMBrowser.GetBrowser() === "Chrome") { start = agent.indexOf("Chrome/"); start = (start !== -1 ? start + 7 : 0); end = agent.indexOf(".", start); end = (end !== -1 ? end : 0); } if (SMBrowser.GetBrowser() === "Safari") { start = agent.indexOf("Version/"); start = (start !== -1 ? start + 8 : 0); end = agent.indexOf(".", start); end = (end !== -1 ? end : 0); } if (SMBrowser.GetBrowser() === "MSIE") { if (agent.indexOf("MSIE") > -1) { start = agent.indexOf("MSIE "); start = (start !== -1 ? start + 5 : 0); end = agent.indexOf(".", start); end = (end !== -1 ? end : 0); } else if (agent.indexOf("Trident") > -1) // IE11+ { start = agent.indexOf("rv:"); start = (start !== -1 ? start + 3 : 0); end = agent.indexOf(".", start); end = (end !== -1 ? end : 0); } } if (SMBrowser.GetBrowser() === "Firefox") { start = agent.indexOf("Firefox/"); start = (start !== -1 ? start + 8 : 0); end = agent.indexOf(".", start); end = (end !== -1 ? end : 0); } if (SMBrowser.GetBrowser() === "Opera") { start = agent.indexOf("Version/"); start = (start !== -1 ? start + 8 : -1); if (start === -1) { start = agent.indexOf("Opera/"); start = (start !== -1 ? start + 6 : -1); } if (start === -1) { start = agent.indexOf("Opera "); start = (start !== -1 ? start + 6 : -1); } end = agent.indexOf(".", start); end = (end !== -1 ? end : 0); } if (start !== 0 && start !== 0) return parseInt(agent.substring(start, end)); return -1; } /// /// Returns browser language - e.g. "da" (Danish), "en" (English) etc. /// SMBrowser.GetLanguage = function() { var lang = null; if (navigator.language) lang = navigator.language.toLowerCase(); else if (navigator.browserLanguage) lang = navigator.browserLanguage.toLowerCase(); if (lang === null || lang === "") return "en"; if (lang.length === 2) return lang; if (lang.length === 5) return lang.substring(0, 2); return "en"; } /// /// Returns page width in pixels on succes, -1 on failure /// SMBrowser.GetPageWidth = function() { var w = -1; if (window.innerWidth) // W3C w = window.innerWidth; else if (document.documentElement && document.documentElement.clientWidth) // IE 6-8 (not quirks mode) w = document.documentElement.clientWidth; return w; } /// /// Returns page height in pixels on succes, -1 on failure /// SMBrowser.GetPageHeight = function() { var h = -1; if (window.innerHeight) // W3C h = window.innerHeight; else if (document.documentElement && document.documentElement.clientHeight) // IE 6-8 (not quirks mode) h = document.documentElement.clientHeight; return h; } /// /// Get screen width /// Set True to return only available space /// SMBrowser.GetScreenWidth = function(onlyAvailable) { if (onlyAvailable === true) return window.screen.availWidth; return window.screen.width; } /// /// Get screen height /// Set True to return only available space /// SMBrowser.GetScreenHeight = function(onlyAvailable) { if (onlyAvailable === true) return window.screen.availHeight; return window.screen.height; } /// /// /// Check whether specified CSS property and CSS value is supported by browser. /// Returns True if supported, otherwise False. /// /// CSS property name /// CSS property value /// SMBrowser.CssSupported = function(property, value) { var div = document.createElement("div"); var cssText = div.style.cssText; try { div.style[property] = value; } catch (err) { return false; } return (cssText !== div.style.cssText); } // SMResourceManager // The order of processing scripts and stylesheets: // http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/#The_order_of_processing_scripts_and_style_sheets /// /// The Resource Manager is a useful mechanism for loading styleheets and JavaScript on demand in a non blocking manner. /// function SMResourceManager() { } SMResourceManager.Jquery = {}; SMResourceManager.Jquery.Loading = false; SMResourceManager.Jquery.Loaded = {}; SMResourceManager.Jquery.Internal = null; /// /// /// Load client script on demand in a non-blocking manner. /// /// // Example of loading a JavaScript file /// /// SMResourceManager.LoadScript("extensions/test/test.js", function(src) /// { ///      alert("JavaScript " + src + " loaded and ready to be used!"); /// }); /// /// Script source (path or URL) /// /// Callback function fired when script loading is complete - takes the script source requested as an argument. /// Be aware that a load error will also trigger the callback to make sure control is always returned. /// Consider using feature detection within callback function for super reliable execution - example: /// if (expectedObjectOrFunction) { /* Successfully loaded, continue.. */ } /// /// SMResourceManager.LoadScript = function(src, callback) { var script = document.createElement("script"); script.type = "text/javascript"; if (callback !== undefined && (SMBrowser.GetBrowser() !== "MSIE" || (SMBrowser.GetBrowser() === "MSIE" && SMBrowser.GetVersion() >= 9))) { script.onload = function() { callback(src); }; // Terrible, but we need same behaviour for all browsers, and IE8 (and below) does not distinguish between success and failure. // Also, we need to make sure control is returned no matter what - just like using ordinary