diff --git a/resources/static/dialog/controllers/authenticate_controller.js b/resources/static/dialog/controllers/authenticate_controller.js index f064f0ca5782fa0310a8d9997d4d9beb0ecef915..b270a65d7412058fe78f728e0c2b6cdfdec3b48b 100644 --- a/resources/static/dialog/controllers/authenticate_controller.js +++ b/resources/static/dialog/controllers/authenticate_controller.js @@ -43,10 +43,11 @@ errors = bid.Errors, validation = bid.Validation, tooltip = bid.Tooltip, + dom = bid.DOM, lastEmail = ""; function getEmail() { - return $("#email").val().trim(); + return dom.getInner("#email").trim(); } function checkEmail(el, event) { @@ -89,18 +90,18 @@ function authenticate(el, event) { var email = getEmail(), - pass = $("#password").val(), + pass = dom.getInner("#password"), self = this; cancelEvent(event); if (!validation.emailAndPassword(email, pass)) return; - user.authenticate(email, pass, + user.authenticate(email, pass, function onComplete(authenticated) { if (authenticated) { self.close("authenticated", { - email: email + email: email }); } else { bid.Tooltip.showTooltip("#cannot_authenticate"); @@ -149,7 +150,7 @@ self.publish("enter_password"); self.submit = authenticate; animateSwap(".start:visible,.newuser:visible,.forgot:visible", ".returning", function() { - $("#password").focus(); + dom.getElements("#password")[0].focus(); }); } @@ -157,7 +158,7 @@ cancelEvent(event); this.submit = resetPassword; - $("#email").attr("disabled", "disabled"); + dom.setAttr("#email", "disabled", "disabled"); animateSwap(".start:visible,.newuser:visible,.returning:visible", ".forgot"); } @@ -165,8 +166,8 @@ function cancelForgotPassword(el, event) { cancelEvent(event); - $("#email").removeAttr("disabled"); - enterPasswordState.call(this); + dom.removeAttr("#email", "disabled"); + enterPasswordState.call(this); } function createUserState(el, event) { @@ -193,7 +194,7 @@ }); this.submit = checkEmail; - // If we already have an email address, check if it is valid, if so, show + // If we already have an email address, check if it is valid, if so, show // password. if (options.email) this.submit(); }, diff --git a/resources/static/dialog/controllers/dialog_controller.js b/resources/static/dialog/controllers/dialog_controller.js index c2d3562fe66d8c0d458b0f8d46051e1538979028..e919c3b45f944a541ef2c1b0412bbb7261ac2850 100644 --- a/resources/static/dialog/controllers/dialog_controller.js +++ b/resources/static/dialog/controllers/dialog_controller.js @@ -1,5 +1,5 @@ -/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */ -/*global setupChannel:true, BrowserID: true, PageController: true, OpenAjax: true */ +/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */ +/*global setupChannel:true, BrowserID: true, PageController: true, OpenAjax: true */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -45,11 +45,12 @@ var bid = BrowserID, user = bid.User, errors = bid.Errors, + dom = bid.DOM, offline = false, win = window, subscriptions = [], hub = OpenAjax.hub; - + function subscribe(message, cb) { subscriptions.push(hub.subscribe(message, cb)); } @@ -84,12 +85,12 @@ var subscription; while(subscription = subscriptions.pop()) { - hub.unsubscribe(subscription); + hub.unsubscribe(subscription); } this._super(); }, - + getVerifiedEmail: function(origin_url, onsuccess, onerror) { var self=this; self.onsuccess = onsuccess; @@ -101,11 +102,11 @@ } user.setOrigin(origin_url); - $("#sitename").text(user.getHostname()); + dom.setInner("#sitename", user.getHostname()); self.doCheckAuth(); - $(win).bind("unload", function() { + dom.bindEvent(win, "unload", function() { bid.Storage.setStagedOnBehalfOf(""); self.doCancel(); }); @@ -113,7 +114,7 @@ stateMachine: function() { - var self=this, + var self=this, el = this.element; subscribe("offline", function(msg, info) { @@ -135,7 +136,7 @@ subscribe("authenticated", function(msg, info) { //self.doEmailSelected(info.email); - // XXX benadida, lloyd - swap these two if you want to experiment with + // XXX benadida, lloyd - swap these two if you want to experiment with // generating assertions directly from signin. self.syncEmails(); }); @@ -217,8 +218,8 @@ doPickEmail: function() { this.element.pickemail({ - // XXX ideal is to get rid of this and have a User function - // that takes care of getting email addresses AND the last used email + // XXX ideal is to get rid of this and have a User function + // that takes care of getting email addresses AND the last used email // for this site. origin: user.getHostname() }); @@ -230,7 +231,7 @@ doForgotPassword: function(email) { this.element.forgotpassword({ - email: email + email: email }); }, @@ -255,8 +256,8 @@ doAssertionGenerated: function(assertion) { var self=this; - // Clear onerror before the call to onsuccess - the code to onsuccess - // calls window.close, which would trigger the onerror callback if we + // Clear onerror before the call to onsuccess - the code to onsuccess + // calls window.close, which would trigger the onerror callback if we // tried this afterwards. self.onerror = null; self.onsuccess(assertion); @@ -269,20 +270,20 @@ syncEmails: function() { var self = this; - user.syncEmails(self.doPickEmail.bind(self), + user.syncEmails(self.doPickEmail.bind(self), self.getErrorDialog(errors.signIn)); }, doCheckAuth: function() { var self=this; - user.checkAuthenticationAndSync(function onSuccess() {}, + user.checkAuthenticationAndSync(function onSuccess() {}, function onComplete(authenticated) { if (authenticated) { self.doPickEmail(); } else { self.doAuthenticate(); } - }, + }, self.getErrorDialog(errors.checkAuthentication)); } diff --git a/resources/static/dialog/controllers/page_controller.js b/resources/static/dialog/controllers/page_controller.js index 70113e8ad0d3165f32734415893fd530125303a6..9ff9f3386ebb3bc0ebfad7a502a7fcd99bdf9d24 100644 --- a/resources/static/dialog/controllers/page_controller.js +++ b/resources/static/dialog/controllers/page_controller.js @@ -1,4 +1,4 @@ -/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */ +/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */ /*global BrowserID: true*/ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -37,7 +37,9 @@ (function() { "use strict"; - var ANIMATION_TIME = 250; + var ANIMATION_TIME = 250, + bid = BrowserID, + dom = bid.DOM; $.Controller.extend("PageController", { @@ -67,53 +69,60 @@ } // XXX move all of these, bleck. - $("form").bind("submit", me.onSubmit.bind(me)); - $("#cancel").click(me.onCancel.bind(me)); - $("#back").click(me.onBack.bind(me)); - $("#thisIsNotMe").click(me.close.bind(me, "notme")); + dom.bindEvent("form", "submit", me.onSubmit.bind(me)); + dom.bindEvent("#cancel", "click", me.onCancel.bind(me)); + dom.bindEvent("#back", "click", me.onBack.bind(me)); + dom.bindEvent("#thisIsNotMe", "click", me.close.bind(me, "notme")); }, destroy: function() { - $("form").unbind("submit"); - $("input").unbind("keyup"); - $("#cancel").unbind("click"); - $("#back").unbind("click"); - $("#thisIsNotMe").unbind("click"); + dom.unbindEvent("form", "submit"); + dom.unbindEvent("input", "keyup"); + dom.unbindEvent("#cancel", "click"); + dom.unbindEvent("#back", "click"); + dom.unbindEvent("#thisIsNotMe", "click"); - $("body").removeClass("waiting"); + dom.removeClass("body", "waiting"); this._super(); }, renderTemplates: function(target, body, body_vars) { if (body) { - var bodyHtml = $.View("//dialog/views/" + body, body_vars); + var bodyHtml = new EJS({url: "/dialog/views/" + body}).render(body_vars); target = $(target + " .contents"); - target.html(bodyHtml).find("input").eq(0).focus(); + target.html(bodyHtml).find("input").eq(0).focus(); } }, renderDialog: function(body, body_vars) { this.renderTemplates("#formWrap", body, body_vars); - $("body").removeClass("error").removeClass("waiting").addClass("form"); + dom.removeClass("body", "error"); + dom.removeClass("body", "waiting"); + dom.addClass("body", "form"); $("#wait, #error").stop().fadeOut(ANIMATION_TIME); }, renderWait: function(body, body_vars) { this.renderTemplates("#wait", body, body_vars); - $("body").removeClass("error").removeClass("form").addClass("waiting").css('opacity', 1); + dom.removeClass("body", "error"); + dom.removeClass("body", "form"); + dom.addClass("body", "waiting"); + $("body").css('opacity', 1); $("#wait").stop().hide().fadeIn(ANIMATION_TIME); }, renderError: function(body, body_vars) { this.renderTemplates("#error", body, body_vars); - $("body").removeClass("waiting").removeClass("form").addClass("error"); + dom.removeClass("body", "waiting"); + dom.removeClass("body", "form"); + dom.addClass("body", "error"); $("#error").stop().css('opacity', 1).hide().fadeIn(ANIMATION_TIME); /** * What a big steaming pile, use CSS animations for this! */ - $("#openMoreInfo").click(function(event) { + dom.bindEvent("#openMoreInfo", "click", function(event) { event.preventDefault(); $("#moreInfo").slideDown(); @@ -142,7 +151,7 @@ doWait: function(info) { this.renderWait("wait.ejs", info); - $("body").addClass("waiting"); + dom.addClass("body", "waiting"); }, close: function(message, data) { @@ -155,7 +164,7 @@ /** * Get a curried function to an error dialog. * @method getErrorDialog - * @method {object} action - info to use for the error dialog. Should have + * @method {object} action - info to use for the error dialog. Should have * two fields, message, description. */ getErrorDialog: function(action) { diff --git a/resources/static/dialog/controllers/pickemail_controller.js b/resources/static/dialog/controllers/pickemail_controller.js index eda8675067f2cc817c7529d2451532bfaf7d1b78..e3cb978f95d2e7e465feefd0426728b0c204b0ca 100644 --- a/resources/static/dialog/controllers/pickemail_controller.js +++ b/resources/static/dialog/controllers/pickemail_controller.js @@ -1,5 +1,5 @@ -/*jshint brgwser:true, jQuery: true, forin: true, laxbreak:true */ -/*global _: true, BrowserID: true, PageController: true */ +/*jshint brgwser:true, jQuery: true, forin: true, laxbreak:true */ +/*global _: true, BrowserID: true, PageController: true */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -42,6 +42,7 @@ user = bid.User, errors = bid.Errors, storage = bid.Storage, + dom = bid.DOM, body = $("body"), animationComplete = body.innerWidth() < 640, assertion; @@ -104,7 +105,7 @@ } function getAssertion(email) { - // Kick of the assertion fetching/keypair generating while we are showing + // Kick of the assertion fetching/keypair generating while we are showing // the animation, hopefully this minimizes the delay the user notices. var self=this; user.getAssertion(email, function(assert) { @@ -122,7 +123,7 @@ animationComplete = true; tryClose.call(self); }); - }); + }); } else { tryClose.call(self); @@ -133,7 +134,7 @@ function signIn(element, event) { cancelEvent(event); var self=this, - email = $("input[type=radio]:checked").val(); + email = dom.getInner("input[type=radio]:checked"); var valid = checkEmail.call(self, email); if (valid) { @@ -145,7 +146,7 @@ } function addEmail(element, event) { - var email = $("#newEmail").val(), + var email = dom.getInner("#newEmail"), self=this; cancelEvent(event); @@ -183,18 +184,18 @@ bodyTemplate: "pickemail.ejs", bodyVars: { identities: user.getStoredEmailKeypairs(), - // XXX ideal is to get rid of this and have a User function - // that takes care of getting email addresses AND the last used email + // XXX ideal is to get rid of this and have a User function + // that takes care of getting email addresses AND the last used email // for this site. siteemail: storage.site.get(origin, "email"), remember: storage.site.get(origin, "remember") || false } }); - $("body").css("opacity", "1"); + body.css("opacity", "1"); - if($("#selectEmail input[type=radio]:visible").length === 0) { - // If there is only one email address, the radio button is never shown, + if(dom.getElements("#selectEmail input[type=radio]:visible").length === 0) { + // If there is only one email address, the radio button is never shown, // instead focus the sign in button so that the user can click enter. // issue #412 $("#signInButton").focus(); diff --git a/resources/static/dialog/dialog.js b/resources/static/dialog/dialog.js index 3651dacb8524211c0e7dbfb553e797d58b7070fa..c141b8f060929fc5130b8cc6e65404b9b1ee24be 100644 --- a/resources/static/dialog/dialog.js +++ b/resources/static/dialog/dialog.js @@ -41,15 +41,15 @@ window.console = window.console || { steal.plugins( 'jquery/controller', // a widget factory - 'jquery/controller/subscribe', // subscribe to OpenAjax.hub - 'jquery/view/ejs', // client side templates - 'jquery/controller/view') // lookup views with the controller's name + 'jquery/controller/subscribe') // subscribe to OpenAjax.hub .resources('jschannel', 'base64', 'underscore-min', 'channel', + 'ejs', 'browserid', + 'dom-jquery', 'storage', 'tooltip', 'validation', @@ -66,15 +66,15 @@ steal.plugins( 'checkregistration', 'pickemail') // loads files in controllers folder - .views('authenticate.ejs', +/* .views('authenticate.ejs', 'confirmemail.ejs', 'pickemail.ejs', 'wait.ejs', 'error.ejs', 'offline.ejs' ). - - then(function() { +*/ + .then(function() { $(function() { $('body').dialog().show(); }); diff --git a/resources/static/dialog/resources/dom-jquery.js b/resources/static/dialog/resources/dom-jquery.js new file mode 100644 index 0000000000000000000000000000000000000000..25d2a691f5a384485e2de772c920b92541c654d4 --- /dev/null +++ b/resources/static/dialog/resources/dom-jquery.js @@ -0,0 +1,300 @@ +/** +* Written by Shane Tomlinson - original source at: +* https://github.com/stomlinson/AFrame-JS/blob/master/src/adapters/jquery.js +* which is licensed under the Creative Commons Attribution 3.0 License. +* +* A DOM Manipulation adapter for jQuery. +* @class BrowserID.DOM +* @static +*/ +BrowserID.DOM = ( function() { + var jQuery = typeof( window ) !== 'undefined' && window.jQuery; + var DOM = { + /** + * Get a set of elements that match the selector + * @method getElements + * @param {selector || element} selector - if a string, a selector to search for. + * @return {array} array of elements + */ + getElements: function( selector ) { + return jQuery( selector ); + }, + + /** + * Get a set of descendent elements that match the selector + * @method getDescendentElements + * @param {string} selector - The selector to search for. + * @param {element} root - root node to search from + * @return {array} array of elements + */ + getDescendentElements: function( selector, root ) { + return jQuery( root ).find( selector ); + }, + + /** + * Get a set of descendent elements that match the selector, include the root node if it + * matches the selector + * @method getElementsIncludeRoot + * @param {string} selector - The selector to search for. + * @param {element} root - root node to search from + * @return {array} array of elements + */ + getElementsIncludeRoot: function( selector, root ) { + root = jQuery( root ); + var set = root.find( selector ); + if( root.is( selector ) ) { + set = root.add( set ); + } + return set; + }, + + /** + * Get the children for an element + * @method getChildren + * @param {selector || element} selector - element to get children for + * @return {array} an array of children + */ + getChildren: function( selector ) { + return jQuery( selector ).children(); + }, + + /** + * Get the nth child element + * @method getNthChild + * @param {selector || element} selector - element to get children for + * @param {number} index - index of the child to get + * @return {element} the nth child if it exists. + */ + getNthChild: function( selector, index ) { + return jQuery( selector ).children()[ index ]; + }, + + /** + * Iterate over a set of elements + * @method forEach + * @param {Elements} elements - elements to iterate over + * @param {function} callback - callback to call. Callback called with: callback( element, index ); + * @param {context} context - context to callback in + */ + forEach: function( elements, callback, context ) { + jQuery( elements ).each( function( index, element ) { + callback.call( context, element, index ); + } ); + }, + + /** + * Remove an element + * @method removeElement + * @param {selector || element} selector - element to remove + */ + removeElement: function( selector ) { + jQuery( selector ).remove(); + }, + + /** + * Bind to an elements DOM Event + * @method bindEvent + * @param {selector || element} element to bind on + * @param {string} eventName - name of event + * @param {function} callback - callback to call + */ + bindEvent: function( element, eventName, callback ) { + return jQuery( element ).bind( eventName, callback ); + }, + + /** + * Unbind an already bound DOM Event from an element. + * @method unbindEvent + * @param {selector || element} element to unbind from + * @param {string} eventName - name of event + * @param {function} callback - callback + */ + unbindEvent: function( element, eventName, callback ) { + return jQuery( element ).unbind( eventName, callback ); + }, + + /** + * Fire a DOM event on an element + * @method fireEvent + * @param {selector || element} element + * @param {string} type - event to fire + */ + fireEvent: function( element, type ) { + return jQuery( element ).trigger( type ); + }, + + /** + * Set the inner value of an element, including input elements + * @method setInner + * @param {selector || element} element - element to set + * @param {string} value - value to set + */ + setInner: function( element, value ) { + var target = jQuery( element ); + if( isValBased( target ) ) { + target.val( value ); + } + else { + target.html( value ); + } + + }, + + /** + * Get the inner value of an element, including input elements + * @method getInner + * @param {selector || element} element + * @return {string} inner value of the element + */ + getInner: function( element ) { + var target = jQuery( element ); + var retval = ''; + + if( isValBased( target ) ) { + retval = target.val(); + } + else { + retval = target.html(); + } + return retval; + }, + + /** + * Set an element's attribute. + * @method setAttr + * @param {selector || element} element + * @param {string} attrName - the attribute name + * @param {string} value - value to set + */ + setAttr: function( element, attrName, value ) { + jQuery( element ).attr( attrName, value ); + }, + + /** + * Get an element's attribute. + * @method getAttr + * @param {selector || element} element + * @param {string} attrName - the attribute name + * @return {string} attribute's value + */ + getAttr: function( element, attrName ) { + return jQuery( element ).attr( attrName ); + }, + + /** + * Check if an element has an attribute + * @method hasAttr + * @param {selector || element} element + * @param {string} attrName - the attribute name + * @return {boolean} true if the element has the attribute, false otw. + */ + hasAttr: function( element, attrName ) { + var val = jQuery( element )[ 0 ].getAttribute( attrName ); + return val !== null; + }, + + /** + * Remove an attribute from an element. + * @method removeAttr + * @param {selector || element} element + * @param {string} attrName - the attribute to remove + */ + removeAttr: function( element, attrName ) { + return jQuery( element ).removeAttr( attrName ); + }, + + /** + * Add a class to an element + * @method addClass + * @param {selector || element} element + * @param {string} className + */ + addClass: function( element, className ) { + jQuery( element ).addClass( className ); + }, + + /** + * Remove a class from an element + * @method removeClass + * @param {selector || element} element + * @param {string} className + */ + removeClass: function( element, className ) { + jQuery( element ).removeClass( className ); + }, + + /** + * Check if an element has a class + * @method hasClass + * @param {selector || element} element + * @param {string} className + * @return {boolean} true if element has class, false otw. + */ + hasClass: function( element, className ) { + return jQuery( element ).hasClass( className ); + }, + + /** + * Create an element + * @method createElement + * @param {string} type - element type + * @param {string} html (optional) - inner HTML + * @return {element} created element + */ + createElement: function( type, html ) { + var element = jQuery( '<' + type + '/>' ); + if( html ) { + BrowserID.DOM.setInner( element, html ); + } + return element; + }, + + /** + * Append an element as the last child of another element + * @method appendTo + * @param {selector || element} elementToInsert + * @param {selector || element} elementToAppendTo + */ + appendTo: function( elementToInsert, elementToAppendTo ) { + jQuery( elementToInsert ).appendTo( jQuery( elementToAppendTo ) ); + }, + + /** + * Insert an element before another element + * @method insertBefore + * @param {selector || element} elementToInsert + * @param {selector || element} elementToInsertBefore + */ + insertBefore: function( elementToInsert, elementToInsertBefore ) { + jQuery( elementToInsert ).insertBefore( elementToInsertBefore ); + }, + + /** + * Insert as the nth child of an element + * @method insertAsNthChild + * @param {selector || element} elementToInsert + * @param {selector || element} parent + * @param {number} index + */ + insertAsNthChild: function( elementToInsert, parent, index ) { + var children = jQuery( parent ).children(); + if( index === children.length ) { + elementToInsert.appendTo( parent ); + } + else { + var insertBefore = children.eq( index ); + elementToInsert.insertBefore( insertBefore ); + } + + } + + + }; + + function isValBased( target ) { + return target.is( 'input' ) || target.is( 'textarea' ); + } + + return DOM; + +}() ); diff --git a/resources/static/dialog/resources/ejs.js b/resources/static/dialog/resources/ejs.js new file mode 100644 index 0000000000000000000000000000000000000000..49d95252400213f7c1f2105344541485ebcb50aa --- /dev/null +++ b/resources/static/dialog/resources/ejs.js @@ -0,0 +1,505 @@ +(function(){ + + +var rsplit = function(string, regex) { + var result = regex.exec(string),retArr = new Array(), first_idx, last_idx, first_bit; + while (result != null) + { + first_idx = result.index; last_idx = regex.lastIndex; + if ((first_idx) != 0) + { + first_bit = string.substring(0,first_idx); + retArr.push(string.substring(0,first_idx)); + string = string.slice(first_idx); + } + retArr.push(result[0]); + string = string.slice(result[0].length); + result = regex.exec(string); + } + if (! string == '') + { + retArr.push(string); + } + return retArr; +}, +chop = function(string){ + return string.substr(0, string.length - 1); +}, +extend = function(d, s){ + for(var n in s){ + if(s.hasOwnProperty(n)) d[n] = s[n] + } +} + + +EJS = function( options ){ + options = typeof options == "string" ? {view: options} : options + this.set_options(options); + if(options.precompiled){ + this.template = {}; + this.template.process = options.precompiled; + EJS.update(this.name, this); + return; + } + if(options.element) + { + if(typeof options.element == 'string'){ + var name = options.element + options.element = document.getElementById( options.element ) + if(options.element == null) throw name+'does not exist!' + } + if(options.element.value){ + this.text = options.element.value + }else{ + this.text = options.element.innerHTML + } + this.name = options.element.id + this.type = '[' + }else if(options.url){ + options.url = EJS.endExt(options.url, this.extMatch); + this.name = this.name ? this.name : options.url; + var url = options.url + //options.view = options.absolute_url || options.view || options.; + var template = EJS.get(this.name /*url*/, this.cache); + if (template) return template; + if (template == EJS.INVALID_PATH) return null; + try{ + this.text = EJS.request( url+(this.cache ? '' : '?'+Math.random() )); + }catch(e){} + + if(this.text == null){ + throw( {type: 'EJS', message: 'There is no template at '+url} ); + } + //this.name = url; + } + var template = new EJS.Compiler(this.text, this.type); + + template.compile(options, this.name); + + + EJS.update(this.name, this); + this.template = template; +}; +/* @Prototype*/ +EJS.prototype = { + /** + * Renders an object with extra view helpers attached to the view. + * @param {Object} object data to be rendered + * @param {Object} extra_helpers an object with additonal view helpers + * @return {String} returns the result of the string + */ + render : function(object, extra_helpers){ + object = object || {}; + this._extra_helpers = extra_helpers; + var v = new EJS.Helpers(object, extra_helpers || {}); + return this.template.process.call(object, object,v); + }, + update : function(element, options){ + if(typeof element == 'string'){ + element = document.getElementById(element) + } + if(options == null){ + _template = this; + return function(object){ + EJS.prototype.update.call(_template, element, object) + } + } + if(typeof options == 'string'){ + params = {} + params.url = options + _template = this; + params.onComplete = function(request){ + var object = eval( request.responseText ) + EJS.prototype.update.call(_template, element, object) + } + EJS.ajax_request(params) + }else + { + element.innerHTML = this.render(options) + } + }, + out : function(){ + return this.template.out; + }, + /** + * Sets options on this view to be rendered with. + * @param {Object} options + */ + set_options : function(options){ + this.type = options.type || EJS.type; + this.cache = options.cache != null ? options.cache : EJS.cache; + this.text = options.text || null; + this.name = options.name || null; + this.ext = options.ext || EJS.ext; + this.extMatch = new RegExp(this.ext.replace(/\./, '\.')); + } +}; +EJS.endExt = function(path, match){ + if(!path) return null; + match.lastIndex = 0 + return path+ (match.test(path) ? '' : this.ext ) +} + + + + +/* @Static*/ +EJS.Scanner = function(source, left, right) { + + extend(this, + {left_delimiter: left +'%', + right_delimiter: '%'+right, + double_left: left+'%%', + double_right: '%%'+right, + left_equal: left+'%=', + left_comment: left+'%#'}) + + this.SplitRegexp = left=='[' ? /(\[%%)|(%%\])|(\[%=)|(\[%#)|(\[%)|(%\]\n)|(%\])|(\n)/ : new RegExp('('+this.double_left+')|(%%'+this.double_right+')|('+this.left_equal+')|('+this.left_comment+')|('+this.left_delimiter+')|('+this.right_delimiter+'\n)|('+this.right_delimiter+')|(\n)') ; + + this.source = source; + this.stag = null; + this.lines = 0; +}; + +EJS.Scanner.to_text = function(input){ + if(input == null || input === undefined) + return ''; + if(input instanceof Date) + return input.toDateString(); + if(input.toString) + return input.toString(); + return ''; +}; + +EJS.Scanner.prototype = { + scan: function(block) { + scanline = this.scanline; + regex = this.SplitRegexp; + if (! this.source == '') + { + var source_split = rsplit(this.source, /\n/); + for(var i=0; i<source_split.length; i++) { + var item = source_split[i]; + this.scanline(item, regex, block); + } + } + }, + scanline: function(line, regex, block) { + this.lines++; + var line_split = rsplit(line, regex); + for(var i=0; i<line_split.length; i++) { + var token = line_split[i]; + if (token != null) { + try{ + block(token, this); + }catch(e){ + throw {type: 'EJS.Scanner', line: this.lines}; + } + } + } + } +}; + + +EJS.Buffer = function(pre_cmd, post_cmd) { + this.line = new Array(); + this.script = ""; + this.pre_cmd = pre_cmd; + this.post_cmd = post_cmd; + for (var i=0; i<this.pre_cmd.length; i++) + { + this.push(pre_cmd[i]); + } +}; +EJS.Buffer.prototype = { + + push: function(cmd) { + this.line.push(cmd); + }, + + cr: function() { + this.script = this.script + this.line.join('; '); + this.line = new Array(); + this.script = this.script + "\n"; + }, + + close: function() { + if (this.line.length > 0) + { + for (var i=0; i<this.post_cmd.length; i++){ + this.push(pre_cmd[i]); + } + this.script = this.script + this.line.join('; '); + line = null; + } + } + +}; + + +EJS.Compiler = function(source, left) { + this.pre_cmd = ['var ___ViewO = [];']; + this.post_cmd = new Array(); + this.source = ' '; + if (source != null) + { + if (typeof source == 'string') + { + source = source.replace(/\r\n/g, "\n"); + source = source.replace(/\r/g, "\n"); + this.source = source; + }else if (source.innerHTML){ + this.source = source.innerHTML; + } + if (typeof this.source != 'string'){ + this.source = ""; + } + } + left = left || '<'; + var right = '>'; + switch(left) { + case '[': + right = ']'; + break; + case '<': + break; + default: + throw left+' is not a supported deliminator'; + break; + } + this.scanner = new EJS.Scanner(this.source, left, right); + this.out = ''; +}; +EJS.Compiler.prototype = { + compile: function(options, name) { + options = options || {}; + this.out = ''; + var put_cmd = "___ViewO.push("; + var insert_cmd = put_cmd; + var buff = new EJS.Buffer(this.pre_cmd, this.post_cmd); + var content = ''; + var clean = function(content) + { + content = content.replace(/\\/g, '\\\\'); + content = content.replace(/\n/g, '\\n'); + content = content.replace(/"/g, '\\"'); + return content; + }; + this.scanner.scan(function(token, scanner) { + if (scanner.stag == null) + { + switch(token) { + case '\n': + content = content + "\n"; + buff.push(put_cmd + '"' + clean(content) + '");'); + buff.cr(); + content = ''; + break; + case scanner.left_delimiter: + case scanner.left_equal: + case scanner.left_comment: + scanner.stag = token; + if (content.length > 0) + { + buff.push(put_cmd + '"' + clean(content) + '")'); + } + content = ''; + break; + case scanner.double_left: + content = content + scanner.left_delimiter; + break; + default: + content = content + token; + break; + } + } + else { + switch(token) { + case scanner.right_delimiter: + switch(scanner.stag) { + case scanner.left_delimiter: + if (content[content.length - 1] == '\n') + { + content = chop(content); + buff.push(content); + buff.cr(); + } + else { + buff.push(content); + } + break; + case scanner.left_equal: + buff.push(insert_cmd + "(EJS.Scanner.to_text(" + content + ")))"); + break; + } + scanner.stag = null; + content = ''; + break; + case scanner.double_right: + content = content + scanner.right_delimiter; + break; + default: + content = content + token; + break; + } + } + }); + if (content.length > 0) + { + // Chould be content.dump in Ruby + buff.push(put_cmd + '"' + clean(content) + '")'); + } + buff.close(); + this.out = buff.script + ";"; + var to_be_evaled = '/*'+name+'*/this.process = function(_CONTEXT,_VIEW) { try { with(_VIEW) { with (_CONTEXT) {'+this.out+" return ___ViewO.join('');}}}catch(e){e.lineNumber=null;throw e;}};"; + + try{ + eval(to_be_evaled); + }catch(e){ + if(typeof JSLINT != 'undefined'){ + JSLINT(this.out); + for(var i = 0; i < JSLINT.errors.length; i++){ + var error = JSLINT.errors[i]; + if(error.reason != "Unnecessary semicolon."){ + error.line++; + var e = new Error(); + e.lineNumber = error.line; + e.message = error.reason; + if(options.view) + e.fileName = options.view; + throw e; + } + } + }else{ + throw e; + } + } + } +}; + + +//type, cache, folder +/** + * Sets default options for all views + * @param {Object} options Set view with the following options + * <table class="options"> + <tbody><tr><th>Option</th><th>Default</th><th>Description</th></tr> + <tr> + <td>type</td> + <td>'<'</td> + <td>type of magic tags. Options are '<' or '[' + </td> + </tr> + <tr> + <td>cache</td> + <td>true in production mode, false in other modes</td> + <td>true to cache template. + </td> + </tr> + </tbody></table> + * + */ +EJS.config = function(options){ + EJS.cache = options.cache != null ? options.cache : EJS.cache; + EJS.type = options.type != null ? options.type : EJS.type; + EJS.ext = options.ext != null ? options.ext : EJS.ext; + + var templates_directory = EJS.templates_directory || {}; //nice and private container + EJS.templates_directory = templates_directory; + EJS.get = function(path, cache){ + if(cache == false) return null; + if(templates_directory[path]) return templates_directory[path]; + return null; + }; + + EJS.update = function(path, template) { + if(path == null) return; + templates_directory[path] = template ; + }; + + EJS.INVALID_PATH = -1; +}; +EJS.config( {cache: true, type: '<', ext: '.ejs' } ); + + + +/** + * @constructor + * By adding functions to EJS.Helpers.prototype, those functions will be available in the + * views. + * @init Creates a view helper. This function is called internally. You should never call it. + * @param {Object} data The data passed to the view. Helpers have access to it through this._data + */ +EJS.Helpers = function(data, extras){ + this._data = data; + this._extras = extras; + extend(this, extras ); +}; +/* @prototype*/ +EJS.Helpers.prototype = { + /** + * Renders a new view. If data is passed in, uses that to render the view. + * @param {Object} options standard options passed to a new view. + * @param {optional:Object} data + * @return {String} + */ + view: function(options, data, helpers){ + if(!helpers) helpers = this._extras + if(!data) data = this._data; + return new EJS(options).render(data, helpers); + }, + /** + * For a given value, tries to create a human representation. + * @param {Object} input the value being converted. + * @param {Object} null_text what text should be present if input == null or undefined, defaults to '' + * @return {String} + */ + to_text: function(input, null_text) { + if(input == null || input === undefined) return null_text || ''; + if(input instanceof Date) return input.toDateString(); + if(input.toString) return input.toString().replace(/\n/g, '<br />').replace(/''/g, "'"); + return ''; + } +}; + EJS.newRequest = function(){ + var factories = [function() { return new ActiveXObject("Msxml2.XMLHTTP"); },function() { return new XMLHttpRequest(); },function() { return new ActiveXObject("Microsoft.XMLHTTP"); }]; + for(var i = 0; i < factories.length; i++) { + try { + var request = factories[i](); + if (request != null) return request; + } + catch(e) { continue;} + } + } + + EJS.request = function(path){ + var request = new EJS.newRequest() + request.open("GET", path, false); + + try{request.send(null);} + catch(e){return null;} + + if ( request.status == 404 || request.status == 2 ||(request.status == 0 && request.responseText == '') ) return null; + + return request.responseText + } + EJS.ajax_request = function(params){ + params.method = ( params.method ? params.method : 'GET') + + var request = new EJS.newRequest(); + request.onreadystatechange = function(){ + if(request.readyState == 4){ + if(request.status == 200){ + params.onComplete(request) + }else + { + params.onComplete(request) + } + } + } + request.open(params.method, params.url) + request.send(null) + } + + +})(); \ No newline at end of file diff --git a/resources/static/test/qunit/qunit.js b/resources/static/test/qunit/qunit.js index c3722bed4afa027eb93cb038aabae29b696d60c5..68a0fe179559cf033fd635f0a7df5727dd78caa8 100644 --- a/resources/static/test/qunit/qunit.js +++ b/resources/static/test/qunit/qunit.js @@ -1,6 +1,7 @@ steal("/dialog/resources/browserid.js", "/test/qunit/mocks/mocks.js", "/test/qunit/mocks/xhr.js", + "/dialog/resources/ejs.js", "/dialog/resources/browser-support.js", "/dialog/resources/error-messages.js", "/dialog/resources/error-display.js", @@ -9,39 +10,32 @@ steal("/dialog/resources/browserid.js", "/dialog/resources/validation.js", "/dialog/resources/underscore-min.js" ) - .plugins( - "jquery", + .plugins("jquery", "jquery/controller", "jquery/controller/subscribe", - "jquery/controller/view", - "jquery/view/ejs", "funcunit/qunit") - .views('testBodyTemplate.ejs', - 'wait.ejs', - 'pickemail.ejs', - 'offline.ejs', - 'error.ejs') - .then("js/browserid_unit_test") - .then("js/page_helpers_unit_test") - .then("include_unit_test") - .then("relay/relay_unit_test") - .then("pages/add_email_address_test") - .then("pages/verify_email_address_test") - .then("pages/forgot_unit_test") - .then("pages/signin_unit_test") - .then("pages/signup_unit_test") - .then("pages/manage_account_unit_test") - .then("resources/tooltip_unit_test") - .then("resources/error-display_unit_test") - .then("resources/channel_unit_test") - .then("resources/browser-support_unit_test") - .then("resources/validation_unit_test") - .then("resources/storage_unit_test") - .then("resources/network_unit_test") - .then("resources/user_unit_test") - .then("controllers/page_controller_unit_test") - .then("controllers/pickemail_controller_unit_test") - .then("controllers/dialog_controller_unit_test") - .then("controllers/checkregistration_controller_unit_test") - .then("controllers/authenticate_controller_unit_test") + .then("/dialog/resources/dom-jquery.js", + "js/browserid_unit_test", + "js/page_helpers_unit_test", + "include_unit_test", + "relay/relay_unit_test", + "pages/add_email_address_test", + "pages/verify_email_address_test", + "pages/forgot_unit_test", + "pages/signin_unit_test", + "pages/signup_unit_test", + "pages/manage_account_unit_test", + "resources/tooltip_unit_test", + "resources/error-display_unit_test", + "resources/channel_unit_test", + "resources/browser-support_unit_test", + "resources/validation_unit_test", + "resources/storage_unit_test", + "resources/network_unit_test", + "resources/user_unit_test", + "controllers/page_controller_unit_test", + "controllers/pickemail_controller_unit_test", + "controllers/dialog_controller_unit_test", + "controllers/checkregistration_controller_unit_test", + "controllers/authenticate_controller_unit_test");