UNPKG

aced

Version:

php+node light FE/BE framework

772 lines (719 loc) 20.8 kB
;(function(){ var initializing = false; this.AceBase = function(){}; AceBase.extend = function(o){ initializing = true; var n = new this; initializing = false; for (var k in o) n[k] = o[k]; n.super = this.prototype; function AceBase() { // #AB0 if (!initializing && this.init instanceof Function) this.init.apply(this,arguments); /* // #AB1 var z = this,args=arguments; if (!initializing && this.init instanceof Function) { setTimeout(function(){ z.init.apply(z,args); },0); } */ } AceBase.prototype = n; AceBase.prototype.constructor = AceBase; AceBase.extend = arguments.callee; return AceBase; } }()); AceBase.prototype.on = function(key,cb){ var keys = key.split(/ +/), i; for (i=0;i<keys.length;++i) this._getEvt(keys[i]).subs.push({ cb: cb }); return this; } AceBase.prototype.ready = function(key,cb){ var keys = key.split(/ +/), evt, i; for (i=0;i<keys.length;++i) { evt = this._getEvt(keys[i]); if (evt.firedOnce) { cb(evt.error,evt.data); } else { evt.subs.push({ cb: cb, typeReady: true }); } } return this; } AceBase.prototype.off = function(key,cb){ var keys = key.split(/ +/) ,i = 0, n = 0 ,evt, undef ; for (;i<keys.length;++i) { if (!this._evts || !this._evts[keys[i]]) continue; evt = this._getEvt(keys[i]); if (cb === undef) { evt.subs = []; } else { for (n=0;n<evt.subs.length;++n) { // checking !sub in case this is called in callback inside fireSubs if (!evt.subs[n] || evt.subs[n].cb == cb) evt.subs[n] = null; } ace.util.arrayFilter(evt.subs, function(sub){ return sub !== null; }); } } return this; } AceBase.prototype.trigger = function(key,error,data){ var keys = key.split(/ +/), evt, i; for (i=0;i<keys.length;++i) { evt = this._getEvt(keys[i]); evt.firedOnce = true; evt.error = error; evt.data = data; this._fireSubs(keys[i]); } return this; } AceBase.prototype._getEvt = function(key){ var undef; if (this._evts == undef) this._evts = {}; if (this._evts[key] == undef) { this._evts[key] = { subs: [] }; } return this._evts[key]; } AceBase.prototype._fireSubs = function(key){ var evt = this._getEvt(key), subs = evt.subs.slice(0); for (i=0;i<subs.length;++i) { subs[i].cb(evt.error,evt.data); } for (i=0;i<evt.subs.length;++i) { if (evt.subs[i].typeReady) evt.subs[i] = null; } ace.util.arrayFilter(evt.subs,function(sub){ return sub !== null; }); } AceBase.prototype.log = function(){ var args = [this.key||(this.config?this.config.key:null)||'anonymous AceBase'], i = 0; for (;i<arguments.length;++i) args.push(arguments[i]); console.log.apply(console,args); } ace = { config: { key: 'ace' ,readyCheckDelay: 15 } ,_appVersion: null ,_readyCbs: [] ,_ready: false ,init: function(){ var z = this; if (z.inited) return false; z.inited = true; (function checkReady(){ if (!window.$) return setTimeout(checkReady,z.config.readyCheckDelay); z._jQExtensions(); z.log('ready'); z._ready = true; $.each(z._readyCbs,function(i,cb){ if (cb instanceof Function) cb(); }); delete z._readyCbs; z.ui.checkForWidgets(); }()); } ,getAppVersion: function(cb){ var z = this; if (z._appVersion !== null) return setTimeout(function(){ cb(false, z._appVersion); },0); z.bus.ready('appversion',cb); if (z._getAppVersionCalledOnce) return; z._getAppVersionCalledOnce = true; // modify this method to suit your package // e.g. fetch current commit hash, or package.json version # z.req('app/version',function(err, data){ if (err) z.log('ERROR', 'failed to get app version', err); else z._appVersion = data.git_hash; ace.bus.trigger('appversion',err,z._appVersion); }); } ,ready: function(cb){ if (this._ready) cb(); else this._readyCbs.push(cb); } ,cssKey: function(module){ return this.config.key+'-'+(module.config.key||module.prototype.config.key); } ,ui: { _modules: {} ,register: function(key,proto){ var z = this; if (z.getModule(key)) return ace.log(key+' already registered'); ace.ready(function(){ // #AB0 var module = z._modules[key] = function(){ AceBase.call(this); }; module.prototype = new AceBase; module.prototype.constructor = module; $.extend(true,module.prototype,{ init: function(){} ,opts: {} },proto,{ key: key ,cssKey: 'ace-'+key }); /* // #AB1 var module = z._modules[key] = AceBase.extend(proto); module.prototype.key = key; module.prototype.cssKey = 'ace-'+key; */ module.instances = []; ace.bus.trigger(key+':registered'); }); } ,getModule: function(key){ return this._modules[key]; } ,moduleReady: function(moduleKey,cb){ var z = this; ace.bus.ready(moduleKey+':registered',function(){ ace.ready(cb); /* these are silly var module = z.getModule(moduleKey) ,i = 0 ,deps ; if (!(module.config && module.config.dependencies && module.config.dependencies instanceof Array && module.config.dependencies.length)) return depsRdy(); deps = module.config.dependencies; (function next(){ ace.bus.ready(deps[i]+':registered',function(){ if (++i == deps.length) return depsRdy(); next(); }); }()); function depsRdy(){ ace.ready(cb); } */ }); } ,checkForWidgets: function(jCont){ var z = this; $(function(){ (jCont || $('body')).find('script[type^="text/ace-"]').each(function(){ var $script = $(this) ,key = $script.attr('type').replace('text/ace-','') ,opts ; try { var optsStr = $.trim($script[0].innerHTML) opts = optsStr == '' ? {} : eval('('+optsStr+')'); } catch (e) { ace.log('ERROR', 'parsing widget opts', key, e); } if (typeof opts != 'object') opts = {}; z.widgetize(key,$script,opts); }); }); } ,widgetize: function(key,$cont,opts,cb){ var z = this; z.moduleReady(key,function(){ var $elm = $('<div class="ace-'+key+'"></div>') ,module = z.getModule(key) ,instance = new module() ; $cont.replaceWith($elm); instance.key = key; instance.opts = $.extend(true,{},module.prototype.opts,opts); instance.$ = { cont: $elm }; module.instances.push(instance); // #AB0 (#AB1 == init commented out) instance.init(); if (cb) cb.call(instance); }); } } ,bus: new AceBase ,util: { strToClass: function(str){ return (str+'').replace(/(^[^a-zA-Z]+)|([^a-zA-Z0-9_\-])/g,''); } ,setUniqueClassVal: function($el, prefix, val, sep){ if (!sep) sep = '-' var uniqClass = prefix+sep+val ,alreadyHasIt = $el.hasClass(alreadyHasIt) if (!alreadyHasIt) $el.addClass(uniqClass) this.removeClassWithPrefix($el, prefix+sep, val) return !alreadyHasIt } ,removeClassWithPrefix: function($el,prefix,except){ $el.each(function(i,el){ $el = $(el) $.each(($el.attr('class')||'').split(' '),function(i,name){ if (name.indexOf(prefix) == 0 && name != prefix+except) $el.removeClass(name) }) }) } ,rand: function(min,max){ return min+Math.round(Math.random()*(max-min)); } ,replaceAll: function(str,search,replace){ return str.split(search).join(replace) } ,isEmptyObject: function(obj){ for (var k in obj) return false; return true } ,capitalize: function(str, isName){ var words = str.split(' ') ,i,c,f2,f3 ; for (i=0,c=words.length;i<c;++i) { if (words[i]) { words[i] = words[i].toLowerCase(); words[i] = words[i].charAt(0).toUpperCase() + words[i].substr(1); if (isName) { // charAt faster than substr f2 = words[i].charAt(0)+words[i].charAt(1); if (f2 == 'Mc' || f2 == 'O\'') words[i] = f2+words[i][2].toUpperCase()+words[i].substr(3); else if ((f3 = f2+words[i].charAt(2)) == 'Mac') words[i] = f3+words[i][3].toUpperCase()+words[i].substr(4); if (words[i].indexOf('-') != -1) words[i] = this.capitalize(words[i].replace(/-/g,' '), true).replace(/ /g,'-'); } } } return words.join(' '); } ,escapeHtml: function(str,nl2br){ str = $('<div>').text(str).html(); if (nl2br) str = str.replace(/\n/g,'<br />') return str; } ,escapeRegEx: function(str){ // adds slashes to regex control chars return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); } ,formatPlace: function(num){ var numPos = Math.abs(num) ,lastChar = (num+'').substr(-1) ,suffix ; if (num == 0) return '0th'; if (numPos > 10 && numPos < 20) suffix = 'th'; else if (lastChar == 1) suffix = 'st'; else if (lastChar == 2) suffix = 'nd'; else if (lastChar == 3) suffix = 'rd'; else suffix = 'th'; return num+suffix; } ,formatInteger: function(num){ // #benched vs various regex, though some got close // format first number found with commas: formatInteger(1000) = '1,000' // will accept already-formatted number, and even fix badly formatted input: formatInteger('$10,00.00') == '$1,000.00' var num = num+'', numNums = 0, numStart, i for (i=0;i<num.length;++i) { if (isNaN(num[i])) { if (num[i] == ',') num = num.substr(0,i)+num.substr(i--+1); else if (numNums) break; } else if (++numNums == 1) numStart = i; } if (numNums > 3) { var n; for (i=3;i<numNums;i=i+3) { n = numStart+numNums-i; num = num.slice(0,n)+','+num.slice(n); } } return num; } ,formatTimeAgo: function(timestamp,now){ if (typeof timestamp == 'string' && isNaN(timestamp)) timestamp = new Date(timestamp); if (timestamp instanceof Date) timestamp = Math.round(+timestamp/1000); if (typeof now == 'string' && isNaN(now)) now = new Date(now); if (now instanceof Date) now = +now; now = typeof now == 'undefined' ? Math.round(+new Date/1000) : now; var secs = now - timestamp ,intervals = [ ['year',31536000] ,['month',2628000] ,['week',604800] ,['day',86400] ,['hour',3600] ,['minute',60] ],recent = 'just now' ,secs,str ; if (secs < 0) secs = 0; $.each(intervals,function(i,interval){ var ago = Math.floor(secs/interval[1]); if (ago > 0) { str = ago+' '+interval[0]+(ago==1?'':'s')+' ago' return false; } }); if (typeof str == 'undefined') str = recent; return str; } ,formatDate: function(date, length){ // accepts Date, or integer timestamp var z = this, d = date ? date : new Date, f; if (typeof d == 'number') d = new Date(d); if (isNaN(d.getTime())) return date; f = z.padZ(d.getMonth()+1)+'/'+z.padZ(d.getDate()); if (length != 'd') { f += '/'+d.getFullYear(); if (length != 'D') { f += ' '+z.padZ(d.getHours())+':'+z.padZ(d.getMinutes()); if (length != 'm') { f += ':'+z.padZ(d.getSeconds()); } } } return f; } ,getViewportScrollY: function(){ return (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop; } ,trueDim: function(jelm,includeMargin){ var nre = /[^0-9\-.]/g ,d = { w: jelm.width() ,h: jelm.height() } ,add,i,c ; add = ['border-left-width','padding-left','padding-right','border-right-width']; if (includeMargin) add.push.call(add, 'margin-left', 'margin-right'); for (i=0,c=add.length;i<c;++i) d.w += +(jelm.css(add[i])||'0').replace(nre,''); add = ['border-top-width','padding-top','padding-bottom','border-bottom-width']; if (includeMargin) add.push.call(add, 'margin-top', 'margin-bottom'); for (i=0,c=add.length;i<c;++i) d.h += +(jelm.css(add[i])||'0').replace(nre,''); return d; } ,arrayFilter: function(arr,cb,start){ var i,c; start = typeof start == 'number' ? start : 0; for (i=start,c=arr.length;i<c;++i) { if (!cb(arr[i])) { arr.splice(i,1); this.arrayFilter(arr,cb,i); break; } } return arr; } ,getImageToWindowFit: function(windowSize,imgSize,center){ // Deprecated in favor of background-size:cover for most use cases /** ex: center = [null,50/100] center = [0.2,0.8] **/ windowSize = [+windowSize[0],+windowSize[1]]; imgSize = [+imgSize[0],+imgSize[1]]; if (!center) center = [null,null]; center = [center[0] === null ? null : +center[0],center[1] === null ? null : +center[1]]; var windowW2H = windowSize[0]/windowSize[1] ,imgW2H = imgSize[0]/imgSize[1] ,offsetX = 0, offsetY = 0 ,newWidth,newHeight,fit ; if (windowW2H > imgW2H) { newWidth = windowSize[0]; newHeight = newWidth/imgW2H; if (newHeight < windowSize[1]) newHeight = windowSize[1]; offsetY = -1 * (newHeight-windowSize[1])/2; if (center[1] !== null) offsetY += (.5-center[1])*newHeight; if (offsetY > 0) offsetY = 0; else if (offsetY < windowSize[1]-newHeight) offsetY = windowSize[1]-newHeight; } else { newHeight = windowSize[1]; newWidth = newHeight*imgW2H; if (newWidth < windowSize[0]) newWidth = windowSize[0]; offsetX = -1*(newWidth-windowSize[0])/2; if (center[0] !== null) offsetX += (.5-center[0])*newWidth; if (offsetX > 0) offsetX = 0; else if (offsetX < windowSize[0]-newWidth) offsetX = windowSize[0]-newWidth; } fit = { width: newWidth ,height: newHeight ,offset: { x: offsetX ,y: offsetY } }; fit.style = 'width:'+fit.width+'px;height:'+fit.height+'px;left:'+fit.offset.x+'px;top:'+fit.offset.y+'px;'; fit.css = { width: fit.width+'px' ,height: fit.height+'px' ,left: fit.offset.x+'px' ,top: fit.offset.y+'px' }; return fit; } ,isWithinViewport: function($elm){ var scrollY = this.getViewportScrollY() ,viewHeight = $(window).height() ,elmY,elmHeight if (typeof $elm == 'number') { elmY = $elm elmHeight = 0 } else { elmY = $elm.offset().top elmHeight = this.trueDim($elm).h } return !!((elmY > scrollY && elmY < scrollY+viewHeight) || (elmY+elmHeight > scrollY && elmY+elmHeight < scrollY+viewHeight)) } ,isFullyWithinViewport: function($elm){ var y = $elm.offset().top ,height = this.trueDim($elm).h return this.isWithinViewport(y) && this.isWithinViewport(y+height) } ,onTouchDevice: function(){ return 'ontouchstart' in document.documentElement; } ,getParameterByName: function(name, url){ url = (url || window.location.href) name = name.replace(/[\[]/, "\\\\[").replace(/[\]]/, "\\\\]"); var regexS = "[\\?&]" + name + "=([^&#]*)" ,regex = new RegExp(regexS) ,results = regex.exec(url) ; return results === null ? false : decodeURIComponent(results[1].replace(/\+/g, " ")); } ,parseCookies: function(cookie){ cookie = cookie || document.cookie; var cookies = cookie.split(';') ,res = {} ; $.each(cookies,function(i,v){ var split = v.split('=') ,key = unescape(split[0][0] == ' ' ? split[0].substr(1) : split[0]) ,val = unescape(split[1]||'') ; if (key != '') res[key] = val; }); return res; } ,setCookie: function(key,val,opts){ var undef,expires,set; if (typeof opts == 'number' || typeof opts == 'string') // allow 3rd argument to == expires opts = {expires:opts}; opts = (opts && typeof opts == 'object') ? opts : {}; if (val == undef) opts.expires = -1; if (typeof opts.expires == 'number') { expires = new Date; expires.setMilliseconds(expires.getMilliseconds()+opts.expires); expires = expires.toUTCString(); } else if (typeof opts.expires == 'string') { expires = opts.expires; } set = (document.cookie = [ escape(key),'=',escape(val) ,expires == undef ? '' : '; expires='+expires ,opts.path == undef ? '; path=/' : '; path='+opts.path ,opts.domain == undef ? '' : '; domain='+opts.domain ,opts.secure ? ';secure' : '' ].join('')); return document.cookie = set; } ,getCookie: function(key){ return this.parseCookies()[key]; } ,deleteCookie: function(key){ return this.setCookie(key, null); } ,padZ: function(n,m){ if (typeof m == 'undefined') m = 2; while ((n+'').length < m) n = '0'+n; return n; } ,hash: function(str){ // convert string to integer. one way var hash = 0, i, l; str += ''; for (i=0,l=str.length;i<l;++i) { hash = (hash<<5)-hash+str.charCodeAt(i); hash |= 0; } return hash; } ,obfu: function (str, salt){ var bound = 5, boundLimit = Math.pow(10,bound), hash = '', i, l, charCode; str += ''; for (i=0,l=str.length;i<l;++i) { charCode = str.charCodeAt(i); if (salt) charCode = (charCode + (salt+'').charCodeAt(i%salt.length))%boundLimit; charCode = this.padZ(charCode,bound); hash += charCode; } return hash; } ,deobfu: function(hash, salt){ var bound = 5, boundLimit = Math.pow(10,bound), str = '', chunk = '', n = 0, i, l; hash += ''; for (i=0,l=hash.length;i<l;++i) { chunk += hash[i]; if (chunk.length == bound) { if (salt) chunk = Math.abs( (chunk - (salt+'').charCodeAt(n%salt.length))%boundLimit ); str += String.fromCharCode(chunk); chunk = ''; ++n; } } return str; } /* ace.util.stdErrAlert() ace.util.stdErrAlert('bad things!') ace.util.stdErrAlert({error:'argh!'}) ace.util.stdErrAlert({code:123}) ace.util.stdErrAlert({code:456,error:'doh!'}) ace.util.stdErrAlert('rawr',{error:'argh!'}) ace.util.stdErrAlert('customMsg','errorMsg') ace.util.stdErrAlert('pshhh',ace.ui._modules.test_schedules.instances[0]) ace.util.stdErrAlert('whaaaat?',{details:'are cool'},ace.ui._modules.test_schedules.instances[0]) ace.util.stdErrAlert('whaaaat?',{error:'im an error'},{details:'are cool'},ace.ui._modules.test_schedules.instances[0]) */ ,stdErrAlert: function(/*customMsg,err,res,module*/){ var customMsg,err,res,module,undef for (var i=0;i<arguments.length;++i) switch (typeof arguments[i]) { case 'string': !customMsg ? (customMsg=arguments[i]) : err={error:arguments[i]}; break; case 'object': arguments[i] instanceof AceBase ? (module=arguments[i]) : (!err && (arguments[i].code !== undef || arguments[i].error !== undef)) ? (err = arguments[i]) : (res = arguments[i]); break; } if (!res && err && err.code === undef && err.error === undef) res = err if (res instanceof AceBase) { module = res res = null } //ace.log('customMsg',customMsg,'err',err,'res',res,'module',module) var header = 'Oh neeooooo.... We have has had errrs!' ,msg = [ 'Please enjoy this picture of a cute puppy while we fix the problem<br /><br /><div style="text-align:center"><img src="/assets/cute_puppy-sm.jpg" alt="" /></div>' ] ; if (err) msg.unshift('('+(err&&err.code?err.code:'??')+') '+(err&&err.error?err.error:'unknown error')) if (customMsg) msg.unshift(customMsg) var body = msg.join('<br /><br />'); (module&&typeof(module.log)=='function' ? module : ace).log('ERROR', 'util.stdErrAlert', err) if (res) { body += '<br /><a href="#" class="show-more-detail">more detail &raquo;&raquo;</a>'; body += '<div class="more-detail" style="display:none;"><pre><code>'+JSON.stringify(res,null,3)+'</code></pre></div>'; } var pop = ace.pop({ header: header ,body: body ,classes: 'ace-pop-stderralert' + (module&&module.cssKey ? ' '+module.cssKey+'-pop' : '') }) pop.$.cont.find('a.show-more-detail').bind('click',function(e){ e.preventDefault(); pop.$.cont.find('.more-detail').css('display',''); }) } } ,_jQExtensions: function(){ $.fn.imagesLoaded = function(cb){ var $elm = this ,$imgs = this.find('img').andSelf().filter('img') ,imgs = [] ,numLoaded = 0 ,numImgs,loaded ; numImgs = $imgs.length; if (!numImgs) return done(); loaded = function(index){ if (!imgs[index]) { imgs[index] = true; if (++numLoaded == numImgs) done(); } }; $imgs.each(function(index){ var doIt = function(){ loaded(index); }; imgs.push(false); if (this.complete) { doIt(); } else { $(this).bind('load',doIt).bind('error',doIt); if (this.complete) // @todo: should check naturalWidth!==undef too for #xbrowser? try IE doIt(); } }); function done(){ if (cb) { setTimeout(function(){ cb.call($elm); },0); } } return this; } $.fn.widgetize = function(widgetName, opts){ ace.ui.widgetize(widgetName, this, opts); } } }; ace.log = AceBase.prototype.log;