Modified: tuscany/sca-cpp/trunk/modules/js/htdocs/ui.js URL: http://svn.apache.org/viewvc/tuscany/sca-cpp/trunk/modules/js/htdocs/ui.js?rev=1428193&r1=1428192&r2=1428193&view=diff ============================================================================== --- tuscany/sca-cpp/trunk/modules/js/htdocs/ui.js (original) +++ tuscany/sca-cpp/trunk/modules/js/htdocs/ui.js Thu Jan 3 07:41:53 2013 @@ -31,6 +31,8 @@ ui.elementByID = function(node, id) { return null; for (var i in node.childNodes) { var child = node.childNodes[i]; + if (isNil(child)) + continue; if (child.id == id) return child; var gchild = ui.elementByID(child, id); @@ -46,7 +48,16 @@ ui.elementByID = function(node, id) { function $(id) { if (id == document) return document; - return ui.elementByID($(document), id); + return memo(document, '$' + id, function() { + return ui.elementByID($(document), id); + }); +} + +/** + * Un-memoize elements previously found by id. + */ +ui.unmemo$ = function(prefix) { + return prefix? unmemo(document, '$' + prefix) : unmemo(document); }; /** @@ -114,10 +125,26 @@ ui.fragmentParams = function(url) { }; /** - * Convert a base64-encoded image to a data URL. + * Convert a base64-encoded PNG image to a data URL. */ -ui.b64img = function(b64) { - return 'data:image/png;base64,' + b64; +ui.b64png = function(b64) { + return 'data:image/png;base64,' + b64.trim(); +}; + +/** + * Convert a base64-encoded JPEG image to a data URL. + */ +ui.b64jpeg = function(b64) { + return 'data:image/jpeg;base64,' + b64.trim(); +}; + +/** + * Convert a data URL to a base64-encoded image. + */ +ui.imgb64 = function(img) { + if (img.startsWith('data:')) + return img.split(',')[1] + return ''; }; /** @@ -131,6 +158,16 @@ ui.declareCSS = function(s) { }; /** + * Include a CSS stylesheet. + */ +ui.includeCSS = function(s) { + var e = ui.declareCSS(s); + var head = document.getElementsByTagName('head')[0]; + head.appendChild(e); + return e; +}; + +/** * Declare a script. */ ui.declareScript = function(s) { @@ -151,15 +188,15 @@ ui.innerScripts = function(e) { * Evaluate a script. */ ui.evalScript = function(s) { - return eval('(function() {\n' + s + '\n})();'); + return eval('(function evalscript() { try { \n' + s + '\n} catch(e) { debug(e.stack); throw e; }})();'); }; /** * Include a script. */ ui.includeScript = function(s) { - //log('include', s); - return eval(s); + //debug('include', s); + return eval('try { \n' + s + '\n} catch(e) { debug(e.stack); throw e; }'); }; /** @@ -171,13 +208,139 @@ ui.isMobile = function() { if (ui.mobiledetected) return ui.mobile; var ua = navigator.userAgent; - if (ua.match(/iPhone/i) || ua.match(/iPad/i) || ua.match(/Android/i) || ua.match(/Blackberry/i) || ua.match(/WebOs/i)) + if (ua.match(/iPhone/i) || ua.match(/iPad/i) || ua.match(/iPod/i) || ua.match(/Android/i) || ua.match(/Blackberry/i) || ua.match(/WebOs/i)) ui.mobile = true; ui.mobiledetected = true; return ui.mobile; }; /** + * Return true if the client is Webkit based. + */ +ui.isWebkit = function() { + return navigator.userAgent.match(/WebKit/i); +}; + +/** + * Return the Webkit version. + */ +ui.webkitVersion = function() { + return Number(navigator.userAgent.replace(/.*AppleWebKit\/(\d+\.\d+).*/, '$1')); +}; + +/** + * Return true if the client is Firefox. + */ +ui.isFirefox = function() { + return navigator.userAgent.match(/Firefox/i); +}; + +/** + * Return the Firefox version. + */ +ui.firefoxVersion = function() { + return Number(navigator.userAgent.replace(/.*Firefox\/(\d+\.\d+).*/, '$1')); +}; + +/** + * Return true if the client is Safari. + */ +ui.isSafari = function() { + return navigator.userAgent.match(/Safari/i); +}; + +/** + * Return true if the client is Chrome. + */ +ui.isChrome = function() { + return navigator.userAgent.match(/Chrome/i); +}; + +/** + * Return true if the client is Internet Explorer. + */ +ui.isMSIE = function() { + return navigator.userAgent.match(/MSIE/i); +}; + +/** + * Return the Internet Explorer version. + */ +ui.msieVersion = function() { + return Number(navigator.userAgent.replace(/.*MSIE (\d+\.\d+).*/, '$1')); +}; + +/** + * Run a UI rendering function asynchronously. + */ +ui.asyncFrame = null; +ui.async = function(f) { + if (isNil(ui.asyncFrame)) + // Use requestAnimationFrame when available, fallback to setTimeout + ui.asyncFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || + function(f) { + return window.setTimeout(f, 16); + }; + return ui.asyncFrame.call(window, f); +}; + +/** + * Delay the execution of a function. + */ +ui.delayed = {} +ui.delay = function(f, t) { + var id = window.setTimeout(function() { + delete ui.delayed[id]; + return f(); + }, isNil(t)? 16 : t); + ui.delayed[id] = id; + return id; +}; + +/** + * Cancel the execution of a delayed function. + */ +ui.cancelDelay = function(id) { + delete ui.delayed[id]; + return window.clearTimeout(id); +}; + +/** + * Run a UI animation. + */ +ui.animationFrame = null; +ui.animation = function(f) { + if (isNil(ui.animationFrame)) + // Use requestAnimationFrame when available, fallback to setInterval + ui.animationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || + function(f) { + if (!('interval' in f) || isNil(f.interval)) { + // First call, setup the interval + f.interval = window.setInterval(function animation() { + f.clearInterval = true; + try { + f(); + } catch(ex) {} + // If the animation function didn't call ui.animation again to + // request another animation frame, clear the interval + if (f.clearInterval) { + f.clearInterval = false; + window.clearInterval(f.interval); + f.interval = null; + } + }, 16); + } else { + // Called to request another animation frame, do not clear the + // interval + f.clearInterval = false; + } + }; + return ui.animationFrame.call(window, f); +}; + +/** * Convert a CSS position to a numeric position. */ ui.numpos = function(p) { @@ -192,46 +355,49 @@ ui.pixpos = function(p) { }; /** - * Default orientation change behavior. + * Default page load behavior. */ -ui.onorientationchange = function(e) { +ui.filler = null; +ui.onload = function() { - // Scroll to the top and hide the address bar + // Add a filler div to make sure we can scroll + if (ui.isMobile()) { + ui.filler = document.createElement('div'); + ui.filler.id = 'filler'; + ui.filler.className = 'filler'; + ui.filler.style.height = ui.pixpos(window.orientation == 0? screen.height : screen.width * 2); + document.body.appendChild(ui.filler); + } else { + // Style scroll bars + var h = document.getElementsByTagName('html'); + if (!isNil(h)) + h[0].className = h[0].className? h[0].classname + ' flatscrollbars' : 'flatscrollbars'; + } + + // Scroll to hide the address bar + document.body.style.display = 'block'; window.scrollTo(0, 0); - // Change fixed position elements to absolute then back to fixed - // to make sure they're correctly layed out after the orientation - // change - map(function(e) { - e.style.position = 'absolute'; - return e; - }, ui.elementsByClassName(document, 'fixed')); - - setTimeout(function() { - map(function(e) { - e.style.position = 'fixed'; - return e; - }, ui.elementsByClassName(document, 'fixed')); - }, 0); + // Set unload handler + window.onunload = function() { + window.scrollTo(0, 0); + return true; + }; + return true; }; /** - * Default page load behavior. + * Default orientation change behavior. */ -ui.onload = function() { +ui.onorientationchange = function(e) { - // Scroll to the top and hide the address bar - window.scrollTo(0, 0); + // Adjust filler height + if (!isNil(ui.filler)) + ui.filler.style.height = ui.pixpos(window.orientation == 0? screen.height : screen.width); - // Initialize fixed position elements only after the page is loaded, - // to workaround layout issues with fixed position on mobile devices - setTimeout(function() { - map(function(e) { - e.style.position = 'fixed'; - return e; - }, ui.elementsByClassName(document, 'fixed')); - }, 0); + // Scroll to hide the address bar + window.scrollTo(0, 0); return true; }; @@ -239,7 +405,7 @@ ui.onload = function() { * Navigate to a new document. */ ui.navigate = function(url, win) { - //log('navigate', url, win); + //debug('navigate', url, win); // Open a new window if (win == '_blank') { @@ -264,6 +430,27 @@ ui.navigate = function(url, win) { if (win == '_view') { if (!window.top.onnavigate) return window.top.open(url, '_self'); + + // Cleanup window event handlers + window.onclick = null; + if (!ui.isMobile()) { + window.onmousedown = null; + window.onmouseup = null; + window.onmousemove = null; + } else { + window.ontouchstart = null; + window.ontouchend = null; + window.ontouchmove = null; + } + + // Cleanup memoized element lookups + ui.unmemo$(); + + // Cancel any timers + for (d in ui.delayed) + ui.cancelDelay(d); + + // Navigate window.top.onnavigate(url); return false; } @@ -369,7 +556,7 @@ ui.datatable = function(l) { } return '<table class="datatable ' + (window.name == 'dataFrame'? ' databg' : '') + '" style="width: 100%;">' + rows(l, 0) + '</table>'; -} +}; /** * Convert a list of elements to an HTML single column table. @@ -405,5 +592,67 @@ ui.datalist = function(l) { } return '<table class="datatable ' + (window.name == 'dataFrame'? ' databg' : '') + '" style="width: 100%;">' + rows(l, 0) + '</table>'; -} +}; + +/** + * Read a file and convert it to a data url. + */ +ui.readfile = function(file, onerror, onprogress, onload) { + var reader = new FileReader(); + reader.onerror = function(e) { + return onerror(); + }; + reader.onprogress = function(e) { + return onprogress(e.lengthComputable? Math.round((e.loaded / e.total) * 90) : 50); + }; + reader.onload = function(r) { + return onload(r.target.result); + }; + return reader.readAsDataURL(file); +}; + +/** + * Read an image url and convert it to a data url. + */ +ui.readimageurl = function(url, onerror, onprogress, onload, width, height) { + // Create a canvas to draw the image + var canvas = document.createElement('canvas'); + if (width) + canvas.width = width; + if (height) + canvas.height = height; + + // Create an image + var img = new Image(); + img.onerror = function(e) { + return onerror(); + }; + img.onload = function() { + // Draw the image + var ctx = canvas.getContext('2d'); + if (width || height) + ctx.drawImage(img, 0, 0, width, height); + else + ctx.drawImage(img, 0, 0); + + // Convert new canvas image to a data url + return onload(canvas.toDataURL('image/png')); + }; + + // Load the image + onprogress(90); + img.src = url; + return true; +}; + +/** + * Read an image file or url and convert it to a data url. + */ +ui.readimage = function(img, onerror, onprogress, onload, width, height) { + if (isString(img)) + return ui.readimageurl(img, onerror, onprogress, onload, width, height); + return ui.readfile(img, onerror, onprogress, function(url) { + return ui.readimageurl(url, onerror, onprogress, onload, width, height); + }, width, height); +};
Modified: tuscany/sca-cpp/trunk/modules/js/htdocs/util.js URL: http://svn.apache.org/viewvc/tuscany/sca-cpp/trunk/modules/js/htdocs/util.js?rev=1428193&r1=1428192&r2=1428193&view=diff ============================================================================== --- tuscany/sca-cpp/trunk/modules/js/htdocs/util.js (original) +++ tuscany/sca-cpp/trunk/modules/js/htdocs/util.js Thu Jan 3 07:41:53 2013 @@ -296,9 +296,19 @@ function memo(obj, key, f) { /** * Un-memoize stored results. */ -function unmemo(obj) { - obj.memo = {}; - return true; +function unmemo(obj, prefix) { + if (!prefix) { + obj.memo = {}; + return true; + } + if (!('memo' in obj)) { + obj.memo = {}; + return true; + } + for (key in obj.memo) { + if (key.substring(0, prefix.length) == prefix) + delete obj.memo[key]; + } } /** @@ -308,6 +318,32 @@ var lstorage = {}; lstorage.enabled = true; /** + * Get a key. + */ +lstorage.key = function(i) { + if (!lstorage.enabled) + return null; + try { + return localStorage.key(i); + } catch(e) { + return null; + } +}; + +/** + * Return the number of keys. + */ +lstorage.length = function() { + if (!lstorage.enabled) + return 0; + try { + return localStorage.length; + } catch(e) { + return 0; + } +}; + +/** * Get an item. */ lstorage.getItem = function(k) { @@ -318,7 +354,7 @@ lstorage.getItem = function(k) { } catch(e) { return null; } -} +}; /** * Set an item. @@ -331,7 +367,7 @@ lstorage.setItem = function(k, v) { } catch(e) { return null; } -} +}; /** * Remove an item. @@ -344,7 +380,7 @@ lstorage.removeItem = function(k) { } catch(e) { return null; } -} +}; /** * Returns a list of the properties of an object. @@ -372,31 +408,28 @@ function domainname(host) { } /** - * Return true if a host name is a subdomain. + * Convert a host name to a top domain name. */ -function issubdomain(host) { - return host.split('.').length > 2; +function topdomainname(host) { + var d = reverse(domainname(host).split('.')); + return reverse(mklist(car(d), cadr(d))).join('.'); } /** - * Return true if the document cookie contains auth information. + * Return true if a host name is a subdomain. */ -function hasauthcookie() { - return !isNil(document.cookie) && - (document.cookie.indexOf('TuscanyOpenAuth=') != -1 || - document.cookie.indexOf('TuscanyOAuth1=') != -1 || - document.cookie.indexOf('TuscanyOAuth2=') != -1 || - document.cookie.indexOf('TuscanyOpenIDAuth=') != -1); +function issubdomain(host) { + return host.split('.').length > 2; } /** * Clear auth information from the document cookie. */ function clearauthcookie() { - document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; - document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; - document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; - document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; + document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/; secure; httponly'; + document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/; secure; httponly'; + document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/; secure; httponly'; + document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/; secure; httponly'; return true; } @@ -414,6 +447,64 @@ function format() { } /** + * Parse an XML dateTime. + */ +function xmldatetime(xml) { + var re = /^([0-9]{4,})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(\.[0-9]+)?(Z|([+-])([0-9]{2}):([0-9]{2}))?$/; + var match = xml.match(re); + if (!match) + return new Date(); + return new Date(Date.UTC(match[1], parseInt(match[2]) - 1, match[3], + match[9]? parseInt(match[4]) + parseInt(match[10]) * (match[9] == '+'? 1 : -1) : match[4], + match[9]? parseInt(match[5]) + parseInt(match[11]) * (match[9] == '+'? 1 : -1) : match[5], + match[6], 0)); +} + +/** + * Encode a string to a url-safe base64 format. + */ +function safeb64encode(s) { + return btoa(s).replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''); +} + +/** + * Decode a url-safe base64 encoded string. + */ +function safeb64decode(s) { + return atob((s.replace(/\-/g, '+').replace(/\_/g, '/') + '===').substring(0, s.length + (s.length % 4))); +} + +/** + * Return a uuid4. + */ +function uuid4() { + if (window.crypto && window.crypto.getRandomValues) { + var b = new Uint16Array(8); + window.crypto.getRandomValues(b); + function s4(n) { + var s = '000' + n.toString(16); + return s.substr(s.length - 4); + } + return s4(b[0]) + s4(b[1]) + '-' + s4(b[2]) + '-' + s4(b[3]) + '-' + s4(b[4]) + '-' + s4(b[5]) + s4(b[6]) + s4(b[7]); + } else { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random() * 16 | 0 + return (c == 'x'? r : (r & 0x3 | 0x8)).toString(16); + }); + } +} + +/** + * Convert an hexadecimal string to ascii. + */ +function hex2ascii(x) { + var a = ''; + for (var i = 0; i < x.length; i += 2) + a += String.fromCharCode(parseInt(x.substr(i, 2), 16)); + return a; +} + +/** * Functions with side effects. Use with moderation. */
