/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /** * UI utility functions. */ var ui = {}; /** * Return a child element of a node with the given id. */ ui.elementByID = function(id) { return document.getElementById(id); }; /** * Return the current document, or a child element with the given id. */ function $(id) { if (id == document) return document; return document.getElementById(id); } /** * Remove ids from a tree of elements. */ ui.removeElementIDs = function(node) { function cleanIDs(node) { for(var i = 0; i < node.childNodes.length; i++) { var c = node.childNodes[i]; if(c.nodeType == 1) { if (c.id != null) c.id = null; cleanIDs(c); } } return true; } return cleanIDs(node); }; /** * Return a list of elements with the given class name. */ ui.elementsByClassName = function(node, c) { return nodeList(node.getElementsByClassName(c)); }; /** * Return the query string of a URL. */ ui.query = function(url) { var u = '' + url; var q = u.indexOf('?'); return q >= 0? u.substring(q + 1) : ''; }; /** * Return the fragment part of a URL. */ ui.fragment = function(url) { var u = '' + url; var h = u.indexOf('#'); return h >= 0? u.substring(h + 1) : ''; }; /** * Return the path and parameters of a URL. */ ui.pathAndParams = function(url) { var u = '' + url; var ds = u.indexOf('//'); var u2 = ds > 0? u.substring(ds + 2) : u; var s = u2.indexOf('/'); return s > 0? u2.substring(s) : ''; }; /** * Return a dictionary of query parameters in a URL. */ ui.queryParams = function(url) { var qp = []; var qs = ui.query(url).split('&'); for (var i = 0; i < qs.length; i++) { var e = qs[i].indexOf('='); if (e > 0) qp[qs[i].substring(0, e)] = unescape(qs[i].substring(e + 1)); } return qp; }; /** * Return a dictionary of fragment parameters in a URL. */ ui.fragmentParams = function(url) { var qp = []; var qs = ui.fragment(url).split('&'); for (var i = 0; i < qs.length; i++) { var e = qs[i].indexOf('='); if (e > 0) qp[qs[i].substring(0, e)] = unescape(qs[i].substring(e + 1)); } return qp; }; /** * Get a style property a DOM element. */ ui.getStyle = function(e, name) { return e.style.getPropertyValue(name); }; /** * Set a style property of a DOM element. */ ui.setStyle = function(e, name, v, async) { if (e.style.getPropertyValue(name) == v) return false; if (!async) return e.style.setProperty(name, v, null); ui.render(function() { return e.style.setProperty(name, v, null); }); return true; }; /** * Remove a style property from a DOM element. */ ui.removeStyle = function(e, name, async) { if (!async) return e.style.removeProperty(name); ui.render(function() { return e.style.removeProperty(name); }); return true; }; /** * Set the CSS class of a DOM element. */ ui.getClass = function(e) { return e.className; }; /** * Set the CSS class of a DOM element. */ ui.setClass = function(e, v, async) { if (e.className == v) return false; if (!async) { e.className = v; return true; } ui.render(function() { e.className = v; }); return true; }; /** * Convert a base64-encoded PNG image to a data URL. */ 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 ''; }; /** * Declare a CSS stylesheet. */ ui.declareCSS = function(s) { var e = document.createElement('style'); e.type = 'text/css'; e.textContent = s; return e; }; /** * 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) { var e = document.createElement('script'); e.type = 'text/javascript'; e.text = s; return e; }; /** * Return the scripts elements under a given element. */ ui.innerScripts = function(e) { return nodeList(e.getElementsByTagName('script')); }; /** * Evaluate a script. */ ui.evalScript = function(s) { return eval('(function evalscript() {\n' + s + '\n})();'); }; /** * Include a script. */ ui.includeScript = function(s) { debug('ui.include', s); return eval(s); }; /** * Return true if the client is a mobile device. */ ui.mobileDetected = false; ui.mobile = false; ui.isMobile = function() { if (ui.mobileDetected) return ui.mobile; var ua = navigator.userAgent; 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) || ua.match(/Mobile.*Firefox/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 Android based. */ ui.isAndroid = function() { return navigator.userAgent.match(/Android/i); }; /** * Return the Android version. */ ui.androidVersion = function() { return Number(navigator.userAgent.replace(/.*Version\/(\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 the Safari version. */ ui.safariVersion = function() { return Number(navigator.userAgent.replace(/.*Version\/(\d+\.\d+).*/, '$1')); }; /** * Return true if the client is Chrome. */ ui.isChrome = function() { return navigator.userAgent.match(/Chrome/i); }; /** * Return the Chrome version. */ ui.chromeVersion = function() { return Number(navigator.userAgent.replace(/.*Chrome\/(\d+\.\d+).*/, '$1')); }; /** * 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 animation. */ ui.animationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || function(f) { if (isNull(f.interval)) { // First call, setup the interval f.interval = window.setInterval(function animation() { f.clearInterval = true; // Call the animation function f(); // 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; } }; ui.animation = function(f) { return ui.animationFrame.call(window, f); }; /** * Run a UI rendering function asynchronously. */ ui.render = function(f) { return ui.animation(f); }; /** * Delay the execution of a function. */ ui.unimportant = {} ui.delay = function(f, t, unimportant) { var id = window.setTimeout(function delayed() { delete ui.unimportant[id]; return f(); }, isNull(t)? 0 : t); if (unimportant) ui.unimportant[id] = id; return id; }; /** * Cancel the execution of a delayed function. */ ui.cancelDelay = function(id) { delete ui.unimportant[id]; return window.clearTimeout(id); }; /** * Convert a CSS position to a numeric position. */ ui.npos = function(p) { return p == null || p == ''? 0 : Number(p.substr(0, p.length - 2)); }; /** * Convert a numeric position to a CSS pixel position. */ ui.pxpos = function(p) { return p + 'px'; }; /** * Show a status message. */ ui.statusElement = undefined; ui.initStatus = function() { ui.statusElement = $('status'); if (isNull(ui.statusElement)) return; ui.setClass(ui.statusElement, ui.isMobile()? 'status3dm' : 'status3d'); ui.setStyle(ui.statusElement, 'display', 'none'); function divtransitionend(e) { ui.setClass(e.target, ui.isMobile()? 'status3dm' : 'status3d'); ui.setStyle(e.target, 'display', 'none'); e.target.error = false; } ui.statusElement.addEventListener('webkitTransitionEnd', divtransitionend, false); ui.statusElement.addEventListener('transitionend', divtransitionend, false); return true; }; ui.status = function(s, c) { debug('ui.status', s); if(isNull(ui.statusElement) || ui.statusElement.error) return s; ui.statusElement.innerHTML = '' + s + ''; ui.setClass(ui.statusElement, ui.isMobile()? 'status3dm' : 'status3d'); ui.setStyle(ui.statusElement, 'display', 'block'); ui.statusElement.error = c == 'errorstatus'; if(ui.statusElement.delay) ui.cancelDelay(ui.statusElement.delay); ui.statusElement.delay = ui.delay(function hidestatus() { ui.setClass(ui.statusElement, ui.isMobile()? 'statusout3dm' : 'statusout3d'); ui.statusElement.error = false; }, c == 'errorstatus'? 8000 : 3000); return s; }; /** * Show an error message. */ ui.error = function(s) { debug('ui.error', s); return ui.status(s, 'errorstatus'); }; /** * Show the online/offline status. */ ui.onlineStatus = function() { return navigator.onLine? true : errorstatus('Offline'); }; /** * Show the working/ready indicator. */ ui.workingElement = undefined; ui.initWorking = function() { ui.workingElement = $('working'); }; ui.working = function() { debug('ui.working'); if (isNull(ui.workingElement)) return false; return ui.setStyle(ui.workingElement, 'display', 'block'); }; ui.ready = function() { debug('ui.ready'); if (isNull(ui.workingElement)) return false; return ui.setStyle(ui.workingElement, 'display', 'none'); }; /** * Get and cache a resource. */ ui.appcache = {}; ui.appcache.get = function(uri, mode) { debug('ui.appcache.get', uri, mode); // Get resource from local storage first var h = uri.indexOf('#'); var u = h == -1? uri : uri.substring(0, h); if(mode != 'remote') { var item = lstorage.getItem('ui.r.' + u); if(item != null && item != '') return item; if(mode == 'local') return undefined; } // Get resource from network var http = new XMLHttpRequest(); http.open("GET", mode == 'remote'? (u + '?t=' + new Date().getTime() + '&r=' + Math.random()) : u, false); http.setRequestHeader('Accept', '*/*'); http.setRequestHeader('X-Cache-Control', 'no-cache'); http.send(null); if(http.status == 200) { var ct = http.getResponseHeader("Content-Type"); if(http.responseText == '' || ct == null || ct == '') { error('http error', u, 'No-Content'); return undefined; } lstorage.setItem('ui.r.' + u, http.responseText); return http.responseText; } error('http error', u, http.status, http.statusText); // Redirect to login page if(http.status == 403 && window.top.location.href.pathname != '/logout/dologout/') { if(window.onloginredirect) window.onloginredirect(new Error('403 ' + http.statusText)); } return undefined; }; /** * Remove a resource from the cache. */ ui.appcache.remove = function(uri) { debug('ui.appcache.remove', uri); var h = uri.indexOf('#'); var u = h == -1? uri : uri.substring(0, h); return lstorage.removeItem(u); }; /** * Default app cache handling behavior. */ ui.onappcache = function(manifest, resources) { if(ui.isMobile() && !ui.isFirefox()) { // On mobile devices, trigger usage of an application cache manifest // Except on mobile Firefox which fails to send cookies with cache manifest requests window.onappcachechecking = function(e) { debug('appcache checking', e); ui.working(); }; window.onappcacheerror = function(e) { debug('appcache error', e); ui.onlineStatus(); ui.ready(); return false; }; window.onappcachenoupdate = function(e) { debug('appcache noupdate', e); ui.ready(); }; window.onappcachedownloading = function(e) { debug('appcache downloading', e); ui.working(); ui.status('Updating'); }; window.onappcacheprogress = function(e) { debug('appcache progress', e); ui.working(); ui.status('Updating'); }; window.onappcacheupdateready = function(e) { debug('appcache updateready', e); // Update offline resources in local storage and reload the page ui.status('Updating'); applicationCache.swapCache(); ui.delay(function swapappcache() { debug('appcache swapped', e); map(function(res) { ui.appcache.remove(car(res)); ui.appcache.get(car(res), 'remote'); }, resources); ui.status('Installed'); ui.ready(); debug('reloading'); window.location.reload(); }); }; window.onappcachecached = function(e) { debug('appcache cached', e); // Install offline resources in local storage ui.status('Installing'); ui.delay(function installoffline() { map(function(res) { ui.appcache.remove(car(res)); ui.appcache.get(car(res), 'remote'); }, resources); ui.status('Installed'); ui.ready(); }); }; window.onloadappcache = function() { debug('appcache iframe loaded'); }; var installer = $('installer'); installer.innerHTML = ''; } else { // On non-mobile devices, check for cache-manifest changes ourselves. ui.working(); var lcmf = ui.appcache.get(manifest + '/cache-manifest.cmf', 'local'); var rcmf = ui.appcache.get(manifest + '/cache-manifest.cmf', 'remote'); if(lcmf == rcmf) { ui.ready(); return true; } debug('cache-manifest changed, reloading'); ui.status(isNull(lcmf)? 'Installing' : 'Updating'); ui.delay(function reloadapp() { map(function(res) { ui.appcache.remove(car(res)); ui.appcache.get(car(res), 'remote'); }, resources); ui.ready(); if(!isNull(lcmf)) { ui.status('Installed'); ui.ready(); debug('reloading'); window.location.reload(); } }); } }; /** * Default page load behavior. */ ui.filler = null; ui.onload = function() { debug('ui.onload'); // Initialize status and working elements ui.initStatus(); ui.initWorking(); // Set orientation change handler document.body.onorientationchange = function(e) { return ui.onorientationchange(e); }; // Handle network offline/online events. window.addEventListener('offline', function(e) { debug('going offline'); ui.status('Offline'); }, false); window.addEventListener('online', function(e) { debug('going online'); ui.status('Online'); }, false); // Add a filler div to make sure we can scroll if (ui.isMobile()) { ui.filler = document.createElement('div'); ui.filler.id = 'filler'; ui.setClass(ui.filler, 'filler'); ui.setStyle(ui.filler, 'height', ui.pxpos(window.orientation == 0? Math.floor(window.innerHeight * 1.5) : Math.floor(window.innerHeight * 1.5))); document.body.appendChild(ui.filler); } else { // Style scroll bars var h = nodeList(document.getElementsByTagName('html')); if (!isNull(h)) { ui.setClass(car(h), car(h).className? car(h).classname + ' flatscrollbars' : 'flatscrollbars'); } } // Scroll to hide the address bar ui.setStyle(document.body, 'display', 'block'); document.body.scrollTop = 0; // Set unload handler window.onunload = function() { document.body.scrollTop = 0; return true; }; return true; }; /** * Default orientation change behavior. */ ui.onorientationchange = function(e) { debug('ui.onorientationchange'); // Adjust filler height if (!isNull(ui.filler)) ui.setStyle(ui.filler, 'height', ui.pxpos(window.orientation == 0? Math.floor(window.innerHeight * 1.5) : Math.floor(window.innerHeight * 1.5))); // Scroll to refresh the page document.body.scrollTop = document.body.scrollTop + 1; document.body.scrollTop = document.body.scrollTop - 1; return true; }; /** * Navigate to a new document. */ ui.navigate = function(url, win) { debug('ui.navigate', url, win); if (url == '' || url == '#') return false; function cleanup() { // 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; } // Cancel any cancelable HTTP requests if (typeof HTTPBindingClient != 'undefined') HTTPBindingClient.cancelRequests(); // Automatically cancel unimportant timers for (var d in ui.unimportant) ui.cancelDelay(d); return true; } // Open a new window if (win == '_blank') { debug('window.open', url, win); window.top.open(url, win); return false; } // Open a new document in the current window if (win == '_self') { cleanup(); debug('window.open', url, win); window.top.open(url, win); return false; } // Reload the current window if (win == '_reload') { cleanup(); debug('window.reload', url); window.top.location = url; window.top.location.reload(); return false; } // Let the current top window handle the navigation if (win == '_view') { cleanup(); if (!window.top.onnavigate) { debug('window.open', url, '_self'); window.top.open(url, '_self'); return false; } window.top.onnavigate(url); return false; } debug('window.open', url, win); window.top.open(url, win); return false; } /** * Bind a click handler to a widget. */ ui.ontouchstart = function(widget, e) { debug('ui.ontouchstart', widget.id); widget.down = true; widget.moved = false; var t = e.touches[0]; widget.moveX = t.clientX; widget.moveY = t.clientY; }; ui.ontouchmove = function(widget, e) { //debug('ontouchmove'); var t = e.touches[0]; if (t.clientX != widget.moveX) { widget.moveX = t.clientX; widget.moved = true; } if (t.clientY != widget.moveY) { widget.moveY = t.clientY; widget.moved = true; } }; ui.ontouchend = function(widget, e) { debug('ui.ontouchend', widget.id); widget.down = false; if (!widget.moved) { e.preventDefault(); debug('ui.fastonclick', widget.id); return widget.onclick(e); } }; ui.onclick = function(widget, handler) { if (ui.isMobile()) { widget.ontouchstart = function(e) { return ui.ontouchstart(widget, e); }; widget.ontouchmove = function(e) { return ui.ontouchmove(widget, e); }; widget.ontouchend = function(e) { return ui.ontouchend(widget, e); }; } widget.onclick = function(e) { debug('ui.onclick', widget.id); return handler(e); }; return widget; }; /** * Build a portable tag. */ ui.href = function(id, loc, target, clazz, html) { if (target == '_blank') return '' + html + ''; return '' + html + ''; }; /** * Update a tag. */ ui.updateHref = function(item, loc, target) { if (!isNull(loc) && !isNull(target)) { var link = item.parentNode; if (target == '_blank') { link.href = loc; link.target = '_blank'; } else { link.href = loc; link.setAttribute('onclick', 'return ui.navigate(\'' + loc + '\', \'' + target + '\');'); } } return item; }; /** * Build a menu bar. */ ui.menuItem = function(id, name, href, target, hilight) { function Menu() { this.content = function() { if (hilight == true) return ui.href(id, href, target, 'tbarsmenu', name); else if (hilight == false) return ui.href(id, href, target, 'tbaramenu', name); else return ui.href(id, href, target, hilight, name); }; } return new Menu(); }; ui.textItem = function(id, name, hilight) { function Item() { this.content = function() { return name; }; } return new Item(); }; ui.menuFunc = function(id, name, fun, hilight) { function Menu() { this.content = function() { function href(id, fun, clazz, html) { return '' + html + ''; } if (hilight == true) return href(id, fun, 'tbarsmenu', name); else if (hilight == false) return href(id, fun, 'tbaramenu', name); else return href(id, fun, hilight, name); }; } return new Menu(); }; ui.menuBar = function(left, center, right) { return '' + reduce(function(bar, item) { return bar + '' + item.content() + ''; }, '', center) + '' + reduce(function(bar, item) { return bar + '' + item.content() + ''; }, '', left) + reduce(function(bar, item) { return bar + '' + item.content() + ''; }, '', right) + ''; }; ui.cmenuBar = function(items) { return reduce(function(bar, item) { return bar + '' + item.content() + ''; }, '', items); }; /** * Update a menu item. */ ui.updateMenuItem = function(item, name, href, target, hilight) { if (!isNull(name)) { if (item.innerHTML != name) item.innerHTML = name; } if (!isNull(hilight)) { if (hilight == true) ui.setClass(item, 'tbarsmenu'); else if (hilight == false) ui.setClass(item, 'tbaramenu'); else ui.setClass(item, hilight); } if (!isNull(href) && !isNull(target)) { var link = item.parentNode; if (target == '_blank') { link.href = href; link.target = '_blank'; } else { link.href = href; link.setAttribute('onclick', 'return ui.navigate(\'' + href + '\', \'' + target + '\');'); } } return item; }; /** * Convert a list of elements to an HTML table. */ ui.dataTable = function(l) { function indent(i) { if (i == 0) return ''; return '  ' + indent(i - 1); } function rows(l, i) { if (isNull(l)) return ''; var e = car(l); // Convert a list of simple values into a list of name value pairs if (!isList(e)) return rows(expandElementValues("'value", l), i); // Convert a list of complex values into a list of name value pairs if (isList(car(e))) return rows(expandElementValues("'value", l), i); // Generate table row for a simple element value if (elementHasValue(e)) { var v = elementValue(e); if (!isList(v)) return '' + indent(i) + elementName(e).substring(1) + '' + '' + (v != null? v : '') + '' + rows(cdr(l), i); return rows(expandElementValues(elementName(e), v), i) + rows(cdr(l), i); } // Generate table row for an element with children return '' + indent(i) + elementName(e).substring(1) + '' + '' + '' + rows(elementChildren(e), i + 1) + rows(cdr(l), i); } return '' + rows(l, 0) + '
'; }; /** * Convert a list of elements to an HTML single column table. */ ui.dataList = function(l) { function rows(l, i) { if (isNull(l)) return ''; var e = car(l); // Convert a list of simple values into a list of name value pairs if (!isList(e)) return rows(expandElementValues("'value", l), i); // Convert a list of complex values into a list of name value pairs if (isList(car(e))) return rows(expandElementValues("'value", l), i); // Generate table row for a simple element value if (elementHasValue(e)) { var v = elementValue(e); if (!isList(v)) return '' + (v != null? v : '') + '' + rows(cdr(l), i); return rows(expandElementValues(elementName(e), v), i) + rows(cdr(l), i); } // Generate rows for an element's children return rows(elementChildren(e), i + 1) + rows(cdr(l), i); } return '' + rows(l, 0) + '
'; }; /** * 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); }; /** * Draw an image on a canvas and convert it to a data URL. */ ui.drawImage = function(img, onload, width, height, crop) { // Rotate an image function rotate(icanvas, onload) { debug('ui.drawImage.rotate'); var img = document.createElement('img'); img.onload = function() { var canvas = document.createElement('canvas'); canvas.width = icanvas.height; canvas.height = icanvas.width; var ctx = canvas.getContext('2d'); ctx.setTransform(0, 1, -1, 0, icanvas.height, 0); ctx.drawImage(img, 0, 0, icanvas.width, icanvas.height); onload(canvas); }; img.src = icanvas.toDataURL('image/png') return true; } // Draw the image on a canvas and convert it to a JPEG data URL function draw(img, onload, sx, sy, swidth, sheight, tx, ty, twidth, theight) { debug('ui.drawImage.draw', sx, sy, swidth, sheight, tx, ty, twidth, theight); var canvas = document.createElement('canvas'); canvas.width = twidth; canvas.height = theight; var ctx = canvas.getContext('2d'); ctx.drawImage(img, sx, sy, swidth, sheight, tx, ty, twidth, theight); return onload(canvas); } // Resize and optionally crop an image function resize(img, onload, width, height, oncrop) { debug('ui.drawImage.resize'); var iwidth = img.width; var iheight = img.height; if (width || height || crop) { if (crop) { // Crop to fit target canvas size var tratio = width / height; var oratio = iwidth / iheight; if (tratio > oratio) { var scale = width / iwidth; var cwidth = iwidth; var cheight = height / scale; var cut = (iheight - cheight) / 2; // Crop top and bottom edges, then resize return draw(img, onload, 0, cut, cwidth, cut + cheight, 0, 0, width, height); } else if (tratio < oratio) { var scale = height / iheight; var cwidth = width / scale; var cheight = iheight; var cut = (iwidth - cwidth) / 2; // Crop left and right edges, then resize return draw(img, onload, cut, 0, cut + cwidth, cheight, 0, 0, width, height); } else { // Just resize return draw(img, onload, 0, 0, iwidth, iheight, 0, 0, width, height); } } else { // Resize to make the image fit if (iwidth <= width && iheight == height) { return draw(img, onload, 0, 0, iwidth, iheight, 0, 0, iwidth, iheight); } else { var tratio = width / height; var oratio = iwidth / iheight; if (tratio > oratio) { // Resize to make height fit var scale = height / iheight; var swidth = iwidth * scale; var sheight = height; return draw(img, onload, 0, 0, iwidth, iheight, 0, 0, swidth, sheight); } else if (tratio < oratio) { // Resize to make width fit var scale = width / iwidth; var swidth = width; var sheight = iheight * scale; return draw(img, onload, 0, 0, iwidth, iheight, 0, 0, swidth, sheight); } else { // Resize to make both width and height fit return draw(img, onload, 0, 0, iwidth, iheight, 0, 0, width, height); } } } } else { // Draw image as is return draw(img, onload, 0, 0, iwidth, iheight, 0, 0, iwidth, iheight); } } // Draw the image, optionally rotate, scale and crop it (function drawImage() { var iwidth = img.width; var iheight = img.height; document.body.removeChild(img); var nwidth = img.width; var nheight = img.height; debug('ui.drawImage', 'img.width', iwidth, 'img.height', iheight, 'nwidth', nwidth, 'nheight', nheight, 'width', width, 'height', height, 'crop', crop); if (iwidth != iheight && iwidth == nheight) // Rotate and resize the image return resize(img, function(canvas) { return rotate(canvas, function(canvas) { return onload(canvas.toDataURL('image/jpeg', 0.95)); }); }, height, width, crop); else { // Just resize the image return resize(img, function(canvas) { return onload(canvas.toDataURL('image/jpeg', 0.95)); }, width, height, crop); } })(); } /** * Read an image url and convert it to a data url. */ ui.readImageURL = function(url, onerror, onprogress, onload, width, height, crop) { if(!width && !height && !crop && url.substring(0, 5) == 'data:') { // Just use the given data URL if we're not resizing the image debug('ui.readImageURL', 'original url'); onprogress(90); ui.delay(function() { return onload(url); }); return true; } // Create an image var img = document.createElement('img'); ui.setStyle(img, 'visibility', 'hidden'); document.body.appendChild(img); img.onerror = function(e) { document.body.removeChild(img); return onerror(); }; img.onload = function() { // Draw the image debug('ui.readImageURL', 'new data url'); return ui.drawImage(img, onload, width, height, crop); }; // Load the image onprogress(90); img.src = url; return true; }; /** * Read an image file or url and convert it to a data url. */ ui.readImageFile = function(img, onerror, onprogress, onload, width, height, crop) { if (isString(img)) return ui.readImageURL(img, onerror, onprogress, onload, width, height, crop); return ui.readFile(img, onerror, onprogress, function onfile(url) { return ui.readImageURL(url, onerror, onprogress, onload, width, height, crop); }); }; /** * Read an image and convert it to a data url. */ ui.readImage = function(img, onload, width, height, crop) { if(!width && !height && img.src.substring(0, 5) == 'data:') { // Just use the given data URL if we're not resizing the image return onload(img.src); } // Draw the image return ui.drawImage(img, onload, width, height, crop); };