diff --git a/README b/README index fe7013d..9c828d4 100644 --- a/README +++ b/README @@ -1,256 +1,36 @@ -== Welcome to Rails +== Support Tool -Rails is a web-application framework that includes everything needed to create -database-backed web applications according to the Model-View-Control pattern. +Support tool is a web Frontend for administrating mailing list +This is a developement for the german Pirate Party. -This pattern splits the view (also called the presentation) into "dumb" -templates that are primarily responsible for inserting pre-built data in between -HTML tags. The model contains the "smart" domain objects (such as Account, -Product, Person, Post) that holds all the business logic and knows how to -persist themselves to a database. The controller handles the incoming requests -(such as Save New Account, Update Product, Show Post) by manipulating the model -and directing data to the view. +This software uses on Ruby on Rails for the server +and Backbone.js for the client. -In Rails, the model is handled by what's called an object-relational mapping -layer entitled Active Record. This layer allows you to present the data from -database rows as objects and embellish these data objects with business logic -methods. You can read more about Active Record in -link:files/vendor/rails/activerecord/README.html. +Author: Sebastian Lehbert -The controller and view are handled by the Action Pack, which handles both -layers by its two parts: Action View and Action Controller. These two layers -are bundled in a single package due to their heavy interdependence. This is -unlike the relationship between the Active Record and Action Pack that is much -more separate. Each of these packages can be used independently outside of -Rails. You can read more about Action Pack in -link:files/vendor/rails/actionpack/README.html. +http://opengermany.de (english) +http://closedgermany.de (german) +s (dot) lehbert (at) gmail (dot) com -== Getting Started -1. At the command prompt, create a new Rails application: - rails new myapp (where myapp is the application name) -2. Change directory to myapp and start the web server: - cd myapp; rails server (run with --help for options) +== License -3. Go to http://localhost:3000/ and you'll see: - "Welcome aboard: You're riding Ruby on Rails!" +This file is part of Support_tool. -4. Follow the guidelines to start developing your application. You can find -the following resources handy: + support_tool is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. -* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html -* Ruby on Rails Tutorial Book: http://www.railstutorial.org/ + support_tool is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - -== Debugging Rails - -Sometimes your application goes wrong. Fortunately there are a lot of tools that -will help you debug it and get it back on the rails. - -First area to check is the application log files. Have "tail -f" commands -running on the server.log and development.log. Rails will automatically display -debugging and runtime information to these files. Debugging info will also be -shown in the browser on requests from 127.0.0.1. - -You can also log your own messages directly into the log file from your code -using the Ruby logger class from inside your controllers. Example: - - class WeblogController < ActionController::Base - def destroy - @weblog = Weblog.find(params[:id]) - @weblog.destroy - logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!") - end - end - -The result will be a message in your log file along the lines of: - - Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1! - -More information on how to use the logger is at http://www.ruby-doc.org/core/ - -Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are -several books available online as well: - -* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe) -* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide) - -These two books will bring you up to speed on the Ruby language and also on -programming in general. - - -== Debugger - -Debugger support is available through the debugger command when you start your -Mongrel or WEBrick server with --debugger. This means that you can break out of -execution at any point in the code, investigate and change the model, and then, -resume execution! You need to install ruby-debug to run the server in debugging -mode. With gems, use sudo gem install ruby-debug. Example: - - class WeblogController < ActionController::Base - def index - @posts = Post.find(:all) - debugger - end - end - -So the controller will accept the action, run the first line, then present you -with a IRB prompt in the server window. Here you can do things like: - - >> @posts.inspect - => "[#nil, "body"=>nil, "id"=>"1"}>, - #"Rails", "body"=>"Only ten..", "id"=>"2"}>]" - >> @posts.first.title = "hello from a debugger" - => "hello from a debugger" - -...and even better, you can examine how your runtime objects actually work: - - >> f = @posts.first - => #nil, "body"=>nil, "id"=>"1"}> - >> f. - Display all 152 possibilities? (y or n) - -Finally, when you're ready to resume execution, you can enter "cont". - - -== Console - -The console is a Ruby shell, which allows you to interact with your -application's domain model. Here you'll have all parts of the application -configured, just like it is when the application is running. You can inspect -domain models, change values, and save to the database. Starting the script -without arguments will launch it in the development environment. - -To start the console, run rails console from the application -directory. - -Options: - -* Passing the -s, --sandbox argument will rollback any modifications - made to the database. -* Passing an environment name as an argument will load the corresponding - environment. Example: rails console production. - -To reload your controllers and models after launching the console run -reload! - -More information about irb can be found at: -link:http://www.rubycentral.com/pickaxe/irb.html - - -== dbconsole - -You can go to the command line of your database directly through rails -dbconsole. You would be connected to the database with the credentials -defined in database.yml. Starting the script without arguments will connect you -to the development database. Passing an argument will connect you to a different -database, like rails dbconsole production. Currently works for MySQL, -PostgreSQL and SQLite 3. - -== Description of Contents - -The default directory structure of a generated Ruby on Rails application: - - |-- app - | |-- controllers - | |-- helpers - | |-- mailers - | |-- models - | `-- views - | `-- layouts - |-- config - | |-- environments - | |-- initializers - | `-- locales - |-- db - |-- doc - |-- lib - | `-- tasks - |-- log - |-- public - | |-- images - | |-- javascripts - | `-- stylesheets - |-- script - |-- test - | |-- fixtures - | |-- functional - | |-- integration - | |-- performance - | `-- unit - |-- tmp - | |-- cache - | |-- pids - | |-- sessions - | `-- sockets - `-- vendor - `-- plugins - -app - Holds all the code that's specific to this particular application. - -app/controllers - Holds controllers that should be named like weblogs_controller.rb for - automated URL mapping. All controllers should descend from - ApplicationController which itself descends from ActionController::Base. - -app/models - Holds models that should be named like post.rb. Models descend from - ActiveRecord::Base by default. - -app/views - Holds the template files for the view that should be named like - weblogs/index.html.erb for the WeblogsController#index action. All views use - eRuby syntax by default. - -app/views/layouts - Holds the template files for layouts to be used with views. This models the - common header/footer method of wrapping views. In your views, define a layout - using the layout :default and create a file named default.html.erb. - Inside default.html.erb, call <% yield %> to render the view using this - layout. - -app/helpers - Holds view helpers that should be named like weblogs_helper.rb. These are - generated for you automatically when using generators for controllers. - Helpers can be used to wrap functionality for your views into methods. - -config - Configuration files for the Rails environment, the routing map, the database, - and other dependencies. - -db - Contains the database schema in schema.rb. db/migrate contains all the - sequence of Migrations for your schema. - -doc - This directory is where your application documentation will be stored when - generated using rake doc:app - -lib - Application specific libraries. Basically, any kind of custom code that - doesn't belong under controllers, models, or helpers. This directory is in - the load path. - -public - The directory available for the web server. Contains subdirectories for - images, stylesheets, and javascripts. Also contains the dispatchers and the - default HTML files. This should be set as the DOCUMENT_ROOT of your web - server. - -script - Helper scripts for automation and generation. - -test - Unit and functional tests along with fixtures. When using the rails generate - command, template test files will be generated for you and placed in this - directory. - -vendor - External libraries that the application depends on. Also includes the plugins - subdirectory. If the app has frozen rails, those gems also go here, under - vendor/rails/. This directory is in the load path. + For a copy of the GNU General Public License, + see http://www.gnu.org/licenses/ + + + diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 0728a04..6d245bc 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -3,7 +3,18 @@ SupportTool <%= stylesheet_link_tag :all %> - <%= javascript_include_tag :defaults, 'sammy', 'plugins/sammy.template' %> + <%= javascript_include_tag 'vendor/jquery', + 'vendor/underscore', + 'vendor/backbone', + 'vendor/json2', + 'vendor/ejs_1.0/ejs.js', + 'vendor/ejs_1.0/view.js', + 'application', + 'app/models/mailing_list', + 'app/controllers/main', + 'app/views/index', + 'app/views/list' + %> <%= csrf_meta_tag %> diff --git a/public/javascripts/app/controllers/main.js b/public/javascripts/app/controllers/main.js new file mode 100644 index 0000000..0c293bd --- /dev/null +++ b/public/javascripts/app/controllers/main.js @@ -0,0 +1,16 @@ +App.Controllers.Main = Backbone.Controller.extend({ + routes: { + "": "index", + }, + + index: function() { + console.log(MailingLists.fetch); + MailingLists.fetch({ + success:function(){ + new App.Views.Index(); + } + }); + + } +}); + diff --git a/public/javascripts/app/models/mailing_list.js b/public/javascripts/app/models/mailing_list.js new file mode 100644 index 0000000..8335eb8 --- /dev/null +++ b/public/javascripts/app/models/mailing_list.js @@ -0,0 +1,12 @@ +var MailingList = Backbone.Model.extend({ +}); + +// var MailingLists = Backbone.Collection.extend({ +// model: MailingList, +// url: '/mailing_lists.json' +// }); + +var MailingLists = new Backbone.Collection; + +MailingLists.model = MailingList; +MailingLists.url = '/mailing_lists.json'; \ No newline at end of file diff --git a/public/javascripts/app/templates/index.ejs b/public/javascripts/app/templates/index.ejs new file mode 100644 index 0000000..1c4b68c --- /dev/null +++ b/public/javascripts/app/templates/index.ejs @@ -0,0 +1,5 @@ +

+ Create New +

+
    +
\ No newline at end of file diff --git a/public/javascripts/app/templates/list.ejs b/public/javascripts/app/templates/list.ejs new file mode 100644 index 0000000..f04ba73 --- /dev/null +++ b/public/javascripts/app/templates/list.ejs @@ -0,0 +1,4 @@ +
  • + <%=model.escape('name')%> + melden +
  • \ No newline at end of file diff --git a/public/javascripts/app/views/index.js b/public/javascripts/app/views/index.js new file mode 100644 index 0000000..ab4ff84 --- /dev/null +++ b/public/javascripts/app/views/index.js @@ -0,0 +1,40 @@ +App.Views.Index = Backbone.View.extend({ + initialize: function() { + this.id = 'main'; + this.collection = MailingLists; + this.template = new EJS({url: 'javascripts/app/templates/index.ejs'}) + + this.collection.bind('add', this.addOne); + this.collection.bind('refresh', this.addAll); + this.collection.bind('all', this.render) + + this.render(); + this.addAll(); + }, + + render: function() { + console.log('rendering'); + // if(this.collection.length > 0) { + // var out = "

    Create New

    "; + // } else { + // out = "

    No documents! Create one

    "; + // } + $(this.el).html(this.template.render(this)); + $('#'+this.id).html(this.el); + }, + + addOne: function(list) { + var view = new App.Views.List({model: list}); + this.$(".mailing-lists").append(view.render().el); + }, + + addAll: function() { + var self = this; + this.collection.each(function(list){ self.addOne(list); }); + } + +}); diff --git a/public/javascripts/app/views/list.js b/public/javascripts/app/views/list.js new file mode 100644 index 0000000..2b803f5 --- /dev/null +++ b/public/javascripts/app/views/list.js @@ -0,0 +1,31 @@ +App.Views.List = Backbone.View.extend({ + initialize: function() { + this.id = 'main'; + this.template = new EJS({url: 'javascripts/app/templates/list.ejs'}) + this.model.bind('change', this.render); + this.count = 0; + }, + events: { + "click .note" : "note" + }, + + note: function() { + this.count++; + this.$('.notice').html(this.count) + }, + + render: function() { + console.log('rendering'); + // if(this.collection.length > 0) { + // var out = "

    Create New

    "; + // } else { + // out = "

    No documents! Create one

    "; + // } + $(this.el).html(this.template.render(this)); + return this + } +}); \ No newline at end of file diff --git a/public/javascripts/application.js b/public/javascripts/application.js index 487608f..7517ac4 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -1,27 +1,17 @@ // Place your application-specific JavaScript functions and classes here // This file is automatically included by javascript_include_tag :defaults -(function($) { - - var app = $.sammy('#main', function() { - - this.use('Template'); - - this.get('#/', function(context) { - this.load('mailing_lists.json') - .then(function(lists) { - $.each(lists, function(i, list) { - context.render('templates/list.template', {list: list}) - .appendTo(context.$element()); - }); - }); - }); - - - }); +var App = { + Views: {}, + Controllers: {}, + init: function() { + new App.Controllers.Main(); + Backbone.history.start(); + console.log('app init'); + } +}; + +$(function() { + App.init(); +}); - $(function() { - app.run('#/'); - }); - -})(jQuery); \ No newline at end of file diff --git a/public/javascripts/jquery.min.js b/public/javascripts/jquery.min.js deleted file mode 100644 index 6437874..0000000 --- a/public/javascripts/jquery.min.js +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * jQuery JavaScript Library v1.5.1 - * http://jquery.com/ - * - * Copyright 2011, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2011, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Wed Feb 23 13:55:29 2011 -0500 - */ -(function(a,b){function cg(a){return d.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cd(a){if(!bZ[a]){var b=d("<"+a+">").appendTo("body"),c=b.css("display");b.remove();if(c==="none"||c==="")c="block";bZ[a]=c}return bZ[a]}function cc(a,b){var c={};d.each(cb.concat.apply([],cb.slice(0,b)),function(){c[this]=a});return c}function bY(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function bX(){try{return new a.XMLHttpRequest}catch(b){}}function bW(){d(a).unload(function(){for(var a in bU)bU[a](0,1)})}function bQ(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var e=a.dataTypes,f={},g,h,i=e.length,j,k=e[0],l,m,n,o,p;for(g=1;g=0===c})}function N(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function F(a,b){return(a&&a!=="*"?a+".":"")+b.replace(r,"`").replace(s,"&")}function E(a){var b,c,e,f,g,h,i,j,k,l,m,n,o,q=[],r=[],s=d._data(this,"events");if(a.liveFired!==this&&s&&s.live&&!a.target.disabled&&(!a.button||a.type!=="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var t=s.live.slice(0);for(i=0;ic)break;a.currentTarget=f.elem,a.data=f.handleObj.data,a.handleObj=f.handleObj,o=f.handleObj.origHandler.apply(f.elem,arguments);if(o===!1||a.isPropagationStopped()){c=f.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function C(a,c,e){var f=d.extend({},e[0]);f.type=a,f.originalEvent={},f.liveFired=b,d.event.handle.call(c,f),f.isDefaultPrevented()&&e[0].preventDefault()}function w(){return!0}function v(){return!1}function g(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function f(a,c,f){if(f===b&&a.nodeType===1){f=a.getAttribute("data-"+c);if(typeof f==="string"){try{f=f==="true"?!0:f==="false"?!1:f==="null"?null:d.isNaN(f)?e.test(f)?d.parseJSON(f):f:parseFloat(f)}catch(g){}d.data(a,c,f)}else f=b}return f}var c=a.document,d=function(){function I(){if(!d.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(I,1);return}d.ready()}}var d=function(a,b){return new d.fn.init(a,b,g)},e=a.jQuery,f=a.$,g,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,i=/\S/,j=/^\s+/,k=/\s+$/,l=/\d/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=navigator.userAgent,w,x=!1,y,z="then done fail isResolved isRejected promise".split(" "),A,B=Object.prototype.toString,C=Object.prototype.hasOwnProperty,D=Array.prototype.push,E=Array.prototype.slice,F=String.prototype.trim,G=Array.prototype.indexOf,H={};d.fn=d.prototype={constructor:d,init:function(a,e,f){var g,i,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!e&&c.body){this.context=c,this[0]=c.body,this.selector="body",this.length=1;return this}if(typeof a==="string"){g=h.exec(a);if(!g||!g[1]&&e)return!e||e.jquery?(e||f).find(a):this.constructor(e).find(a);if(g[1]){e=e instanceof d?e[0]:e,k=e?e.ownerDocument||e:c,j=m.exec(a),j?d.isPlainObject(e)?(a=[c.createElement(j[1])],d.fn.attr.call(a,e,!0)):a=[k.createElement(j[1])]:(j=d.buildFragment([g[1]],[k]),a=(j.cacheable?d.clone(j.fragment):j.fragment).childNodes);return d.merge(this,a)}i=c.getElementById(g[2]);if(i&&i.parentNode){if(i.id!==g[2])return f.find(a);this.length=1,this[0]=i}this.context=c,this.selector=a;return this}if(d.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return d.makeArray(a,this)},selector:"",jquery:"1.5.1",length:0,size:function(){return this.length},toArray:function(){return E.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var e=this.constructor();d.isArray(a)?D.apply(e,a):d.merge(e,a),e.prevObject=this,e.context=this.context,b==="find"?e.selector=this.selector+(this.selector?" ":"")+c:b&&(e.selector=this.selector+"."+b+"("+c+")");return e},each:function(a,b){return d.each(this,a,b)},ready:function(a){d.bindReady(),y.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(E.apply(this,arguments),"slice",E.call(arguments).join(","))},map:function(a){return this.pushStack(d.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:D,sort:[].sort,splice:[].splice},d.fn.init.prototype=d.fn,d.extend=d.fn.extend=function(){var a,c,e,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i==="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!=="object"&&!d.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;y.resolveWith(c,[d]),d.fn.trigger&&d(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!x){x=!0;if(c.readyState==="complete")return setTimeout(d.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",A,!1),a.addEventListener("load",d.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",A),a.attachEvent("onload",d.ready);var b=!1;try{b=a.frameElement==null}catch(e){}c.documentElement.doScroll&&b&&I()}}},isFunction:function(a){return d.type(a)==="function"},isArray:Array.isArray||function(a){return d.type(a)==="array"},isWindow:function(a){return a&&typeof a==="object"&&"setInterval"in a},isNaN:function(a){return a==null||!l.test(a)||isNaN(a)},type:function(a){return a==null?String(a):H[B.call(a)]||"object"},isPlainObject:function(a){if(!a||d.type(a)!=="object"||a.nodeType||d.isWindow(a))return!1;if(a.constructor&&!C.call(a,"constructor")&&!C.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a){}return c===b||C.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!=="string"||!b)return null;b=d.trim(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return a.JSON&&a.JSON.parse?a.JSON.parse(b):(new Function("return "+b))();d.error("Invalid JSON: "+b)},parseXML:function(b,c,e){a.DOMParser?(e=new DOMParser,c=e.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),e=c.documentElement,(!e||!e.nodeName||e.nodeName==="parsererror")&&d.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(a){if(a&&i.test(a)){var b=c.head||c.getElementsByTagName("head")[0]||c.documentElement,e=c.createElement("script");d.support.scriptEval()?e.appendChild(c.createTextNode(a)):e.text=a,b.insertBefore(e,b.firstChild),b.removeChild(e)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,e){var f,g=0,h=a.length,i=h===b||d.isFunction(a);if(e){if(i){for(f in a)if(c.apply(a[f],e)===!1)break}else for(;g1){var f=E.call(arguments,0),g=b,h=function(a){return function(b){f[a]=arguments.length>1?E.call(arguments,0):b,--g||c.resolveWith(e,f)}};while(b--)a=f[b],a&&d.isFunction(a.promise)?a.promise().then(h(b),c.reject):--g;g||c.resolveWith(e,f)}else c!==a&&c.resolve(a);return e},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}d.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.subclass=this.subclass,a.fn.init=function b(b,c){c&&c instanceof d&&!(c instanceof a)&&(c=a(c));return d.fn.init.call(this,b,c,e)},a.fn.init.prototype=a.fn;var e=a(c);return a},browser:{}}),y=d._Deferred(),d.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){H["[object "+b+"]"]=b.toLowerCase()}),w=d.uaMatch(v),w.browser&&(d.browser[w.browser]=!0,d.browser.version=w.version),d.browser.webkit&&(d.browser.safari=!0),G&&(d.inArray=function(a,b){return G.call(b,a)}),i.test(" ")&&(j=/^[\s\xA0]+/,k=/[\s\xA0]+$/),g=d(c),c.addEventListener?A=function(){c.removeEventListener("DOMContentLoaded",A,!1),d.ready()}:c.attachEvent&&(A=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",A),d.ready())});return d}();(function(){d.support={};var b=c.createElement("div");b.style.display="none",b.innerHTML="
    a";var e=b.getElementsByTagName("*"),f=b.getElementsByTagName("a")[0],g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=b.getElementsByTagName("input")[0];if(e&&e.length&&f){d.support={leadingWhitespace:b.firstChild.nodeType===3,tbody:!b.getElementsByTagName("tbody").length,htmlSerialize:!!b.getElementsByTagName("link").length,style:/red/.test(f.getAttribute("style")),hrefNormalized:f.getAttribute("href")==="/a",opacity:/^0.55$/.test(f.style.opacity),cssFloat:!!f.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,deleteExpando:!0,optDisabled:!1,checkClone:!1,noCloneEvent:!0,noCloneChecked:!0,boxModel:null,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableHiddenOffsets:!0},i.checked=!0,d.support.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,d.support.optDisabled=!h.disabled;var j=null;d.support.scriptEval=function(){if(j===null){var b=c.documentElement,e=c.createElement("script"),f="script"+d.now();try{e.appendChild(c.createTextNode("window."+f+"=1;"))}catch(g){}b.insertBefore(e,b.firstChild),a[f]?(j=!0,delete a[f]):j=!1,b.removeChild(e),b=e=f=null}return j};try{delete b.test}catch(k){d.support.deleteExpando=!1}!b.addEventListener&&b.attachEvent&&b.fireEvent&&(b.attachEvent("onclick",function l(){d.support.noCloneEvent=!1,b.detachEvent("onclick",l)}),b.cloneNode(!0).fireEvent("onclick")),b=c.createElement("div"),b.innerHTML="";var m=c.createDocumentFragment();m.appendChild(b.firstChild),d.support.checkClone=m.cloneNode(!0).cloneNode(!0).lastChild.checked,d(function(){var a=c.createElement("div"),b=c.getElementsByTagName("body")[0];if(b){a.style.width=a.style.paddingLeft="1px",b.appendChild(a),d.boxModel=d.support.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,d.support.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
    ",d.support.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
    t
    ";var e=a.getElementsByTagName("td");d.support.reliableHiddenOffsets=e[0].offsetHeight===0,e[0].style.display="",e[1].style.display="none",d.support.reliableHiddenOffsets=d.support.reliableHiddenOffsets&&e[0].offsetHeight===0,a.innerHTML="",b.removeChild(a).style.display="none",a=e=null}});var n=function(a){var b=c.createElement("div");a="on"+a;if(!b.attachEvent)return!0;var d=a in b;d||(b.setAttribute(a,"return;"),d=typeof b[a]==="function"),b=null;return d};d.support.submitBubbles=n("submit"),d.support.changeBubbles=n("change"),b=e=f=null}})();var e=/^(?:\{.*\}|\[.*\])$/;d.extend({cache:{},uuid:0,expando:"jQuery"+(d.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?d.cache[a[d.expando]]:a[d.expando];return!!a&&!g(a)},data:function(a,c,e,f){if(d.acceptData(a)){var g=d.expando,h=typeof c==="string",i,j=a.nodeType,k=j?d.cache:a,l=j?a[d.expando]:a[d.expando]&&d.expando;if((!l||f&&l&&!k[l][g])&&h&&e===b)return;l||(j?a[d.expando]=l=++d.uuid:l=d.expando),k[l]||(k[l]={},j||(k[l].toJSON=d.noop));if(typeof c==="object"||typeof c==="function")f?k[l][g]=d.extend(k[l][g],c):k[l]=d.extend(k[l],c);i=k[l],f&&(i[g]||(i[g]={}),i=i[g]),e!==b&&(i[c]=e);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[c]:i}},removeData:function(b,c,e){if(d.acceptData(b)){var f=d.expando,h=b.nodeType,i=h?d.cache:b,j=h?b[d.expando]:d.expando;if(!i[j])return;if(c){var k=e?i[j][f]:i[j];if(k){delete k[c];if(!g(k))return}}if(e){delete i[j][f];if(!g(i[j]))return}var l=i[j][f];d.support.deleteExpando||i!=a?delete i[j]:i[j]=null,l?(i[j]={},h||(i[j].toJSON=d.noop),i[j][f]=l):h&&(d.support.deleteExpando?delete b[d.expando]:b.removeAttribute?b.removeAttribute(d.expando):b[d.expando]=null)}},_data:function(a,b,c){return d.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=d.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),d.fn.extend({data:function(a,c){var e=null;if(typeof a==="undefined"){if(this.length){e=d.data(this[0]);if(this[0].nodeType===1){var g=this[0].attributes,h;for(var i=0,j=g.length;i-1)return!0;return!1},val:function(a){if(!arguments.length){var c=this[0];if(c){if(d.nodeName(c,"option")){var e=c.attributes.value;return!e||e.specified?c.value:c.text}if(d.nodeName(c,"select")){var f=c.selectedIndex,g=[],h=c.options,i=c.type==="select-one";if(f<0)return null;for(var k=i?f:0,l=i?f+1:h.length;k=0;else if(d.nodeName(this,"select")){var f=d.makeArray(e);d("option",this).each(function(){this.selected=d.inArray(d(this).val(),f)>=0}),f.length||(this.selectedIndex=-1)}else this.value=e}})}}),d.extend({attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,e,f){if(!a||a.nodeType===3||a.nodeType===8||a.nodeType===2)return b;if(f&&c in d.attrFn)return d(a)[c](e);var g=a.nodeType!==1||!d.isXMLDoc(a),h=e!==b;c=g&&d.props[c]||c;if(a.nodeType===1){var i=k.test(c);if(c==="selected"&&!d.support.optSelected){var j=a.parentNode;j&&(j.selectedIndex,j.parentNode&&j.parentNode.selectedIndex)}if((c in a||a[c]!==b)&&g&&!i){h&&(c==="type"&&l.test(a.nodeName)&&a.parentNode&&d.error("type property can't be changed"),e===null?a.nodeType===1&&a.removeAttribute(c):a[c]=e);if(d.nodeName(a,"form")&&a.getAttributeNode(c))return a.getAttributeNode(c).nodeValue;if(c==="tabIndex"){var o=a.getAttributeNode("tabIndex");return o&&o.specified?o.value:m.test(a.nodeName)||n.test(a.nodeName)&&a.href?0:b}return a[c]}if(!d.support.style&&g&&c==="style"){h&&(a.style.cssText=""+e);return a.style.cssText}h&&a.setAttribute(c,""+e);if(!a.attributes[c]&&(a.hasAttribute&&!a.hasAttribute(c)))return b;var p=!d.support.hrefNormalized&&g&&i?a.getAttribute(c,2):a.getAttribute(c);return p===null?b:p}h&&(a[c]=e);return a[c]}});var p=/\.(.*)$/,q=/^(?:textarea|input|select)$/i,r=/\./g,s=/ /g,t=/[^\w\s.|`]/g,u=function(a){return a.replace(t,"\\$&")};d.event={add:function(c,e,f,g){if(c.nodeType!==3&&c.nodeType!==8){try{d.isWindow(c)&&(c!==a&&!c.frameElement)&&(c=a)}catch(h){}if(f===!1)f=v;else if(!f)return;var i,j;f.handler&&(i=f,f=i.handler),f.guid||(f.guid=d.guid++);var k=d._data(c);if(!k)return;var l=k.events,m=k.handle;l||(k.events=l={}),m||(k.handle=m=function(){return typeof d!=="undefined"&&!d.event.triggered?d.event.handle.apply(m.elem,arguments):b}),m.elem=c,e=e.split(" ");var n,o=0,p;while(n=e[o++]){j=i?d.extend({},i):{handler:f,data:g},n.indexOf(".")>-1?(p=n.split("."),n=p.shift(),j.namespace=p.slice(0).sort().join(".")):(p=[],j.namespace=""),j.type=n,j.guid||(j.guid=f.guid);var q=l[n],r=d.event.special[n]||{};if(!q){q=l[n]=[];if(!r.setup||r.setup.call(c,g,p,m)===!1)c.addEventListener?c.addEventListener(n,m,!1):c.attachEvent&&c.attachEvent("on"+n,m)}r.add&&(r.add.call(c,j),j.handler.guid||(j.handler.guid=f.guid)),q.push(j),d.event.global[n]=!0}c=null}},global:{},remove:function(a,c,e,f){if(a.nodeType!==3&&a.nodeType!==8){e===!1&&(e=v);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=d.hasData(a)&&d._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(e=c.handler,c=c.type);if(!c||typeof c==="string"&&c.charAt(0)==="."){c=c||"";for(h in t)d.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+d.map(m.slice(0).sort(),u).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!e){for(j=0;j=0&&(a.type=f=f.slice(0,-1),a.exclusive=!0),e||(a.stopPropagation(),d.event.global[f]&&d.each(d.cache,function(){var b=d.expando,e=this[b];e&&e.events&&e.events[f]&&d.event.trigger(a,c,e.handle.elem)}));if(!e||e.nodeType===3||e.nodeType===8)return b;a.result=b,a.target=e,c=d.makeArray(c),c.unshift(a)}a.currentTarget=e;var h=d._data(e,"handle");h&&h.apply(e,c);var i=e.parentNode||e.ownerDocument;try{e&&e.nodeName&&d.noData[e.nodeName.toLowerCase()]||e["on"+f]&&e["on"+f].apply(e,c)===!1&&(a.result=!1,a.preventDefault())}catch(j){}if(!a.isPropagationStopped()&&i)d.event.trigger(a,c,i,!0);else if(!a.isDefaultPrevented()){var k,l=a.target,m=f.replace(p,""),n=d.nodeName(l,"a")&&m==="click",o=d.event.special[m]||{};if((!o._default||o._default.call(e,a)===!1)&&!n&&!(l&&l.nodeName&&d.noData[l.nodeName.toLowerCase()])){try{l[m]&&(k=l["on"+m],k&&(l["on"+m]=null),d.event.triggered=!0,l[m]())}catch(q){}k&&(l["on"+m]=k),d.event.triggered=!1}}},handle:function(c){var e,f,g,h,i,j=[],k=d.makeArray(arguments);c=k[0]=d.event.fix(c||a.event),c.currentTarget=this,e=c.type.indexOf(".")<0&&!c.exclusive,e||(g=c.type.split("."),c.type=g.shift(),j=g.slice(0).sort(),h=new RegExp("(^|\\.)"+j.join("\\.(?:.*\\.)?")+"(\\.|$)")),c.namespace=c.namespace||j.join("."),i=d._data(this,"events"),f=(i||{})[c.type];if(i&&f){f=f.slice(0);for(var l=0,m=f.length;l-1?d.map(a.options,function(a){return a.selected}).join("-"):"":a.nodeName.toLowerCase()==="select"&&(c=a.selectedIndex);return c},B=function B(a){var c=a.target,e,f;if(q.test(c.nodeName)&&!c.readOnly){e=d._data(c,"_change_data"),f=A(c),(a.type!=="focusout"||c.type!=="radio")&&d._data(c,"_change_data",f);if(e===b||f===e)return;if(e!=null||f)a.type="change",a.liveFired=b,d.event.trigger(a,arguments[1],c)}};d.event.special.change={filters:{focusout:B,beforedeactivate:B,click:function(a){var b=a.target,c=b.type;(c==="radio"||c==="checkbox"||b.nodeName.toLowerCase()==="select")&&B.call(this,a)},keydown:function(a){var b=a.target,c=b.type;(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&B.call(this,a)},beforeactivate:function(a){var b=a.target;d._data(b,"_change_data",A(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in z)d.event.add(this,c+".specialChange",z[c]);return q.test(this.nodeName)},teardown:function(a){d.event.remove(this,".specialChange");return q.test(this.nodeName)}},z=d.event.special.change.filters,z.focus=z.beforeactivate}c.addEventListener&&d.each({focus:"focusin",blur:"focusout"},function(a,b){function c(a){a=d.event.fix(a),a.type=b;return d.event.handle.call(this,a)}d.event.special[b]={setup:function(){this.addEventListener(a,c,!0)},teardown:function(){this.removeEventListener(a,c,!0)}}}),d.each(["bind","one"],function(a,c){d.fn[c]=function(a,e,f){if(typeof a==="object"){for(var g in a)this[c](g,e,a[g],f);return this}if(d.isFunction(e)||e===!1)f=e,e=b;var h=c==="one"?d.proxy(f,function(a){d(this).unbind(a,h);return f.apply(this,arguments)}):f;if(a==="unload"&&c!=="one")this.one(a,e,f);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},d.attrFn&&(d.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,e,g){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!=="string")return e;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(f.call(n)==="[object Array]")if(u)if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&e.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&e.push(j[t]);else e.push.apply(e,n);else p(n,e);o&&(k(o,h,e,g),k.uniqueSort(e));return e};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b==="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){return"text"===a.getAttribute("type")},radio:function(a){return"radio"===a.type},checkbox:function(a){return"checkbox"===a.type},file:function(a){return"file"===a.type},password:function(a){return"password"===a.type},submit:function(a){return"submit"===a.type},image:function(a){return"image"===a.type},reset:function(a){return"reset"===a.type},button:function(a){return"button"===a.type||a.nodeName.toLowerCase()==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(f.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length==="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!=="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!=="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!=="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!=="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

    ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector,d=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(e){d=!0}b&&(k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(d||!l.match.PSEUDO.test(c)&&!/!=/.test(c))return b.call(a,c)}catch(e){}return k(c,null,null,[a]).length>0})}(),function(){var a=c.createElement("div");a.innerHTML="
    ";if(a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!=="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(var g=c;g0},closest:function(a,b){var c=[],e,f,g=this[0];if(d.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(e=0,f=a.length;e-1:d(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=L.test(a)?d(a,b||this.context):null;for(e=0,f=this.length;e-1:d.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b)break}}c=c.length>1?d.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a==="string")return d.inArray(this[0],a?d(a):this.parent().children());return d.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a==="string"?d(a,b):d.makeArray(a),e=d.merge(this.get(),c);return this.pushStack(N(c[0])||N(e[0])?e:d.unique(e))},andSelf:function(){return this.add(this.prevObject)}}),d.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return d.dir(a,"parentNode")},parentsUntil:function(a,b,c){return d.dir(a,"parentNode",c)},next:function(a){return d.nth(a,2,"nextSibling")},prev:function(a){return d.nth(a,2,"previousSibling")},nextAll:function(a){return d.dir(a,"nextSibling")},prevAll:function(a){return d.dir(a,"previousSibling")},nextUntil:function(a,b,c){return d.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return d.dir(a,"previousSibling",c)},siblings:function(a){return d.sibling(a.parentNode.firstChild,a)},children:function(a){return d.sibling(a.firstChild)},contents:function(a){return d.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:d.makeArray(a.childNodes)}},function(a,b){d.fn[a]=function(c,e){var f=d.map(this,b,c),g=K.call(arguments);G.test(a)||(e=c),e&&typeof e==="string"&&(f=d.filter(e,f)),f=this.length>1&&!M[a]?d.unique(f):f,(this.length>1||I.test(e))&&H.test(a)&&(f=f.reverse());return this.pushStack(f,a,g.join(","))}}),d.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?d.find.matchesSelector(b[0],a)?[b[0]]:[]:d.find.matches(a,b)},dir:function(a,c,e){var f=[],g=a[c];while(g&&g.nodeType!==9&&(e===b||g.nodeType!==1||!d(g).is(e)))g.nodeType===1&&f.push(g),g=g[c];return f},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var P=/ jQuery\d+="(?:\d+|null)"/g,Q=/^\s+/,R=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,S=/<([\w:]+)/,T=/",""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]};X.optgroup=X.option,X.tbody=X.tfoot=X.colgroup=X.caption=X.thead,X.th=X.td,d.support.htmlSerialize||(X._default=[1,"div
    ","
    "]),d.fn.extend({text:function(a){if(d.isFunction(a))return this.each(function(b){var c=d(this);c.text(a.call(this,b,c.text()))});if(typeof a!=="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return d.text(this)},wrapAll:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapAll(a.call(this,b))});if(this[0]){var b=d(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapInner(a.call(this,b))});return this.each(function(){var b=d(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){d(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){d.nodeName(this,"body")||d(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=d(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,d(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,e;(e=this[c])!=null;c++)if(!a||d.filter(a,[e]).length)!b&&e.nodeType===1&&(d.cleanData(e.getElementsByTagName("*")),d.cleanData([e])),e.parentNode&&e.parentNode.removeChild(e);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&d.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return d.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(P,""):null;if(typeof a!=="string"||V.test(a)||!d.support.leadingWhitespace&&Q.test(a)||X[(S.exec(a)||["",""])[1].toLowerCase()])d.isFunction(a)?this.each(function(b){var c=d(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);else{a=a.replace(R,"<$1>");try{for(var c=0,e=this.length;c1&&l0?this.clone(!0):this).get();d(f[h])[b](j),e=e.concat(j)}return this.pushStack(e,a,f.selector)}}),d.extend({clone:function(a,b,c){var e=a.cloneNode(!0),f,g,h;if((!d.support.noCloneEvent||!d.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!d.isXMLDoc(a)){$(a,e),f=_(a),g=_(e);for(h=0;f[h];++h)$(f[h],g[h])}if(b){Z(a,e);if(c){f=_(a),g=_(e);for(h=0;f[h];++h)Z(f[h],g[h])}}return e},clean:function(a,b,e,f){b=b||c,typeof b.createElement==="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var g=[];for(var h=0,i;(i=a[h])!=null;h++){typeof i==="number"&&(i+="");if(!i)continue;if(typeof i!=="string"||U.test(i)){if(typeof i==="string"){i=i.replace(R,"<$1>");var j=(S.exec(i)||["",""])[1].toLowerCase(),k=X[j]||X._default,l=k[0],m=b.createElement("div");m.innerHTML=k[1]+i+k[2];while(l--)m=m.lastChild;if(!d.support.tbody){var n=T.test(i),o=j==="table"&&!n?m.firstChild&&m.firstChild.childNodes:k[1]===""&&!n?m.childNodes:[];for(var p=o.length-1;p>=0;--p)d.nodeName(o[p],"tbody")&&!o[p].childNodes.length&&o[p].parentNode.removeChild(o[p])}!d.support.leadingWhitespace&&Q.test(i)&&m.insertBefore(b.createTextNode(Q.exec(i)[0]),m.firstChild),i=m.childNodes}}else i=b.createTextNode(i);i.nodeType?g.push(i):g=d.merge(g,i)}if(e)for(h=0;g[h];h++)!f||!d.nodeName(g[h],"script")||g[h].type&&g[h].type.toLowerCase()!=="text/javascript"?(g[h].nodeType===1&&g.splice.apply(g,[h+1,0].concat(d.makeArray(g[h].getElementsByTagName("script")))),e.appendChild(g[h])):f.push(g[h].parentNode?g[h].parentNode.removeChild(g[h]):g[h]);return g},cleanData:function(a){var b,c,e=d.cache,f=d.expando,g=d.event.special,h=d.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&d.noData[j.nodeName.toLowerCase()])continue;c=j[d.expando];if(c){b=e[c]&&e[c][f];if(b&&b.events){for(var k in b.events)g[k]?d.event.remove(j,k):d.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[d.expando]:j.removeAttribute&&j.removeAttribute(d.expando),delete e[c]}}}});var bb=/alpha\([^)]*\)/i,bc=/opacity=([^)]*)/,bd=/-([a-z])/ig,be=/([A-Z])/g,bf=/^-?\d+(?:px)?$/i,bg=/^-?\d/,bh={position:"absolute",visibility:"hidden",display:"block"},bi=["Left","Right"],bj=["Top","Bottom"],bk,bl,bm,bn=function(a,b){return b.toUpperCase()};d.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return d.access(this,a,c,!0,function(a,c,e){return e!==b?d.style(a,c,e):d.css(a,c)})},d.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bk(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{zIndex:!0,fontWeight:!0,opacity:!0,zoom:!0,lineHeight:!0},cssProps:{"float":d.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,e,f){if(a&&a.nodeType!==3&&a.nodeType!==8&&a.style){var g,h=d.camelCase(c),i=a.style,j=d.cssHooks[h];c=d.cssProps[h]||h;if(e===b){if(j&&"get"in j&&(g=j.get(a,!1,f))!==b)return g;return i[c]}if(typeof e==="number"&&isNaN(e)||e==null)return;typeof e==="number"&&!d.cssNumber[h]&&(e+="px");if(!j||!("set"in j)||(e=j.set(a,e))!==b)try{i[c]=e}catch(k){}}},css:function(a,c,e){var f,g=d.camelCase(c),h=d.cssHooks[g];c=d.cssProps[g]||g;if(h&&"get"in h&&(f=h.get(a,!0,e))!==b)return f;if(bk)return bk(a,c,g)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]},camelCase:function(a){return a.replace(bd,bn)}}),d.curCSS=d.css,d.each(["height","width"],function(a,b){d.cssHooks[b]={get:function(a,c,e){var f;if(c){a.offsetWidth!==0?f=bo(a,b,e):d.swap(a,bh,function(){f=bo(a,b,e)});if(f<=0){f=bk(a,b,b),f==="0px"&&bm&&(f=bm(a,b,b));if(f!=null)return f===""||f==="auto"?"0px":f}if(f<0||f==null){f=a.style[b];return f===""||f==="auto"?"0px":f}return typeof f==="string"?f:f+"px"}},set:function(a,b){if(!bf.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),d.support.opacity||(d.cssHooks.opacity={get:function(a,b){return bc.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style;c.zoom=1;var e=d.isNaN(b)?"":"alpha(opacity="+b*100+")",f=c.filter||"";c.filter=bb.test(f)?f.replace(bb,e):c.filter+" "+e}}),c.defaultView&&c.defaultView.getComputedStyle&&(bl=function(a,c,e){var f,g,h;e=e.replace(be,"-$1").toLowerCase();if(!(g=a.ownerDocument.defaultView))return b;if(h=g.getComputedStyle(a,null))f=h.getPropertyValue(e),f===""&&!d.contains(a.ownerDocument.documentElement,a)&&(f=d.style(a,e));return f}),c.documentElement.currentStyle&&(bm=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bf.test(d)&&bg.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bk=bl||bm,d.expr&&d.expr.filters&&(d.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!d.support.reliableHiddenOffsets&&(a.style.display||d.css(a,"display"))==="none"},d.expr.filters.visible=function(a){return!d.expr.filters.hidden(a)});var bp=/%20/g,bq=/\[\]$/,br=/\r?\n/g,bs=/#.*$/,bt=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bu=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bv=/(?:^file|^widget|\-extension):$/,bw=/^(?:GET|HEAD)$/,bx=/^\/\//,by=/\?/,bz=/)<[^<]*)*<\/script>/gi,bA=/^(?:select|textarea)/i,bB=/\s+/,bC=/([?&])_=[^&]*/,bD=/(^|\-)([a-z])/g,bE=function(a,b,c){return b+c.toUpperCase()},bF=/^([\w\+\.\-]+:)\/\/([^\/?#:]*)(?::(\d+))?/,bG=d.fn.load,bH={},bI={},bJ,bK;try{bJ=c.location.href}catch(bL){bJ=c.createElement("a"),bJ.href="",bJ=bJ.href}bK=bF.exec(bJ.toLowerCase()),d.fn.extend({load:function(a,c,e){if(typeof a!=="string"&&bG)return bG.apply(this,arguments);if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var g=a.slice(f,a.length);a=a.slice(0,f)}var h="GET";c&&(d.isFunction(c)?(e=c,c=b):typeof c==="object"&&(c=d.param(c,d.ajaxSettings.traditional),h="POST"));var i=this;d.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?d("
    ").append(c.replace(bz,"")).find(g):c)),e&&i.each(e,[c,b,a])}});return this},serialize:function(){return d.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?d.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bA.test(this.nodeName)||bu.test(this.type))}).map(function(a,b){var c=d(this).val();return c==null?null:d.isArray(c)?d.map(c,function(a,c){return{name:b.name,value:a.replace(br,"\r\n")}}):{name:b.name,value:c.replace(br,"\r\n")}}).get()}}),d.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){d.fn[b]=function(a){return this.bind(b,a)}}),d.each(["get","post"],function(a,c){d[c]=function(a,e,f,g){d.isFunction(e)&&(g=g||f,f=e,e=b);return d.ajax({type:c,url:a,data:e,success:f,dataType:g})}}),d.extend({getScript:function(a,c){return d.get(a,b,c,"script")},getJSON:function(a,b,c){return d.get(a,b,c,"json")},ajaxSetup:function(a,b){b?d.extend(!0,a,d.ajaxSettings,b):(b=a,a=d.extend(!0,d.ajaxSettings,b));for(var c in {context:1,url:1})c in b?a[c]=b[c]:c in d.ajaxSettings&&(a[c]=d.ajaxSettings[c]);return a},ajaxSettings:{url:bJ,isLocal:bv.test(bK[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":d.parseJSON,"text xml":d.parseXML}},ajaxPrefilter:bM(bH),ajaxTransport:bM(bI),ajax:function(a,c){function v(a,c,l,n){if(r!==2){r=2,p&&clearTimeout(p),o=b,m=n||"",u.readyState=a?4:0;var q,t,v,w=l?bP(e,u,l):b,x,y;if(a>=200&&a<300||a===304){if(e.ifModified){if(x=u.getResponseHeader("Last-Modified"))d.lastModified[k]=x;if(y=u.getResponseHeader("Etag"))d.etag[k]=y}if(a===304)c="notmodified",q=!0;else try{t=bQ(e,w),c="success",q=!0}catch(z){c="parsererror",v=z}}else{v=c;if(!c||a)c="error",a<0&&(a=0)}u.status=a,u.statusText=c,q?h.resolveWith(f,[t,c,u]):h.rejectWith(f,[u,c,v]),u.statusCode(j),j=b,s&&g.trigger("ajax"+(q?"Success":"Error"),[u,e,q?t:v]),i.resolveWith(f,[u,c]),s&&(g.trigger("ajaxComplete",[u,e]),--d.active||d.event.trigger("ajaxStop"))}}typeof a==="object"&&(c=a,a=b),c=c||{};var e=d.ajaxSetup({},c),f=e.context||e,g=f!==e&&(f.nodeType||f instanceof d)?d(f):d.event,h=d.Deferred(),i=d._Deferred(),j=e.statusCode||{},k,l={},m,n,o,p,q,r=0,s,t,u={readyState:0,setRequestHeader:function(a,b){r||(l[a.toLowerCase().replace(bD,bE)]=b);return this},getAllResponseHeaders:function(){return r===2?m:null},getResponseHeader:function(a){var c;if(r===2){if(!n){n={};while(c=bt.exec(m))n[c[1].toLowerCase()]=c[2]}c=n[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){r||(e.mimeType=a);return this},abort:function(a){a=a||"abort",o&&o.abort(a),v(0,a);return this}};h.promise(u),u.success=u.done,u.error=u.fail,u.complete=i.done,u.statusCode=function(a){if(a){var b;if(r<2)for(b in a)j[b]=[j[b],a[b]];else b=a[u.status],u.then(b,b)}return this},e.url=((a||e.url)+"").replace(bs,"").replace(bx,bK[1]+"//"),e.dataTypes=d.trim(e.dataType||"*").toLowerCase().split(bB),e.crossDomain||(q=bF.exec(e.url.toLowerCase()),e.crossDomain=q&&(q[1]!=bK[1]||q[2]!=bK[2]||(q[3]||(q[1]==="http:"?80:443))!=(bK[3]||(bK[1]==="http:"?80:443)))),e.data&&e.processData&&typeof e.data!=="string"&&(e.data=d.param(e.data,e.traditional)),bN(bH,e,c,u);if(r===2)return!1;s=e.global,e.type=e.type.toUpperCase(),e.hasContent=!bw.test(e.type),s&&d.active++===0&&d.event.trigger("ajaxStart");if(!e.hasContent){e.data&&(e.url+=(by.test(e.url)?"&":"?")+e.data),k=e.url;if(e.cache===!1){var w=d.now(),x=e.url.replace(bC,"$1_="+w);e.url=x+(x===e.url?(by.test(e.url)?"&":"?")+"_="+w:"")}}if(e.data&&e.hasContent&&e.contentType!==!1||c.contentType)l["Content-Type"]=e.contentType;e.ifModified&&(k=k||e.url,d.lastModified[k]&&(l["If-Modified-Since"]=d.lastModified[k]),d.etag[k]&&(l["If-None-Match"]=d.etag[k])),l.Accept=e.dataTypes[0]&&e.accepts[e.dataTypes[0]]?e.accepts[e.dataTypes[0]]+(e.dataTypes[0]!=="*"?", */*; q=0.01":""):e.accepts["*"];for(t in e.headers)u.setRequestHeader(t,e.headers[t]);if(e.beforeSend&&(e.beforeSend.call(f,u,e)===!1||r===2)){u.abort();return!1}for(t in {success:1,error:1,complete:1})u[t](e[t]);o=bN(bI,e,c,u);if(o){u.readyState=1,s&&g.trigger("ajaxSend",[u,e]),e.async&&e.timeout>0&&(p=setTimeout(function(){u.abort("timeout")},e.timeout));try{r=1,o.send(l,v)}catch(y){status<2?v(-1,y):d.error(y)}}else v(-1,"No Transport");return u},param:function(a,c){var e=[],f=function(a,b){b=d.isFunction(b)?b():b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=d.ajaxSettings.traditional);if(d.isArray(a)||a.jquery&&!d.isPlainObject(a))d.each(a,function(){f(this.name,this.value)});else for(var g in a)bO(g,a[g],c,f);return e.join("&").replace(bp,"+")}}),d.extend({active:0,lastModified:{},etag:{}});var bR=d.now(),bS=/(\=)\?(&|$)|()\?\?()/i;d.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return d.expando+"_"+bR++}}),d.ajaxPrefilter("json jsonp",function(b,c,e){var f=typeof b.data==="string";if(b.dataTypes[0]==="jsonp"||c.jsonpCallback||c.jsonp!=null||b.jsonp!==!1&&(bS.test(b.url)||f&&bS.test(b.data))){var g,h=b.jsonpCallback=d.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2",m=function(){a[h]=i,g&&d.isFunction(i)&&a[h](g[0])};b.jsonp!==!1&&(j=j.replace(bS,l),b.url===j&&(f&&(k=k.replace(bS,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},e.then(m,m),b.converters["script json"]=function(){g||d.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),d.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){d.globalEval(a);return a}}}),d.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),d.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var bT=d.now(),bU,bV;d.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&bX()||bY()}:bX,bV=d.ajaxSettings.xhr(),d.support.ajax=!!bV,d.support.cors=bV&&"withCredentials"in bV,bV=b,d.support.ajax&&d.ajaxTransport(function(a){if(!a.crossDomain||d.support.cors){var c;return{send:function(e,f){var g=a.xhr(),h,i;a.username?g.open(a.type,a.url,a.async,a.username,a.password):g.open(a.type,a.url,a.async);if(a.xhrFields)for(i in a.xhrFields)g[i]=a.xhrFields[i];a.mimeType&&g.overrideMimeType&&g.overrideMimeType(a.mimeType),(!a.crossDomain||a.hasContent)&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(i in e)g.setRequestHeader(i,e[i])}catch(j){}g.send(a.hasContent&&a.data||null),c=function(e,i){var j,k,l,m,n;try{if(c&&(i||g.readyState===4)){c=b,h&&(g.onreadystatechange=d.noop,delete bU[h]);if(i)g.readyState!==4&&g.abort();else{j=g.status,l=g.getAllResponseHeaders(),m={},n=g.responseXML,n&&n.documentElement&&(m.xml=n),m.text=g.responseText;try{k=g.statusText}catch(o){k=""}j||!a.isLocal||a.crossDomain?j===1223&&(j=204):j=m.text?200:404}}}catch(p){i||f(-1,p)}m&&f(j,k,m,l)},a.async&&g.readyState!==4?(bU||(bU={},bW()),h=bT++,g.onreadystatechange=bU[h]=c):c()},abort:function(){c&&c(0,1)}}}});var bZ={},b$=/^(?:toggle|show|hide)$/,b_=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,ca,cb=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];d.fn.extend({show:function(a,b,c){var e,f;if(a||a===0)return this.animate(cc("show",3),a,b,c);for(var g=0,h=this.length;g=0;a--)c[a].elem===this&&(b&&c[a](!0),c.splice(a,1))}),b||this.dequeue();return this}}),d.each({slideDown:cc("show",1),slideUp:cc("hide",1),slideToggle:cc("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){d.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),d.extend({speed:function(a,b,c){var e=a&&typeof a==="object"?d.extend({},a):{complete:c||!c&&b||d.isFunction(a)&&a,duration:a,easing:c&&b||b&&!d.isFunction(b)&&b};e.duration=d.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in d.fx.speeds?d.fx.speeds[e.duration]:d.fx.speeds._default,e.old=e.complete,e.complete=function(){e.queue!==!1&&d(this).dequeue(),d.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig||(b.orig={})}}),d.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(d.fx.step[this.prop]||d.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=d.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function g(a){return e.step(a)}var e=this,f=d.fx;this.startTime=d.now(),this.start=a,this.end=b,this.unit=c||this.unit||(d.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,g.elem=this.elem,g()&&d.timers.push(g)&&!ca&&(ca=setInterval(f.tick,f.interval))},show:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),d(this.elem).show()},hide:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=d.now(),c=!0;if(a||b>=this.options.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),this.options.curAnim[this.prop]=!0;for(var e in this.options.curAnim)this.options.curAnim[e]!==!0&&(c=!1);if(c){if(this.options.overflow!=null&&!d.support.shrinkWrapBlocks){var f=this.elem,g=this.options;d.each(["","X","Y"],function(a,b){f.style["overflow"+b]=g.overflow[a]})}this.options.hide&&d(this.elem).hide();if(this.options.hide||this.options.show)for(var h in this.options.curAnim)d.style(this.elem,h,this.options.orig[h]);this.options.complete.call(this.elem)}return!1}var i=b-this.startTime;this.state=i/this.options.duration;var j=this.options.specialEasing&&this.options.specialEasing[this.prop],k=this.options.easing||(d.easing.swing?"swing":"linear");this.pos=d.easing[j||k](this.state,i,0,1,this.options.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update();return!0}},d.extend(d.fx,{tick:function(){var a=d.timers;for(var b=0;b
    ";d.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),e=b.firstChild,f=e.firstChild,h=e.nextSibling.firstChild.firstChild,this.doesNotAddBorder=f.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,f.style.position="fixed",f.style.top="20px",this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15,f.style.position=f.style.top="",e.style.overflow="hidden",e.style.position="relative",this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),a=b=e=f=g=h=null,d.offset.initialize=d.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;d.offset.initialize(),d.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(d.css(a,"marginTop"))||0,c+=parseFloat(d.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var e=d.css(a,"position");e==="static"&&(a.style.position="relative");var f=d(a),g=f.offset(),h=d.css(a,"top"),i=d.css(a,"left"),j=e==="absolute"&&d.inArray("auto",[h,i])>-1,k={},l={},m,n;j&&(l=f.position()),m=j?l.top:parseInt(h,10)||0,n=j?l.left:parseInt(i,10)||0,d.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):f.css(k)}},d.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),e=cf.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(d.css(a,"marginTop"))||0,c.left-=parseFloat(d.css(a,"marginLeft"))||0,e.top+=parseFloat(d.css(b[0],"borderTopWidth"))||0,e.left+=parseFloat(d.css(b[0],"borderLeftWidth"))||0;return{top:c.top-e.top,left:c.left-e.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&(!cf.test(a.nodeName)&&d.css(a,"position")==="static"))a=a.offsetParent;return a})}}),d.each(["Left","Top"],function(a,c){var e="scroll"+c;d.fn[e]=function(c){var f=this[0],g;if(!f)return null;if(c!==b)return this.each(function(){g=cg(this),g?g.scrollTo(a?d(g).scrollLeft():c,a?c:d(g).scrollTop()):this[e]=c});g=cg(f);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:d.support.boxModel&&g.document.documentElement[e]||g.document.body[e]:f[e]}}),d.each(["Height","Width"],function(a,c){var e=c.toLowerCase();d.fn["inner"+c]=function(){return this[0]?parseFloat(d.css(this[0],e,"padding")):null},d.fn["outer"+c]=function(a){return this[0]?parseFloat(d.css(this[0],e,a?"margin":"border")):null},d.fn[e]=function(a){var f=this[0];if(!f)return a==null?null:this;if(d.isFunction(a))return this.each(function(b){var c=d(this);c[e](a.call(this,b,c[e]()))});if(d.isWindow(f)){var g=f.document.documentElement["client"+c];return f.document.compatMode==="CSS1Compat"&&g||f.document.body["client"+c]||g}if(f.nodeType===9)return Math.max(f.documentElement["client"+c],f.body["scroll"+c],f.documentElement["scroll"+c],f.body["offset"+c],f.documentElement["offset"+c]);if(a===b){var h=d.css(f,e),i=parseFloat(h);return d.isNaN(i)?h:i}return this.css(e,typeof a==="string"?a:a+"px")}}),a.jQuery=a.$=d})(window); \ No newline at end of file diff --git a/public/javascripts/plugins/sammy.cache.js b/public/javascripts/plugins/sammy.cache.js deleted file mode 100644 index b2622c8..0000000 --- a/public/javascripts/plugins/sammy.cache.js +++ /dev/null @@ -1,115 +0,0 @@ -// deprecated -(function($) { - - Sammy = Sammy || {}; - - // A simple cache strategy that stores key/values in memory. - Sammy.MemoryCacheProxy = function(initial) { - this._cache = initial || {}; - }; - - $.extend(Sammy.MemoryCacheProxy.prototype, { - exists: function(name) { - return (typeof this._cache[name] != "undefined"); - }, - set: function(name, value) { - return this._cache[name] = value; - }, - get: function(name) { - return this._cache[name]; - }, - clear: function(name) { - delete this._cache[name]; - } - }); - - // A simple cache strategy that stores key/values $element.data() with a cache. prefix - Sammy.DataCacheProxy = function(initial, $element) { - initial = initial || {}; - this.$element = $element; - $.each(initial, function(key, value) { - $element.data('cache.' + key, value); - }); - }; - - $.extend(Sammy.DataCacheProxy.prototype, { - exists: function(name) { - return (typeof this.$element.data('cache.' + name) != "undefined"); - }, - set: function(name, value) { - return this.$element.data('cache.' + name, value); - }, - get: function(name) { - return this.$element.data('cache.' + name); - }, - clear: function(name) { - this.$element.removeData('cache.' + name); - } - }); - - // Sammy.Cache provides helpers for caching data within the lifecycle of a - // Sammy app. The plugin provides two main methods on Sammy.Application, - // cache and clearCache. Each app has its own cache store so that - // you dont have to worry about collisions. There are currently two different 'cache proxies' - // that share the same API but store the data in different ways. - // - // ### Arguments - // - // * `proxy` decides which caching proxy to use, either 'memory'(default) or 'data' - // - Sammy.Cache = function(app, proxy) { - - app.log('**WARNING:** This version of Sammy.Cache has been deprecated in favor of using the version in Sammy.Storage and will be removed in 1.0') - - if (proxy == 'data') { - this.cache_proxy = new Sammy.DataCacheProxy({}, this.$element()); - } else { - this.cache_proxy = new Sammy.MemoryCacheProxy({}); - } - - app.cache_partials = true; - - $.extend(app, { - // cache is the main method for interacting with the cache store. The same - // method is used for both setting and getting the value. The API is similar - // to jQuery.fn.attr() - // - // ### Examples - // - // // setting a value - // cache('key', 'value'); - // - // // getting a value - // cache('key'); //=> 'value' - // - // // setting a value with a callback - // cache('key', function() { - // // this is the app - // return app.element_selector; - // }); - // - cache: function(name, value) { - if (typeof value == 'undefined') { - return this.cache_proxy.get(name); - } else if ($.isFunction(value) && !this.cache_proxy.exists(name)) { - return this.cache_proxy.set(name, value.apply(this)); - } else { - return this.cache_proxy.set(name, value) - } - }, - - // clears the cached value for name - clearCache: function(name) { - return this.cache_proxy.clear(name); - } - }); - - app.helpers({ - // a helper shortcut for use in Sammy.EventContext - cache: function(name, value) { - return this.app.cache(name, value); - } - }); - }; - -})(jQuery); diff --git a/public/javascripts/plugins/sammy.data_location_proxy.js b/public/javascripts/plugins/sammy.data_location_proxy.js deleted file mode 100644 index 33140cd..0000000 --- a/public/javascripts/plugins/sammy.data_location_proxy.js +++ /dev/null @@ -1,78 +0,0 @@ -(function($) { - - Sammy = Sammy || {}; - - // The DataLocationProxy is an optional location proxy prototype. As opposed to - // the `HashLocationProxy` it gets its location from a jQuery.data attribute - // tied to the application's element. You can set the name of the attribute by - // passing a string as the second argument to the constructor. The default attribute - // name is 'sammy-location'. To read more about location proxies, check out the - // documentation for `Sammy.HashLocationProxy` - // - // An optional `href_attribute` can be passed, which specifies a DOM element - // attribute that holds "links" to different locations in the app. When the - // proxy is bound, clicks to element that have this attribute activate a - // `setLocation()` using the contents of the `href_attribute`. - // - // ### Example - // - // var app = $.sammy(function() { - // // set up the location proxy - // this.setLocationProxy(new Sammy.DataLocationProxy(this, 'location', 'rel')); - // - // this.get('about', function() { - // this.partial('about.html'); - // }); - // - // }); - // - // In this scenario, if an element existed within the template: - // - // About Us - // - // Clicking on that link would not go to /about, but would set the apps location - // to 'about' and trigger the route. - Sammy.DataLocationProxy = function(app, data_name, href_attribute) { - this.app = app; - this.data_name = data_name || 'sammy-location'; - this.href_attribute = href_attribute; - }; - - Sammy.DataLocationProxy.prototype = { - bind: function() { - var proxy = this; - this.app.$element().bind('setData', function(e, key, value) { - if (key == proxy.data_name) { - // jQuery unfortunately fires the event before it sets the value - // work around it, by setting the value ourselves - proxy.app.$element().each(function() { - $.data(this, proxy.data_name, value); - }); - proxy.app.trigger('location-changed'); - } - }); - if (this.href_attribute) { - this.app.$element().delegate('[' + this.href_attribute + ']', 'click', function(e) { - e.preventDefault(); - proxy.setLocation($(this).attr(proxy.href_attribute)); - }); - } - }, - - unbind: function() { - if (this.href_attribute) { - this.app.$element().undelegate('[' + this.href_attribute + ']', 'click'); - } - this.app.$element().unbind('setData'); - }, - - getLocation: function() { - return this.app.$element().data(this.data_name) || ''; - }, - - setLocation: function(new_location) { - return this.app.$element().data(this.data_name, new_location); - } - }; - -})(jQuery); diff --git a/public/javascripts/plugins/sammy.ejs.js b/public/javascripts/plugins/sammy.ejs.js deleted file mode 100644 index 4fb7493..0000000 --- a/public/javascripts/plugins/sammy.ejs.js +++ /dev/null @@ -1,700 +0,0 @@ -(function($) { - - // Embeddedjs is property of http://embeddedjs.com/ - // Sammy ejs plugin written Codeofficer @ http://www.codeofficer.com/ - - 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; - }; - - var chop = function(string) { - return string.substr(0, string.length - 1); - }; - - var extend = function(d, s) { - for (var n in s) { - if(s.hasOwnProperty(n)) d[n] = s[n]; - }; - }; - - /* @Constructor*/ - 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 0) { - for (var i=0; i 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 - * - - - - - - - - - - - -
    OptionDefaultDescription
    type'<'type of magic tags. Options are '<' or '[' -
    cachetrue in production mode, false in other modestrue to cache template. -
    - * - */ - 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, '
    ').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); - }; - - EJS.Helpers.prototype.date_tag = function(name, value , html_options) { - if(!(value instanceof(Date))) value = new Date(); - var month_names = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; - var years = [], months = [], days =[]; - var year = value.getFullYear(); - var month = value.getMonth(); - var day = value.getDate(); - for (var y = year - 15; y < year+15 ; y++) { - years.push({value: y, text: y}); - }; - for (var m = 0; m < 12; m++) { - months.push({value: (m), text: month_names[m]}); - }; - for (var d = 0; d < 31; d++) { - days.push({value: (d+1), text: (d+1)}); - }; - var year_select = this.select_tag(name+'[year]', year, years, {id: name+'[year]'} ); - var month_select = this.select_tag(name+'[month]', month, months, {id: name+'[month]'}); - var day_select = this.select_tag(name+'[day]', day, days, {id: name+'[day]'}); - return year_select+month_select+day_select; - }; - - EJS.Helpers.prototype.form_tag = function(action, html_options) { - html_options = html_options || {}; - html_options.action = action; - if(html_options.multipart == true) { - html_options.method = 'post'; - html_options.enctype = 'multipart/form-data'; - }; - return this.start_tag_for('form', html_options); - }; - - EJS.Helpers.prototype.form_tag_end = function() { - return this.tag_end('form'); - }; - - EJS.Helpers.prototype.hidden_field_tag = function(name, value, html_options) { - return this.input_field_tag(name, value, 'hidden', html_options); - }; - - EJS.Helpers.prototype.input_field_tag = function(name, value , inputType, html_options) { - html_options = html_options || {}; - html_options.id = html_options.id || name; - html_options.value = value || ''; - html_options.type = inputType || 'text'; - html_options.name = name; - return this.single_tag_for('input', html_options); - }; - - EJS.Helpers.prototype.is_current_page = function(url) { - return (window.location.href == url || window.location.pathname == url ? true : false); - }; - - EJS.Helpers.prototype.link_to = function(name, url, html_options) { - if(!name) var name = 'null'; - if(!html_options) var html_options = {}; - if(html_options.confirm){ - html_options.onclick = " var ret_confirm = confirm(\""+html_options.confirm+"\"); if(!ret_confirm){ return false;} "; - html_options.confirm = null; - }; - html_options.href = url; - return this.start_tag_for('a', html_options)+name+ this.tag_end('a'); - }; - - EJS.Helpers.prototype.submit_link_to = function(name, url, html_options) { - if(!name) var name = 'null'; - if(!html_options) var html_options = {}; - html_options.onclick = html_options.onclick || ''; - if(html_options.confirm){ - html_options.onclick = " var ret_confirm = confirm(\""+html_options.confirm+"\"); if(!ret_confirm){ return false;} "; - html_options.confirm = null; - }; - html_options.value = name; - html_options.type = 'submit'; - html_options.onclick = html_options.onclick + (url ? this.url_for(url) : '')+'return false;'; - return this.start_tag_for('input', html_options); - }; - - EJS.Helpers.prototype.link_to_if = function(condition, name, url, html_options, post, block) { - return this.link_to_unless((condition == false), name, url, html_options, post, block); - }; - - EJS.Helpers.prototype.link_to_unless = function(condition, name, url, html_options, block) { - html_options = html_options || {}; - if(condition) { - if(block && typeof(block) == 'function') { - return block(name, url, html_options, block); - } else { - return name; - }; - } else { - return this.link_to(name, url, html_options); - }; - }; - - EJS.Helpers.prototype.link_to_unless_current = function(name, url, html_options, block) { - html_options = html_options || {}; - return this.link_to_unless(this.is_current_page(url), name, url, html_options, block); - }; - - - EJS.Helpers.prototype.password_field_tag = function(name, value, html_options) { - return this.input_field_tag(name, value, 'password', html_options); - }; - - EJS.Helpers.prototype.select_tag = function(name, value, choices, html_options) { - html_options = html_options || {}; - html_options.id = html_options.id || name; - html_options.value = value; - html_options.name = name; - var txt = ''; - txt += this.start_tag_for('select', html_options); - for(var i = 0; i < choices.length; i++) { - var choice = choices[i]; - var optionOptions = {value: choice.value}; - if(choice.value == value) optionOptions.selected ='selected'; - txt += this.start_tag_for('option', optionOptions )+choice.text+this.tag_end('option'); - }; - txt += this.tag_end('select'); - return txt; - }; - - EJS.Helpers.prototype.single_tag_for = function(tag, html_options) { - return this.tag(tag, html_options, '/>'); - }; - - EJS.Helpers.prototype.start_tag_for = function(tag, html_options) { - return this.tag(tag, html_options); - }; - - EJS.Helpers.prototype.submit_tag = function(name, html_options) { - html_options = html_options || {}; - html_options.type = html_options.type || 'submit'; - html_options.value = name || 'Submit'; - return this.single_tag_for('input', html_options); - }; - - EJS.Helpers.prototype.tag = function(tag, html_options, end) { - if(!end) var end = '>'; - var txt = ' '; - for(var attr in html_options) { - if(html_options[attr] != null) { - var value = html_options[attr].toString(); - } else { - var value=''; - }; - // special case because "class" is a reserved word in IE - if(attr == "Class") attr = "class"; - if( value.indexOf("'") != -1 ) { - txt += attr+'=\"'+value+'\" '; - } else { - txt += attr+"='"+value+"' "; - }; - }; - return '<' + tag + txt + end; - }; - - EJS.Helpers.prototype.tag_end = function(tag) { - return ''; - }; - - EJS.Helpers.prototype.text_area_tag = function(name, value, html_options) { - html_options = html_options || {}; - html_options.id = html_options.id || name; - html_options.name = html_options.name || name; - value = value || ''; - if(html_options.size) { - html_options.cols = html_options.size.split('x')[0]; - html_options.rows = html_options.size.split('x')[1]; - delete html_options.size; - }; - html_options.cols = html_options.cols || 50; - html_options.rows = html_options.rows || 4; - return this.start_tag_for('textarea', html_options)+value+this.tag_end('textarea'); - }; - - EJS.Helpers.prototype.text_tag = EJS.Helpers.prototype.text_area_tag; - - EJS.Helpers.prototype.text_field_tag = function(name, value, html_options) { - return this.input_field_tag(name, value, 'text', html_options); - }; - - EJS.Helpers.prototype.url_for = function(url) { - return 'window.location="' + url + '";'; - }; - - EJS.Helpers.prototype.img_tag = function(image_location, alt, options){ - options = options || {}; - options.src = image_location; - options.alt = alt; - return this.single_tag_for('img', options); - }; - - // ------------------------------------------------------------- - - Sammy = Sammy || {}; - - Sammy.EJS = function(app, method_alias) { - - // *Helper:* Uses simple templating to parse ERB like templates. - // - // ### Arguments - // - // * `template` A String template. '<% %>' tags are evaluated as Javascript and replaced with the elements in data. - // * `data` An Object containing the replacement values for the template. - //data is extended with the EventContext allowing you to call its methods within the template. - // * `name` An optional String name to cache the template. - // - var template = function(template, data, name) { - // use name for caching - if (typeof name == 'undefined') name = template; - return new EJS({text: template, name: name}).render(data); - }; - - // set the default method name/extension - if (!method_alias) method_alias = 'ejs'; - - // create the helper at the method alias - app.helper(method_alias, template); - - }; - -})(jQuery); diff --git a/public/javascripts/plugins/sammy.exceptional.js b/public/javascripts/plugins/sammy.exceptional.js deleted file mode 100644 index 8b4dfe6..0000000 --- a/public/javascripts/plugins/sammy.exceptional.js +++ /dev/null @@ -1,29 +0,0 @@ -(function($) { - - Sammy = Sammy || {}; - - // A plugin that posts errors to Exceptional. - // - // ### Arguments - // - // Sammy.Exceptional accepts an optional argument that is the Exceptional - // implementation. It will default to the global `Exceptional` object. - // - // ### Requirements - // - // The sole requirement is a global Exceptional object with a handle - // function. Contrast have published an implementation (see below). - // - // ### See Also - // * http://www.getexceptional.com/ - // * https://github.com/contrast/exceptional-js - Sammy.Exceptional = function(app, errorReporter) { - errorReporter = errorReporter || window.Exceptional; - app.bind('error', function(e, data) { - if (data && data.error) { - errorReporter.handle(data.error.message, window.location.href, '0'); - } - }); - }; - -}(jQuery)); diff --git a/public/javascripts/plugins/sammy.flash.js b/public/javascripts/plugins/sammy.flash.js deleted file mode 100644 index de4fad0..0000000 --- a/public/javascripts/plugins/sammy.flash.js +++ /dev/null @@ -1,97 +0,0 @@ -(function($) { - - Sammy = Sammy || {}; - - Sammy.FlashHash = function() { - this.now = {}; - }; - - Sammy.FlashHash.prototype = { - // @return [String] this Flash, rendered as an
      . - toHTML: function() { - result = this._renderUL(); - this.clear(); - return result; - }, - - clear: function() { - this._clearHash(this); - this._clearHash(this.now); - }, - - // Callback on redirect. - // @api private - _onRedirect: function() { - this._clearHash(this.now); - }, - - // clear out all flash keys - // @api private - _clearHash: function(hash) { - var key; - for (key in hash) { - if (key !== 'now' && hash.hasOwnProperty(key)) { - hash[key] = undefined; - } - } - }, - - _renderUL: function() { - return '
        ' + - this._renderLIs(this) + - this._renderLIs(this.now) + - '
      '; - }, - - _renderLIs: function(hash) { - var result = '', - key; - for (key in hash) { - if (hash[key] && key !== 'now' && hash.hasOwnProperty(key)) { - result = result + '
    • ' + hash[key] + '
    • '; - } - } - console.log('rendered flash: ' + result); - return result; - } - }; - - Sammy.Flash = function(app) { - app.flash = new Sammy.FlashHash(); - - // *Helper* flash(key, value) get or set a flash message that will - // be erased on the next render (but not on redirect). - // - // @param [String] key, the Flash key - // @param [String] value, the new value; optional - // @return [Sammy.FlashHash, String, null] if a key was given, the value for that key; else, the Flash - app.helper('flash', function(key, value) { - if (arguments.length === 0) { - return this.app.flash; - } else if (arguments.length === 2) { - this.app.flash[key] = value; - } - return this.app.flash[key]; - }); - - // *Helper* flashNow(key, value) get or set a flash message that - // will be erased on the next render or redirect. - // - // @param [String] key, the Flash key - // @param [String] value, the new value; optional - // @return [String, null] the value for the given key - app.helper('flashNow', function(key, value) { - if (arguments.length === 0) { - return this.app.flash.now; - }else if (arguments.length === 2) { - this.app.flash.now[key] = value; - } - return this.app.flash.now[key]; - }); - - app.bind('redirect', function() { - this.app.flash._onRedirect(); - }); - }; - -})(jQuery); diff --git a/public/javascripts/plugins/sammy.form.js b/public/javascripts/plugins/sammy.form.js deleted file mode 100644 index 53825ea..0000000 --- a/public/javascripts/plugins/sammy.form.js +++ /dev/null @@ -1,277 +0,0 @@ -(function($) { - - Sammy = Sammy || {}; - - function getStringContent(object, content) { - if (typeof content === 'undefined') { - return ''; - } else if ($.isFunction(content)) { - content = content.apply(object); - } - return content.toString(); - }; - - function simple_element(tag, attributes, content) { - var html = "<"; - html += tag; - if (typeof attributes != 'undefined') { - $.each(attributes, function(key, value) { - if (value != null) { - html += " " + key + "='"; - html += getStringContent(attributes, value).replace(/\'/g, "\'"); - html += "'"; - } - }); - } - if (content === false) { - html += ">"; - } else if (typeof content != 'undefined') { - html += ">"; - html += getStringContent(this, content); - html += ""; - } else { - html += " />"; - } - return html; - }; - - // Sammy.FormBuilder is based very closely on the Rails FormBuilder classes. - // Its goal is to make it easy to create HTML forms for creating and editing - // JavaScript objects. It eases the process by auto-populating existing values - // into form inputs and creating input names suitable for parsing by - // Sammy.NestedParams and other backend frameworks. - // - // You initialize a Sammy.FormBuilder by passing the 'name' of the object and - // the object itself. Once initialized you create form elements with the object's - // prototype methods. Each of these methods returns a string of HTML suitable for - // appending through a template or directly with jQuery. - // - // ### Example - // - // var item = { - // name: 'My Item', - // price: '$25.50', - // meta: { - // id: '123' - // } - // }; - // var form = new Sammy.FormBuilder('item', item); - // form.text('name'); - // //=> - // - // Nested attributes can be accessed/referred to by a 'keypath' which is - // basically a string representation of the dot notation. - // - // form.hidden('meta.id'); - // //=> - // - Sammy.FormBuilder = function(name, object) { - this.name = name; - this.object = object; - }; - - $.extend(Sammy.FormBuilder.prototype, { - - // creates the open form tag with the object attributes - open: function(attributes) { - return simple_element('form', $.extend({'method': 'post', 'action': '#/' + this.name + 's'}, attributes), false); - }, - - // closes the form - close: function() { - return ''; - }, - - // creates a label for `keypath` with the text `content - // with an optional `attributes` object - label: function(keypath, content, attributes) { - var attrs = {'for': this._attributesForKeyPath(keypath).name}; - return simple_element('label', $.extend(attrs, attributes), content); - }, - - // creates a hidden input for `keypath` with an optional `attributes` object - hidden: function(keypath, attributes) { - attributes = $.extend({type: 'hidden'}, this._attributesForKeyPath(keypath), attributes); - return simple_element('input', attributes); - }, - - // creates a text input for `keypath` with an optional `attributes` object - text: function(keypath, attributes) { - attributes = $.extend({type: 'text'}, this._attributesForKeyPath(keypath), attributes); - return simple_element('input', attributes); - }, - - // creates a textarea for `keypath` with an optional `attributes` object - textarea: function(keypath, attributes) { - var current; - attributes = $.extend(this._attributesForKeyPath(keypath), attributes); - current = attributes.value; - delete attributes['value']; - return simple_element('textarea', attributes, current); - }, - - // creates a password input for `keypath` with an optional `attributes` object - password: function(keypath, attributes) { - return this.text(keypath, $.extend({type: 'password'}, attributes)); - }, - - // creates a select element for `keypath` with the option elements - // specified by an array in `options`. If `options` is an array of arrays, - // the first element in each subarray becomes the text of the option and the - // second becomes the value. - // - // ### Example - // - // var options = [ - // ['Small', 's'], - // ['Medium', 'm'], - // ['Large', 'l'] - // ]; - // form.select('size', options); - // //=> - // - // - // - // - // - // And a Sammy app like: - // - // var app = $.sammy(function(app) { - // this.use(Sammy.NestedParams); - // - // this.post('#/parse_me', function(context) { - // $.log(this.params); - // }); - // }); - // - // If you filled out the form with some values and submitted it, you would see something - // like this in your log: - // - // { - // 'obj': { - // 'first': 'value', - // 'second': 'value', - // 'hash': { - // 'first': 'value', - // 'second': 'value' - // } - // } - // } - // - // It supports creating arrays with [] and other niceities. Check out the tests for - // full specs. - // - Sammy.NestedParams = function(app) { - - app._parseParamPair = parseNestedParam; - - }; - -})(jQuery); diff --git a/public/javascripts/plugins/sammy.oauth2.js b/public/javascripts/plugins/sammy.oauth2.js deleted file mode 100644 index 6d0fab2..0000000 --- a/public/javascripts/plugins/sammy.oauth2.js +++ /dev/null @@ -1,144 +0,0 @@ -(function($) { - Sammy = Sammy || {}; - - // Sammy.OAuth2 is a plugin for using OAuth 2.0 to authenticate users and - // access your application's API. Requires Sammy.Session. - // - // Triggers the following events: - // - // * `oauth.connected` - Access token set and ready to use. Triggered when new - // access token acquired, of when application starts and already has access - // token. - // * `oauth.disconnected` - Access token reset. Triggered by - // loseAccessToken(). - // * `oauth.denied` - Authorization attempt rejected. - // - // ### Example - // - // this.use('Storage'); - // this.use('OAuth2'); - // this.oauthorize = "/oauth/authorize"; - // - // // The quick & easy way - // this.requireOAuth(); - // // Specific path - // this.requireOAuth("/private"); - // // Filter you can apply to specific URLs - // this.before(function(context) { return context.requireOAuth(); }) - // // Apply to specific request - // this.get("/private", function(context) { - // this.requireOAuth(function() { - // // Do something - // }); - // }); - // - // // Sign in/sign out. - // this.bind("oauth.connected", function() { $("#signin").hide() }); - // this.bind("oauth.disconnected", function() { $("#signin").show() }); - // - // // Handle access denied and other errors - // this.bind("oauth.denied", function(evt, error) { - // this.partial("admin/views/no_access.tmpl", { error: error.message }); - // }); - // - // // Sign out. - // this.get("#/signout", function(context) { - // context.loseAccessToken(); - // context.redirect("#/"); - // }); - // - Sammy.OAuth2 = function(app) { - app.use('JSON'); - this.authorize = "/oauth/authorize"; - - // Use this on request that require OAuth token. You can use this in a - // filter: it will redirect and return false if the access token is missing. - // You can use it in a route, it will redirect to get the access token, or - // call the callback function if it has an access token. - this.helper("requireOAuth", function(cb) { - if (this.app.getAccessToken()) { - if (cb) - cb.apply(this); - } else { - this.redirect(this.app.authorize + "?state=" + escape(this.path)); - return false; - } - }); - - // Use this to sign out. - this.helper("loseAccessToken", function() { - this.app.loseAccessToken(); - }); - - // Use this in your application to require an OAuth access token on all, or - // the specified paths. It sets up a before filter on the specified paths. - this.requireOAuth = function(options) { - this.before(options || {}, function(context) { - return context.requireOAuth(); - }); - } - - // Returns the access token. Uses Sammy.Session to store the token. - this.getAccessToken = function() { - return this.session("oauth.token"); - } - // Stores the access token in the session. - this.setAccessToken = function(token) { - this.session("oauth.token", token); - this.trigger("oauth.connected"); - } - // Lose access token: use this to sign out. - this.loseAccessToken = function() { - this.session("oauth.token", null); - this.trigger("oauth.disconnected"); - } - - // Add OAuth 2.0 access token to all XHR requests. - $(document).ajaxSend(function(evt, xhr) { - var token = app.getAccessToken(); - if (token) - xhr.setRequestHeader("Authorization", "OAuth " + token); - }); - - // Converts query string parameters in fragment identifier to object. - function parseParams(hash) { - var pairs = hash.substring(1).split("&"), params = {}; - for (var i in pairs) { - var splat = pairs[i].split("="); - params[splat[0]] = splat[1].replace(/\+/g, " "); - } - return params; - } - - var start_url; - // Capture the application's start URL, we'll need that later on for - // redirection. - this.bind("run", function(evt, params) { - start_url = params.start_url || "#"; - if (this.app.getAccessToken()) - this.trigger("oauth.connected"); - }); - - // Intercept OAuth authorization response with access token, stores it and - // redirects to original URL, or application root. - this.before(/^#(access_token=|[^\\].*\&access_token=)/, function(context) { - var params = parseParams(context.path); - this.app.setAccessToken(params.access_token); - // When the filter redirected the original request, it passed the original - // request's URL in the state parameter, which we get back after - // authorization. - context.redirect(params.state.length == 0 ? this.app.start_url : unescape(params.state)); - return false; - }).get(/^#(access_token=|[^\\].*\&access_token=)/, function(context) { }); - - // Intercept OAuth authorization response with error (typically access - // denied). - this.before(/^#(error=|[^\\].*\&error=)/, function(context) { - var params = parseParams(context.path); - var message = params.error_description || "Access denined"; - context.trigger("oauth.denied", { code: params.error, message: message }); - return false; - }).get(/^#(error=|[^\\].*\&error=)/, function(context) { }); - - } -})(jQuery); diff --git a/public/javascripts/plugins/sammy.path_location_proxy.js b/public/javascripts/plugins/sammy.path_location_proxy.js deleted file mode 100644 index 13f9014..0000000 --- a/public/javascripts/plugins/sammy.path_location_proxy.js +++ /dev/null @@ -1,29 +0,0 @@ -(function($) { - - Sammy = Sammy || {}; - - // `Sammy.PathLocationProxy` is a simple Location Proxy that just - // gets and sets window.location. This allows you to use - // Sammy to route on the full URL path instead of just the hash. It - // will take a full refresh to get the app to change state. - // - // To read more about location proxies, check out the - // documentation for `Sammy.HashLocationProxy` - Sammy.PathLocationProxy = function(app) { - this.app = app; - }; - - Sammy.PathLocationProxy.prototype = { - bind: function() {}, - unbind: function() {}, - - getLocation: function() { - return [window.location.pathname, window.location.search].join(''); - }, - - setLocation: function(new_location) { - return window.location = new_location; - } - }; - -})(jQuery); diff --git a/public/javascripts/plugins/sammy.pure.js b/public/javascripts/plugins/sammy.pure.js deleted file mode 100644 index 16fe3df..0000000 --- a/public/javascripts/plugins/sammy.pure.js +++ /dev/null @@ -1,756 +0,0 @@ -(function($) { - -/*! - PURE Unobtrusive Rendering Engine for HTML - - Licensed under the MIT licenses. - More information at: http://www.opensource.org - - Copyright (c) 2010 Michael Cvilic - BeeBole.com - - Thanks to Rog Peppe for the functional JS jump - revision: 2.47 -*/ - -var $p, pure = $p = function(){ - var sel = arguments[0], - ctxt = false; - - if(typeof sel === 'string'){ - ctxt = arguments[1] || false; - } - return $p.core(sel, ctxt); -}; - -$p.core = function(sel, ctxt, plugins){ - //get an instance of the plugins - var plugins = getPlugins(), - templates = []; - - //search for the template node(s) - switch(typeof sel){ - case 'string': - templates = plugins.find(ctxt || document, sel); - if(templates.length === 0) { - error('The template "' + sel + '" was not found'); - } - break; - case 'undefined': - error('The template root is undefined, check your selector'); - break; - default: - templates = [sel]; - } - - for(var i = 0, ii = templates.length; i < ii; i++){ - plugins[i] = templates[i]; - } - plugins.length = ii; - - // set the signature string that will be replaced at render time - var Sig = '_s' + Math.floor( Math.random() * 1000000 ) + '_', - // another signature to prepend to attributes and avoid checks: style, height, on[events]... - attPfx = '_a' + Math.floor( Math.random() * 1000000 ) + '_', - // rx to parse selectors, e.g. "+tr.foo[class]" - selRx = /^(\+)?([^\@\+]+)?\@?([^\+]+)?(\+)?$/, - // set automatically attributes for some tags - autoAttr = { - IMG:'src', - INPUT:'value' - }, - // check if the argument is an array - thanks salty-horse (Ori Avtalion) - isArray = Array.isArray ? - function(o) { - return Array.isArray(o); - } : - function(o) { - return Object.prototype.toString.call(o) === "[object Array]"; - }; - - return plugins; - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * - core functions - * * * * * * * * * * * * * * * * * * * * * * * * * */ - - - // error utility - function error(e){ - if(typeof console !== 'undefined'){ - console.log(e); - }else{ alert(e); } - throw('pure error: ' + e); - } - - //return a new instance of plugins - function getPlugins(){ - var plugins = $p.plugins, - f = function(){}; - f.prototype = plugins; - - // do not overwrite functions if external definition - f.prototype.compile = plugins.compile || compile; - f.prototype.render = plugins.render || render; - f.prototype.autoRender = plugins.autoRender || autoRender; - f.prototype.find = plugins.find || find; - - // give the compiler and the error handling to the plugin context - f.prototype._compiler = compiler; - f.prototype._error = error; - - return new f(); - } - - // returns the outer HTML of a node - function outerHTML(node){ - // if IE take the internal method otherwise build one - return node.outerHTML || ( - function(n){ - var div = document.createElement('div'), h; - div.appendChild( n.cloneNode(true) ); - h = div.innerHTML; - div = null; - return h; - })(node); - } - - // returns the string generator function - function wrapquote(qfn, f){ - return function(ctxt){ - return qfn('' + f.call(ctxt.context, ctxt)); - }; - } - - // default find using querySelector when available on the browser - function find(n, sel){ - if(typeof n === 'string'){ - sel = n; - n = false; - } - if(typeof document.querySelectorAll !== 'undefined'){ - return (n||document).querySelectorAll( sel ); - }else{ - error('You can test PURE standalone with: iPhone, FF3.5+, Safari4+ and IE8+\n\nTo run PURE on your browser, you need a JS library/framework with a CSS selector engine'); - } - } - - // create a function that concatenates constant string - // sections (given in parts) and the results of called - // functions to fill in the gaps between parts (fns). - // fns[n] fills in the gap between parts[n-1] and parts[n]; - // fns[0] is unused. - // this is the inner template evaluation loop. - function concatenator(parts, fns){ - return function(ctxt){ - var strs = [ parts[ 0 ] ], - n = parts.length, - fnVal, pVal, attLine, pos; - - for(var i = 1; i < n; i++){ - fnVal = fns[i]( ctxt ); - pVal = parts[i]; - - // if the value is empty and attribute, remove it - if(fnVal === ''){ - attLine = strs[ strs.length - 1 ]; - if( ( pos = attLine.search( /[\w]+=\"?$/ ) ) > -1){ - strs[ strs.length - 1 ] = attLine.substring( 0, pos ); - pVal = pVal.substr( 1 ); - } - } - - strs[ strs.length ] = fnVal; - strs[ strs.length ] = pVal; - } - return strs.join(''); - }; - } - - // parse and check the loop directive - function parseloopspec(p){ - var m = p.match( /^(\w+)\s*<-\s*(\S+)?$/ ); - if(m === null){ - error('bad loop spec: "' + p + '"'); - } - if(m[1] === 'item'){ - error('"item<-..." is a reserved word for the current running iteration.\n\nPlease choose another name for your loop.'); - } - if( !m[2] || (m[2] && (/context/i).test(m[2]))){ //undefined or space(IE) - m[2] = function(ctxt){return ctxt.context;}; - } - return {name: m[1], sel: m[2]}; - } - - // parse a data selector and return a function that - // can traverse the data accordingly, given a context. - function dataselectfn(sel){ - if(typeof(sel) === 'function'){ - return sel; - } - //check for a valid js variable name with hyphen(for properties only), $, _ and : - var m = sel.match(/^[a-zA-Z\$_\@][\w\$:-]*(\.[\w\$:-]*[^\.])*$/); - if(m === null){ - var found = false, s = sel, parts = [], pfns = [], i = 0, retStr; - // check if literal - if(/\'|\"/.test( s.charAt(0) )){ - if(/\'|\"/.test( s.charAt(s.length-1) )){ - retStr = s.substring(1, s.length-1); - return function(){ return retStr; }; - } - }else{ - // check if literal + #{var} - while((m = s.match(/#\{([^{}]+)\}/)) !== null){ - found = true; - parts[i++] = s.slice(0, m.index); - pfns[i] = dataselectfn(m[1]); - s = s.slice(m.index + m[0].length, s.length); - } - } - if(!found){ - error('bad data selector syntax: ' + sel); - } - parts[i] = s; - return concatenator(parts, pfns); - } - m = sel.split('.'); - return function(ctxt){ - var data = ctxt.context; - if(!data){ - return ''; - } - var v = ctxt[m[0]], - i = 0; - if(v && v.item){ - data = v.item; - i += 1; - } - var n = m.length; - for(; i < n; i++){ - if(!data){break;} - data = data[m[i]]; - } - return (!data && data !== 0) ? '':data; - }; - } - - // wrap in an object the target node/attr and their properties - function gettarget(dom, sel, isloop){ - var osel, prepend, selector, attr, append, target = []; - if( typeof sel === 'string' ){ - osel = sel; - var m = sel.match(selRx); - if( !m ){ - error( 'bad selector syntax: ' + sel ); - } - - prepend = m[1]; - selector = m[2]; - attr = m[3]; - append = m[4]; - - if(selector === '.' || ( !selector && attr ) ){ - target[0] = dom; - }else{ - target = plugins.find(dom, selector); - } - if(!target || target.length === 0){ - return error('The node "' + sel + '" was not found in the template'); - } - }else{ - // autoRender node - prepend = sel.prepend; - attr = sel.attr; - append = sel.append; - target = [dom]; - } - - if( prepend || append ){ - if( prepend && append ){ - error('append/prepend cannot take place at the same time'); - }else if( isloop ){ - error('no append/prepend/replace modifiers allowed for loop target'); - }else if( append && isloop ){ - error('cannot append with loop (sel: ' + osel + ')'); - } - } - var setstr, getstr, quotefn, isStyle, isClass, attName, setfn; - if(attr){ - isStyle = (/^style$/i).test(attr); - isClass = (/^class$/i).test(attr); - attName = isClass ? 'className' : attr; - setstr = function(node, s) { - node.setAttribute(attPfx + attr, s); - if (attName in node && !isStyle) { - node[attName] = ''; - } - if (node.nodeType === 1) { - node.removeAttribute(attr); - isClass && node.removeAttribute(attName); - } - }; - if (isStyle || isClass) {//IE no quotes special care - if(isStyle){ - getstr = function(n){ return n.style.cssText; }; - }else{ - getstr = function(n){ return n.className; }; - } - quotefn = function(s){ return s.replace(/\"/g, '"'); }; - }else { - getstr = function(n){ return n.getAttribute(attr); }; - quotefn = function(s){ return s.replace(/\"/g, '"').replace(/\s/g, ' '); }; - } - if(prepend){ - setfn = function(node, s){ setstr( node, s + getstr( node )); }; - }else if(append){ - setfn = function(node, s){ setstr( node, getstr( node ) + s); }; - }else{ - setfn = function(node, s){ setstr( node, s ); }; - } - }else{ - if (isloop) { - setfn = function(node, s) { - var pn = node.parentNode; - if (pn) { - //replace node with s - pn.insertBefore(document.createTextNode(s), node.nextSibling); - pn.removeChild(node); - } - }; - } else { - if (prepend) { - setfn = function(node, s) { node.insertBefore(document.createTextNode(s), node.firstChild); }; - } else if (append) { - setfn = function(node, s) { node.appendChild(document.createTextNode(s));}; - } else { - setfn = function(node, s) { - while (node.firstChild) { node.removeChild(node.firstChild); } - node.appendChild(document.createTextNode(s)); - }; - } - } - quotefn = function(s) { return s; }; - } - return { attr: attr, nodes: target, set: setfn, sel: osel, quotefn: quotefn }; - } - - function setsig(target, n){ - var sig = Sig + n + ':'; - for(var i = 0; i < target.nodes.length; i++){ - // could check for overlapping targets here. - target.set( target.nodes[i], sig ); - } - } - - // read de loop data, and pass it to the inner rendering function - function loopfn(name, dselect, inner, sorter, filter){ - return function(ctxt){ - var a = dselect(ctxt), - old = ctxt[name], - temp = { items : a }, - filtered = 0, - length, - strs = [], - buildArg = function(idx, temp, ftr, len){ - ctxt.pos = temp.pos = idx; - ctxt.item = temp.item = a[ idx ]; - ctxt.items = a; - //if array, set a length property - filtered items - typeof len !== 'undefined' && (ctxt.length = len); - //if filter directive - if(typeof ftr === 'function' && ftr(ctxt) === false){ - filtered++; - return; - } - strs.push( inner.call(temp, ctxt ) ); - }; - ctxt[name] = temp; - if( isArray(a) ){ - length = a.length || 0; - // if sort directive - if(typeof sorter === 'function'){ - a.sort(sorter); - } - //loop on array - for(var i = 0, ii = length; i < ii; i++){ - buildArg(i, temp, filter, length - filtered); - } - }else{ - if(a && typeof sorter !== 'undefined'){ - error('sort is only available on arrays, not objects'); - } - //loop on collections - for(var prop in a){ - a.hasOwnProperty( prop ) && buildArg(prop, temp, filter); - } - } - - typeof old !== 'undefined' ? ctxt[name] = old : delete ctxt[name]; - return strs.join(''); - }; - } - // generate the template for a loop node - function loopgen(dom, sel, loop, fns){ - var already = false, ls, sorter, filter, prop; - for(prop in loop){ - if(loop.hasOwnProperty(prop)){ - if(prop === 'sort'){ - sorter = loop.sort; - continue; - }else if(prop === 'filter'){ - filter = loop.filter; - continue; - } - if(already){ - error('cannot have more than one loop on a target'); - } - ls = prop; - already = true; - } - } - if(!ls){ - error('Error in the selector: ' + sel + '\nA directive action must be a string, a function or a loop(<-)'); - } - var dsel = loop[ls]; - // if it's a simple data selector then we default to contents, not replacement. - if(typeof(dsel) === 'string' || typeof(dsel) === 'function'){ - loop = {}; - loop[ls] = {root: dsel}; - return loopgen(dom, sel, loop, fns); - } - var spec = parseloopspec(ls), - itersel = dataselectfn(spec.sel), - target = gettarget(dom, sel, true), - nodes = target.nodes; - - for(i = 0; i < nodes.length; i++){ - var node = nodes[i], - inner = compiler(node, dsel); - fns[fns.length] = wrapquote(target.quotefn, loopfn(spec.name, itersel, inner, sorter, filter)); - target.nodes = [node]; // N.B. side effect on target. - setsig(target, fns.length - 1); - } - } - - function getAutoNodes(n, data){ - var ns = n.getElementsByTagName('*'), - an = [], - openLoops = {a:[],l:{}}, - cspec, - isNodeValue, - i, ii, j, jj, ni, cs, cj; - //for each node found in the template - for(i = -1, ii = ns.length; i < ii; i++){ - ni = i > -1 ?ns[i]:n; - if(ni.nodeType === 1 && ni.className !== ''){ - //when a className is found - cs = ni.className.split(' '); - // for each className - for(j = 0, jj=cs.length;j -1 || isNodeValue){ - ni.className = ni.className.replace('@'+cspec.attr, ''); - if(isNodeValue){ - cspec.attr = false; - } - } - an.push({n:ni, cspec:cspec}); - } - } - } - } - return an; - - function checkClass(c, tagName){ - // read the class - var ca = c.match(selRx), - attr = ca[3] || autoAttr[tagName], - cspec = {prepend:!!ca[1], prop:ca[2], attr:attr, append:!!ca[4], sel:c}, - i, ii, loopi, loopil, val; - // check in existing open loops - for(i = openLoops.a.length-1; i >= 0; i--){ - loopi = openLoops.a[i]; - loopil = loopi.l[0]; - val = loopil && loopil[cspec.prop]; - if(typeof val !== 'undefined'){ - cspec.prop = loopi.p + '.' + cspec.prop; - if(openLoops.l[cspec.prop] === true){ - val = val[0]; - } - break; - } - } - // not found check first level of data - if(typeof val === 'undefined'){ - val = isArray(data) ? data[0][cspec.prop] : data[cspec.prop]; - // nothing found return - if(typeof val === 'undefined'){ - return false; - } - } - // set the spec for autoNode - if(isArray(val)){ - openLoops.a.push( {l:val, p:cspec.prop} ); - openLoops.l[cspec.prop] = true; - cspec.t = 'loop'; - }else{ - cspec.t = 'str'; - } - return cspec; - } - } - - // returns a function that, given a context argument, - // will render the template defined by dom and directive. - function compiler(dom, directive, data, ans){ - var fns = []; - // autoRendering nodes parsing -> auto-nodes - ans = ans || data && getAutoNodes(dom, data); - if(data){ - var j, jj, cspec, n, target, nodes, itersel, node, inner; - // for each auto-nodes - while(ans.length > 0){ - cspec = ans[0].cspec; - n = ans[0].n; - ans.splice(0, 1); - if(cspec.t === 'str'){ - // if the target is a value - target = gettarget(n, cspec, false); - setsig(target, fns.length); - fns[fns.length] = wrapquote(target.quotefn, dataselectfn(cspec.prop)); - }else{ - // if the target is a loop - itersel = dataselectfn(cspec.sel); - target = gettarget(n, cspec, true); - nodes = target.nodes; - for(j = 0, jj = nodes.length; j < jj; j++){ - node = nodes[j]; - inner = compiler(node, false, data, ans); - fns[fns.length] = wrapquote(target.quotefn, loopfn(cspec.sel, itersel, inner)); - target.nodes = [node]; - setsig(target, fns.length - 1); - } - } - } - } - // read directives - var target, dsel; - for(var sel in directive){ - if(directive.hasOwnProperty(sel)){ - dsel = directive[sel]; - if(typeof(dsel) === 'function' || typeof(dsel) === 'string'){ - // set the value for the node/attr - target = gettarget(dom, sel, false); - setsig(target, fns.length); - fns[fns.length] = wrapquote(target.quotefn, dataselectfn(dsel)); - }else{ - // loop on node - loopgen(dom, sel, dsel, fns); - } - } - } - // convert node to a string - var h = outerHTML(dom), pfns = []; - // IE adds an unremovable "selected, value" attribute - // hard replace while waiting for a better solution - h = h.replace(/<([^>]+)\s(value\=""|selected)\s?([^>]*)>/ig, "<$1 $3>"); - - // remove attribute prefix - h = h.split(attPfx).join(''); - - // slice the html string at "Sig" - var parts = h.split( Sig ), p; - // for each slice add the return string of - for(var i = 1; i < parts.length; i++){ - p = parts[i]; - // part is of the form "fn-number:..." as placed there by setsig. - pfns[i] = fns[ parseInt(p, 10) ]; - parts[i] = p.substring( p.indexOf(':') + 1 ); - } - return concatenator(parts, pfns); - } - // compile the template with directive - // if a context is passed, the autoRendering is triggered automatically - // return a function waiting the data as argument - function compile(directive, ctxt, template){ - var rfn = compiler( ( template || this[0] ).cloneNode(true), directive, ctxt); - return function(context){ - return rfn({context:context}); - }; - } - //compile with the directive as argument - // run the template function on the context argument - // return an HTML string - // should replace the template and return this - function render(ctxt, directive){ - var fn = typeof directive === 'function' ? directive : plugins.compile( directive, false, this[0] ); - for(var i = 0, ii = this.length; i < ii; i++){ - this[i] = replaceWith( this[i], fn( ctxt, false )); - } - context = null; - return this; - } - - // compile the template with autoRender - // run the template function on the context argument - // return an HTML string - function autoRender(ctxt, directive){ - var fn = plugins.compile( directive, ctxt, this[0] ); - for(var i = 0, ii = this.length; i < ii; i++){ - this[i] = replaceWith( this[i], fn( ctxt, false)); - } - context = null; - return this; - } - - function replaceWith(elm, html) { - var ne, - ep = elm.parentNode, - depth = 0; - switch (elm.tagName) { - case 'TBODY': case 'THEAD': case 'TFOOT': - html = '' + html + '
      '; - depth = 1; - break; - case 'TR': - html = '' + html + '
      '; - depth = 2; - break; - case 'TD': case 'TH': - html = '' + html + '
      '; - depth = 3; - break; - } - tmp = document.createElement('SPAN'); - tmp.style.display = 'none'; - document.body.appendChild(tmp); - tmp.innerHTML = html; - ne = tmp.firstChild; - while (depth--) { - ne = ne.firstChild; - } - ep.insertBefore(ne, elm); - ep.removeChild(elm); - document.body.removeChild(tmp); - elm = ne; - - ne = ep = null; - return elm; - } -}; - -$p.plugins = {}; - -$p.libs = { - dojo:function(){ - if(typeof document.querySelector === 'undefined'){ - $p.plugins.find = function(n, sel){ - return dojo.query(sel, n); - }; - } - }, - domassistant:function(){ - if(typeof document.querySelector === 'undefined'){ - $p.plugins.find = function(n, sel){ - return $(n).cssSelect(sel); - }; - } - DOMAssistant.attach({ - publicMethods : [ 'compile', 'render', 'autoRender'], - compile:function(directive, ctxt){ return $p(this).compile(directive, ctxt); }, - render:function(ctxt, directive){ return $( $p(this).render(ctxt, directive) )[0]; }, - autoRender:function(ctxt, directive){ return $( $p(this).autoRender(ctxt, directive) )[0]; } - }); - }, - jquery:function(){ - if(typeof document.querySelector === 'undefined'){ - $p.plugins.find = function(n, sel){ - return jQuery(n).find(sel); - }; - } - jQuery.fn.extend({ - compile:function(directive, ctxt){ return $p(this[0]).compile(directive, ctxt); }, - render:function(ctxt, directive){ return jQuery( $p( this[0] ).render( ctxt, directive ) ); }, - autoRender:function(ctxt, directive){ return jQuery( $p( this[0] ).autoRender( ctxt, directive ) ); } - }); - }, - mootools:function(){ - if(typeof document.querySelector === 'undefined'){ - $p.plugins.find = function(n, sel){ - return $(n).getElements(sel); - }; - } - Element.implement({ - compile:function(directive, ctxt){ return $p(this).compile(directive, ctxt); }, - render:function(ctxt, directive){ return $p(this).render(ctxt, directive); }, - autoRender:function(ctxt, directive){ return $p(this).autoRender(ctxt, directive); } - }); - }, - prototype:function(){ - if(typeof document.querySelector === 'undefined'){ - $p.plugins.find = function(n, sel){ - n = n === document ? n.body : n; - return typeof n === 'string' ? $$(n) : $(n).select(sel); - }; - } - Element.addMethods({ - compile:function(element, directive, ctxt){ return $p(element).compile(directive, ctxt); }, - render:function(element, ctxt, directive){ return $p(element).render(ctxt, directive); }, - autoRender:function(element, ctxt, directive){ return $p(element).autoRender(ctxt, directive); } - }); - }, - sizzle:function(){ - if(typeof document.querySelector === 'undefined'){ - $p.plugins.find = function(n, sel){ - return Sizzle(sel, n); - }; - } - }, - sly:function(){ - if(typeof document.querySelector === 'undefined'){ - $p.plugins.find = function(n, sel){ - return Sly(sel, n); - }; - } - } -}; - -// get lib specifics if available -(function(){ - var libkey = - typeof dojo !== 'undefined' && 'dojo' || - typeof DOMAssistant !== 'undefined' && 'domassistant' || - typeof jQuery !== 'undefined' && 'jquery' || - typeof MooTools !== 'undefined' && 'mootools' || - typeof Prototype !== 'undefined' && 'prototype' || - typeof Sizzle !== 'undefined' && 'sizzle' || - typeof Sly !== 'undefined' && 'sly'; - - libkey && $p.libs[libkey](); -})(); - - - Sammy = Sammy || {}; - - // `Sammy.Pure` is a simple wrapper around the pure.js templating engine for - // use in Sammy apps. - // - // See http://beebole.com/pure/ for detailed documentation. - Sammy.Pure = function(app, method_alias) { - - var pure = function(template, data, directives) { - return $(template).autoRender(data, directives); - }; - - // set the default method name/extension - if (!method_alias) method_alias = 'pure'; - app.helper(method_alias, pure); - - }; - -})(jQuery); diff --git a/public/javascripts/plugins/sammy.storage.js b/public/javascripts/plugins/sammy.storage.js deleted file mode 100644 index ad3cb86..0000000 --- a/public/javascripts/plugins/sammy.storage.js +++ /dev/null @@ -1,577 +0,0 @@ -(function($) { - - Sammy = Sammy || {}; - - // Sammy.Store is an abstract adapter class that wraps the multitude of in - // browser data storage into a single common set of methods for storing and - // retreiving data. The JSON library is used (through the inclusion of the - // Sammy.JSON) plugin, to automatically convert objects back and forth from - // stored strings. - // - // Sammy.Store can be used directly, but within a Sammy.Application it is much - // easier to use the `Sammy.Storage` plugin and its helper methods. - // - // Sammy.Store also supports the KVO pattern, by firing DOM/jQuery Events when - // a key is set. - // - // ### Example - // - // // create a new store named 'mystore', tied to the #main element, using HTML5 localStorage - // // Note: localStorage only works on browsers that support it - // var store = new Sammy.Store({name: 'mystore', element: '#element', type: 'local'}); - // store.set('foo', 'bar'); - // store.get('foo'); //=> 'bar' - // store.set('json', {obj: 'this is an obj'}); - // store.get('json'); //=> {obj: 'this is an obj'} - // store.keys(); //=> ['foo','json'] - // store.clear('foo'); - // store.keys(); //=> ['json'] - // store.clearAll(); - // store.keys(); //=> [] - // - // ### Arguments - // - // The constructor takes a single argument which is a Object containing these possible options. - // - // * `name` The name/namespace of this store. Stores are unique by name/type. (default 'store') - // * `element` A selector for the element that the store is bound to. (default 'body') - // * `type` The type of storage/proxy to use (default 'memory') - // - // Extra options are passed to the storage constructor. - // Sammy.Store supports the following methods of storage: - // - // * `memory` Basic object storage - // * `data` jQuery.data DOM Storage - // * `cookie` Access to document.cookie. Limited to 2K - // * `local` HTML5 DOM localStorage, browswer support is currently limited. - // * `session` HTML5 DOM sessionStorage, browswer support is currently limited. - // - Sammy.Store = function(options) { - var store = this; - this.options = options || {}; - this.name = this.options.name || 'store'; - this.element = this.options.element || 'body'; - this.$element = $(this.element); - if ($.isArray(this.options.type)) { - $.each(this.options.type, function(i, type) { - if (Sammy.Store.isAvailable(type)) { - store.type = type; - return false; - } - }); - } else { - this.type = this.options.type || 'memory'; - } - this.meta_key = this.options.meta_key || '__keys__'; - this.storage = new Sammy.Store[Sammy.Store.stores[this.type]](this.name, this.element, this.options); - }; - - Sammy.Store.stores = { - 'memory': 'Memory', - 'data': 'Data', - 'local': 'LocalStorage', - 'session': 'SessionStorage', - 'cookie': 'Cookie' - }; - - $.extend(Sammy.Store.prototype, { - // Checks for the availability of the current storage type in the current browser/config. - isAvailable: function() { - if ($.isFunction(this.storage.isAvailable)) { - return this.storage.isAvailable(); - } else { - true; - } - }, - // Checks for the existance of `key` in the current store. Returns a boolean. - exists: function(key) { - return this.storage.exists(key); - }, - // Sets the value of `key` with `value`. If `value` is an - // object, it is turned to and stored as a string with `JSON.stringify`. - // It also tries to conform to the KVO pattern triggering jQuery events on the - // element that the store is bound to. - // - // ### Example - // - // var store = new Sammy.Store({name: 'kvo'}); - // $('body').bind('set-kvo-foo', function(e, data) { - // Sammy.log(data.key + ' changed to ' + data.value); - // }); - // store.set('foo', 'bar'); // logged: foo changed to bar - // - set: function(key, value) { - var string_value = (typeof value == 'string') ? value : JSON.stringify(value); - key = key.toString(); - this.storage.set(key, string_value); - if (key != this.meta_key) { - this._addKey(key); - this.$element.trigger('set-' + this.name, {key: key, value: value}); - this.$element.trigger('set-' + this.name + '-' + key, {key: key, value: value}); - }; - // always return the original value - return value; - }, - // Returns the set value at `key`, parsing with `JSON.parse` and - // turning into an object if possible - get: function(key) { - var value = this.storage.get(key); - if (typeof value == 'undefined' || value == null || value == '') { - return value; - } - try { - return JSON.parse(value); - } catch(e) { - return value; - } - }, - // Removes the value at `key` from the current store - clear: function(key) { - this._removeKey(key); - return this.storage.clear(key); - }, - // Clears all the values for the current store. - clearAll: function() { - var self = this; - this.each(function(key, value) { - self.clear(key); - }); - }, - // Returns the all the keys set for the current store as an array. - // Internally Sammy.Store keeps this array in a 'meta_key' for easy access. - keys: function() { - return this.get(this.meta_key) || []; - }, - // Iterates over each key value pair passing them to the `callback` function - // - // ### Example - // - // store.each(function(key, value) { - // Sammy.log('key', key, 'value', value); - // }); - // - each: function(callback) { - var i = 0, - keys = this.keys(), - returned; - - for (i; i < keys.length; i++) { - returned = callback(keys[i], this.get(keys[i])); - if (returned === false) { return false; } - }; - }, - // Filters the store by a filter function that takes a key value. - // Returns an array of arrays where the first element of each array - // is the key and the second is the value of that key. - // - // ### Example - // - // var store = new Sammy.Store; - // store.set('one', 'two'); - // store.set('two', 'three'); - // store.set('1', 'two'); - // var returned = store.filter(function(key, value) { - // // only return - // return value === 'two'; - // }); - // // returned => [['one', 'two'], ['1', 'two']]; - // - filter: function(callback) { - var found = []; - this.each(function(key, value) { - if (callback(key, value)) { - found.push([key, value]); - } - return true; - }); - return found; - }, - // Works exactly like filter except only returns the first matching key - // value pair instead of all of them - first: function(callback) { - var found = false; - this.each(function(key, value) { - if (callback(key, value)) { - found = [key, value]; - return false; - } - }); - return found; - }, - // Returns the value at `key` if set, otherwise, runs the callback - // and sets the value to the value returned in the callback. - // - // ### Example - // - // var store = new Sammy.Store; - // store.exists('foo'); //=> false - // store.fetch('foo', function() { - // return 'bar!'; - // }); //=> 'bar!' - // store.get('foo') //=> 'bar!' - // store.fetch('foo', function() { - // return 'baz!'; - // }); //=> 'bar! - // - fetch: function(key, callback) { - if (!this.exists(key)) { - return this.set(key, callback.apply(this)); - } else { - return this.get(key); - } - }, - // loads the response of a request to `path` into `key`. - // - // ### Example - // - // In /mytemplate.tpl: - // - // My Template - // - // In app.js: - // - // var store = new Sammy.Store; - // store.load('mytemplate', '/mytemplate.tpl', function() { - // s.get('mytemplate') //=> My Template - // }); - // - load: function(key, path, callback) { - var s = this; - $.get(path, function(response) { - s.set(key, response); - if (callback) { callback.apply(this, [response]); } - }); - }, - - _addKey: function(key) { - var keys = this.keys(); - if ($.inArray(key, keys) == -1) { keys.push(key); } - this.set(this.meta_key, keys); - }, - _removeKey: function(key) { - var keys = this.keys(); - var index = $.inArray(key, keys); - if (index != -1) { keys.splice(index, 1); } - this.set(this.meta_key, keys); - } - }); - - // Tests if the type of storage is available/works in the current browser/config. - // Especially useful for testing the availability of the awesome, but not widely - // supported HTML5 DOM storage - Sammy.Store.isAvailable = function(type) { - try { - return Sammy.Store[Sammy.Store.stores[type]].prototype.isAvailable(); - } catch(e) { - return false; - } - }; - - // Memory ('memory') is the basic/default store. It stores data in a global - // JS object. Data is lost on refresh. - Sammy.Store.Memory = function(name, element) { - this.name = name; - this.element = element; - this.namespace = [this.element, this.name].join('.'); - Sammy.Store.Memory.store = Sammy.Store.Memory.store || {}; - Sammy.Store.Memory.store[this.namespace] = Sammy.Store.Memory.store[this.namespace] || {}; - this.store = Sammy.Store.Memory.store[this.namespace]; - }; - $.extend(Sammy.Store.Memory.prototype, { - isAvailable: function() { return true; }, - exists: function(key) { - return (typeof this.store[key] != "undefined"); - }, - set: function(key, value) { - return this.store[key] = value; - }, - get: function(key) { - return this.store[key]; - }, - clear: function(key) { - delete this.store[key]; - } - }); - - // Data ('data') stores objects using the jQuery.data() methods. This has the advantadge - // of scoping the data to the specific element. Like the 'memory' store its data - // will only last for the length of the current request (data is lost on refresh/etc). - Sammy.Store.Data = function(name, element) { - this.name = name; - this.element = element; - this.$element = $(element); - }; - $.extend(Sammy.Store.Data.prototype, { - isAvailable: function() { return true; }, - exists: function(key) { - return !!this.$element.data(this._key(key)); - }, - set: function(key, value) { - return this.$element.data(this._key(key), value); - }, - get: function(key) { - return this.$element.data(this._key(key)); - }, - clear: function(key) { - this.$element.removeData(this._key(key)); - }, - _key: function(key) { - return ['store', this.name, key].join('.'); - } - }); - - // LocalStorage ('local') makes use of HTML5 DOM Storage, and the window.localStorage - // object. The great advantage of this method is that data will persist beyond - // the current request. It can be considered a pretty awesome replacement for - // cookies accessed via JS. The great disadvantage, though, is its only available - // on the latest and greatest browsers. - // - // For more info on DOM Storage: - // https://developer.mozilla.org/en/DOM/Storage - // http://www.w3.org/TR/2009/WD-webstorage-20091222/ - // - Sammy.Store.LocalStorage = function(name, element) { - this.name = name; - this.element = element; - }; - $.extend(Sammy.Store.LocalStorage.prototype, { - isAvailable: function() { - return ('localStorage' in window) && (window.location.protocol != 'file:'); - }, - exists: function(key) { - return (this.get(key) != null); - }, - set: function(key, value) { - return window.localStorage.setItem(this._key(key), value); - }, - get: function(key) { - return window.localStorage.getItem(this._key(key)); - }, - clear: function(key) { - window.localStorage.removeItem(this._key(key));; - }, - _key: function(key) { - return ['store', this.element, this.name, key].join('.'); - } - }); - - // .SessionStorage ('session') is similar to LocalStorage (part of the same API) - // and shares similar browser support/availability. The difference is that - // SessionStorage is only persistant through the current 'session' which is defined - // as the length that the current window is open. This means that data will survive - // refreshes but not close/open or multiple windows/tabs. For more info, check out - // the `LocalStorage` documentation and links. - Sammy.Store.SessionStorage = function(name, element) { - this.name = name; - this.element = element; - }; - $.extend(Sammy.Store.SessionStorage.prototype, { - isAvailable: function() { - return ('sessionStorage' in window) && - (window.location.protocol != 'file:') && - ($.isFunction(window.sessionStorage.setItem)); - }, - exists: function(key) { - return (this.get(key) != null); - }, - set: function(key, value) { - return window.sessionStorage.setItem(this._key(key), value); - }, - get: function(key) { - var value = window.sessionStorage.getItem(this._key(key)); - if (value && typeof value.value != "undefined") { value = value.value } - return value; - }, - clear: function(key) { - window.sessionStorage.removeItem(this._key(key));; - }, - _key: function(key) { - return ['store', this.element, this.name, key].join('.'); - } - }); - - // .Cookie ('cookie') storage uses browser cookies to store data. JavaScript - // has access to a single document.cookie variable, which is limited to 2Kb in - // size. Cookies are also considered 'unsecure' as the data can be read easily - // by other sites/JS. Cookies do have the advantage, though, of being widely - // supported and persistent through refresh and close/open. Where available, - // HTML5 DOM Storage like LocalStorage and SessionStorage should be used. - // - // .Cookie can also take additional options: - // - // * `expires_in` Number of seconds to keep the cookie alive (default 2 weeks). - // * `path` The path to activate the current cookie for (default '/'). - // - // For more information about document.cookie, check out the pre-eminint article - // by ppk: http://www.quirksmode.org/js/cookies.html - // - Sammy.Store.Cookie = function(name, element, options) { - this.name = name; - this.element = element; - this.options = options || {}; - this.path = this.options.path || '/'; - // set the expires in seconds or default 14 days - this.expires_in = this.options.expires_in || (14 * 24 * 60 * 60); - }; - $.extend(Sammy.Store.Cookie.prototype, { - isAvailable: function() { - return ('cookie' in document) && (window.location.protocol != 'file:'); - }, - exists: function(key) { - return (this.get(key) != null); - }, - set: function(key, value) { - return this._setCookie(key, value); - }, - get: function(key) { - return this._getCookie(key); - }, - clear: function(key) { - this._setCookie(key, "", -1); - }, - _key: function(key) { - return ['store', this.element, this.name, key].join('.'); - }, - _getCookie: function(key) { - var escaped = this._key(key).replace(/(\.|\*|\(|\)|\[|\])/g, '\\$1'); - var match = document.cookie.match("(^|;\\s)" + escaped + "=([^;]*)(;|$)") - return (match ? match[2] : null); - }, - _setCookie: function(key, value, expires) { - if (!expires) { expires = (this.expires_in * 1000) } - var date = new Date(); - date.setTime(date.getTime() + expires); - var set_cookie = [ - this._key(key), "=", value, - "; expires=", date.toGMTString(), - "; path=", this.path - ].join(''); - document.cookie = set_cookie; - } - }); - - // Sammy.Storage is a plugin that provides shortcuts for creating and using - // Sammy.Store objects. Once included it provides the `store()` app level - // and helper methods. Depends on Sammy.JSON (or json2.js). - Sammy.Storage = function(app) { - this.use(Sammy.JSON); - - this.stores = this.stores || {}; - - // `store()` creates and looks up existing `Sammy.Store` objects - // for the current application. The first time used for a given `'name'` - // initializes a `Sammy.Store` and also creates a helper under the store's - // name. - // - // ### Example - // - // var app = $.sammy(function() { - // this.use(Sammy.Storage); - // - // // initializes the store on app creation. - // this.store('mystore', {type: 'cookie'}); - // - // this.get('#/', function() { - // // returns the Sammy.Store object - // this.store('mystore'); - // // sets 'foo' to 'bar' using the shortcut/helper - // // equivilent to this.store('mystore').set('foo', 'bar'); - // this.mystore('foo', 'bar'); - // // returns 'bar' - // // equivilent to this.store('mystore').get('foo'); - // this.mystore('foo'); - // // returns 'baz!' - // // equivilent to: - // // this.store('mystore').fetch('foo!', function() { - // // return 'baz!'; - // // }) - // this.mystore('foo!', function() { - // return 'baz!'; - // }); - // - // this.clearMystore(); - // // equivilent to: - // // this.store('mystore').clearAll() - // }); - // - // }); - // - // ### Arguments - // - // * `name` The name of the store and helper. the name must be unique per application. - // * `options` A JS object of options that can be passed to the Store constuctor on initialization. - // - this.store = function(name, options) { - // if the store has not been initialized - if (typeof this.stores[name] == 'undefined') { - // create initialize the store - var clear_method_name = "clear" + name.substr(0,1).toUpperCase() + name.substr(1); - this.stores[name] = new Sammy.Store($.extend({ - name: name, - element: this.element_selector - }, options || {})); - // app.name() - this[name] = function(key, value) { - if (typeof value == 'undefined') { - return this.stores[name].get(key); - } else if ($.isFunction(value)) { - return this.stores[name].fetch(key, value); - } else { - return this.stores[name].set(key, value) - } - }; - // app.clearName(); - this[clear_method_name] = function() { - return this.stores[name].clearAll(); - } - // context.name() - this.helper(name, function() { - return this.app[name].apply(this.app, arguments); - }); - // context.clearName(); - this.helper(clear_method_name, function() { - return this.app[clear_method_name](); - }); - } - return this.stores[name]; - }; - - this.helpers({ - store: function() { - return this.app.store.apply(this.app, arguments); - } - }); - }; - - // Sammy.Session is an additional plugin for creating a common 'session' store - // for the given app. It is a very simple wrapper around `Sammy.Storage` - // that provides a simple fallback mechanism for trying to provide the best - // possible storage type for the session. This means, `LocalStorage` - // if available, otherwise `Cookie`, otherwise `Memory`. - // It provides the `session()` helper through `Sammy.Storage#store()`. - // - // See the `Sammy.Storage` plugin for full documentation. - // - Sammy.Session = function(app, options) { - this.use(Sammy.Storage); - // check for local storage, then cookie storage, then just use memory - this.store('session', $.extend({type: ['local', 'cookie', 'memory']}, options)); - }; - - // Sammy.Cache provides helpers for caching data within the lifecycle of a - // Sammy app. The plugin provides two main methods on `Sammy.Application`, - // `cache` and `clearCache`. Each app has its own cache store so that - // you dont have to worry about collisions. As of 0.5 the original Sammy.Cache module - // has been deprecated in favor of this one based on Sammy.Storage. The exposed - // API is almost identical, but Sammy.Storage provides additional backends including - // HTML5 Storage. `Sammy.Cache` will try to use these backends when available - // (in this order) `LocalStorage`, `SessionStorage`, and `Memory` - Sammy.Cache = function(app, options) { - this.use(Sammy.Storage); - // set cache_partials to true - this.cache_partials = true; - // check for local storage, then session storage, then just use memory - this.store('cache', $.extend({type: ['local', 'session', 'memory']}, options)); - }; - -})(jQuery); diff --git a/public/javascripts/plugins/sammy.template.js b/public/javascripts/plugins/sammy.template.js deleted file mode 100644 index 2ce315f..0000000 --- a/public/javascripts/plugins/sammy.template.js +++ /dev/null @@ -1,141 +0,0 @@ -(function($) { - - // Simple JavaScript Templating - // John Resig - http://ejohn.org/ - MIT Licensed - // adapted from: http://ejohn.org/blog/javascript-micro-templating/ - // originally $.srender by Greg Borenstein http://ideasfordozens.com in Feb 2009 - // modified for Sammy by Aaron Quint for caching templates by name - var srender_cache = {}; - var srender = function(name, template, data, options) { - var fn, escaped_string; - // target is an optional element; if provided, the result will be inserted into it - // otherwise the result will simply be returned to the caller - if (srender_cache[name]) { - fn = srender_cache[name]; - } else { - if (typeof template == 'undefined') { - // was a cache check, return false - return false; - } - // If options escape_html is false, dont escape the contents by default - if (options && options.escape_html === false) { - escaped_string = "\",$1,\""; - } else { - escaped_string = "\",h($1),\""; - } - // Generate a reusable function that will serve as a template - // generator (and which will be cached). - fn = srender_cache[name] = new Function("obj", - "var ___$$$___=[],print=function(){___$$$___.push.apply(___$$$___,arguments);};" + - - // Introduce the data as local variables using with(){} - "with(obj){___$$$___.push(\"" + - - // Convert the template into pure JavaScript - String(template) - .replace(/[\r\t\n]/g, " ") - .replace(/\"/g, '\\"') - .split("<%").join("\t") - .replace(/((^|%>)[^\t]*)/g, "$1\r") - .replace(/\t=(.*?)%>/g, escaped_string) - .replace(/\t!(.*?)%>/g, "\",$1,\"") - .split("\t").join("\");") - .split("%>").join("___$$$___.push(\"") - .split("\r").join("") - + "\");}return ___$$$___.join('');"); - } - - if (typeof data != 'undefined') { - return fn(data); - } else { - return fn; - } - }; - - Sammy = Sammy || {}; - - // `Sammy.Template` is a simple plugin that provides a way to create - // and render client side templates. The rendering code is based on John Resig's - // quick templates and Greg Borenstien's srender plugin. - // This is also a great template/boilerplate for Sammy plugins. - // - // Templates use `<% %>` tags to denote embedded javascript. - // - // ### Examples - // - // Here is an example template (user.template): - // - // // user.template - //
      - //
      <%= user.name %>
      - // <% if (user.photo_url) { %> - //
      - // <% } %> - //
      - // - // Given that is a publicly accesible file, you would render it like: - // - // // app.js - // $.sammy(function() { - // // include the plugin - // this.use('Template'); - // - // this.get('#/', function() { - // // the template is rendered in the current context. - // this.user = {name: 'Aaron Quint'}; - // // partial calls template() because of the file extension - // this.partial('user.template'); - // }) - // }); - // - // You can also pass a second argument to use() that will alias the template - // method and therefore allow you to use a different extension for template files - // in partial() - // - // // alias to 'tpl' - // this.use(Sammy.Template, 'tpl'); - // - // // now .tpl files will be run through srender - // this.get('#/', function() { - // this.partial('myfile.tpl'); - // }); - // - // By default, the data passed into the tempalate is passed automatically passed through - // Sammy's `escapeHTML` method in order to prevent possible XSS attacks. This is - // a problem though if you're using something like `Sammy.Form` which renders HTML - // within the templates. You can get around this in two ways. One, you can use the - // `<%! %>` instead of `<%= %>`. Two, you can pass the `escape_html = false` option - // when interpolating, i.e: - // - // this.get('#/', function() { - // this.template('myform.tpl', {form: "
      "}, {escape_html: false}); - // }); - // - Sammy.Template = function(app, method_alias) { - - // *Helper:* Uses simple templating to parse ERB like templates. - // - // ### Arguments - // - // * `template` A String template. '<% %>' tags are evaluated as Javascript and replaced with the elements in data. - // * `data` An Object containing the replacement values for the template. - // data is extended with the EventContext allowing you to call its methods within the template. - // * `name` An optional String name to cache the template. - // - var template = function(template, data, name, options) { - // use name for caching - if (typeof name == 'undefined') { name = template; } - if (typeof options == 'undefined' && typeof name == 'object') { - options = name; name = template; - } - return srender(name, template, $.extend({}, this, data), options); - }; - - // set the default method name/extension - if (!method_alias) { method_alias = 'template'; } - // create the helper at the method alias - app.helper(method_alias, template); - - }; - -})(jQuery); diff --git a/public/javascripts/plugins/sammy.title.js b/public/javascripts/plugins/sammy.title.js deleted file mode 100644 index 9874a1e..0000000 --- a/public/javascripts/plugins/sammy.title.js +++ /dev/null @@ -1,59 +0,0 @@ -(function($) { - - Sammy = Sammy || {}; - - // Sammy.Title is a very simple plugin to easily set the document's title. - // It supplies a helper for setting the title (`title()`) within routes, - // and an app level method for setting the global title (`setTitle()`) - Sammy.Title = function() { - - // setTitle allows setting a global title or a function that modifies the - // title for each route/page. - // - // ### Example - // - // // setting a title prefix - // $.sammy(function() { - // - // this.setTitle('My App -'); - // - // this.get('#/', function() { - // this.title('Home'); // document's title == "My App - Home" - // }); - // }); - // - // // setting a title with a function - // $.sammy(function() { - // - // this.setTitle(function(title) { - // return [title, " /// My App"].join(''); - // }); - // - // this.get('#/', function() { - // this.title('Home'); // document's title == "Home /// My App"; - // }); - // }); - // - this.setTitle = function(title) { - if (!$.isFunction(title)) { - this.title_function = function(additional_title) { - return [title, additional_title].join(' '); - } - } else { - this.title_function = title; - } - }; - - // *Helper* title() sets the document title, passing it through the function - // defined by setTitle() if set. - this.helper('title', function() { - var new_title = $.makeArray(arguments).join(' '); - if (this.app.title_function) { - new_title = this.app.title_function(new_title); - } - document.title = new_title; - }); - - }; - -})(jQuery); diff --git a/public/javascripts/plugins/sammy.tmpl.js b/public/javascripts/plugins/sammy.tmpl.js deleted file mode 100644 index 0e9f0a1..0000000 --- a/public/javascripts/plugins/sammy.tmpl.js +++ /dev/null @@ -1,520 +0,0 @@ -(function( jQuery, undefined ){ - - /* - * a port of the... - * jQuery Templating Plugin - * Copyright 2010, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - */ - - var oldManip = jQuery.fn.domManip, tmplItmAtt = "_tmplitem", htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /, - newTmplItems = {}, wrappedItems = {}, appendToTmplItems, topTmplItem = { key: 0, data: {} }, itemKey = 0, cloneIndex = 0, stack = []; - - function newTmplItem( options, parentItem, fn, data ) { - // Returns a template item data structure for a new rendered instance of a template (a 'template item'). - // The content field is a hierarchical array of strings and nested items (to be - // removed and replaced by nodes field of dom elements, once inserted in DOM). - var newItem = { - data: data || (parentItem ? parentItem.data : {}), - _wrap: parentItem ? parentItem._wrap : null, - tmpl: null, - parent: parentItem || null, - nodes: [], - calls: tiCalls, - nest: tiNest, - wrap: tiWrap, - html: tiHtml, - update: tiUpdate - }; - if ( options ) { - jQuery.extend( newItem, options, { nodes: [], parent: parentItem } ); - } - if ( fn ) { - // Build the hierarchical content to be used during insertion into DOM - newItem.tmpl = fn; - newItem._ctnt = newItem._ctnt || newItem.tmpl( jQuery, newItem ); - newItem.key = ++itemKey; - // Keep track of new template item, until it is stored as jQuery Data on DOM element - (stack.length ? wrappedItems : newTmplItems)[itemKey] = newItem; - } - return newItem; - } - - // Override appendTo etc., in order to provide support for targeting multiple elements. (This code would disappear if integrated in jquery core). - jQuery.each({ - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" - }, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var ret = [], insert = jQuery( selector ), elems, i, l, tmplItems, - parent = this.length === 1 && this[0].parentNode; - - appendToTmplItems = newTmplItems || {}; - if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { - insert[ original ]( this[0] ); - ret = this; - } else { - for ( i = 0, l = insert.length; i < l; i++ ) { - cloneIndex = i; - elems = (i > 0 ? this.clone(true) : this).get(); - jQuery.fn[ original ].apply( jQuery(insert[i]), elems ); - ret = ret.concat( elems ); - } - cloneIndex = 0; - ret = this.pushStack( ret, name, insert.selector ); - } - tmplItems = appendToTmplItems; - appendToTmplItems = null; - jQuery.tmpl.complete( tmplItems ); - return ret; - }; - }); - - jQuery.fn.extend({ - // Use first wrapped element as template markup. - // Return wrapped set of template items, obtained by rendering template against data. - tmpl: function( data, options, parentItem ) { - return jQuery.tmpl( this[0], data, options, parentItem ); - }, - - // Find which rendered template item the first wrapped DOM element belongs to - tmplItem: function() { - return jQuery.tmplItem( this[0] ); - }, - - // Consider the first wrapped element as a template declaration, and get the compiled template or store it as a named template. - template: function( name ) { - return jQuery.template( name, this[0] ); - }, - - domManip: function( args, table, callback, options ) { - // This appears to be a bug in the appendTo, etc. implementation - // it should be doing .call() instead of .apply(). See #6227 - if ( args[0] && args[0].nodeType ) { - var dmArgs = jQuery.makeArray( arguments ), argsLength = args.length, i = 0, tmplItem; - while ( i < argsLength && !(tmplItem = jQuery.data( args[i++], "tmplItem" ))) {} - if ( argsLength > 1 ) { - dmArgs[0] = [jQuery.makeArray( args )]; - } - if ( tmplItem && cloneIndex ) { - dmArgs[2] = function( fragClone ) { - // Handler called by oldManip when rendered template has been inserted into DOM. - jQuery.tmpl.afterManip( this, fragClone, callback ); - }; - } - oldManip.apply( this, dmArgs ); - } else { - oldManip.apply( this, arguments ); - } - cloneIndex = 0; - if ( !appendToTmplItems ) { - jQuery.tmpl.complete( newTmplItems ); - } - return this; - } - }); - - jQuery.extend({ - // Return wrapped set of template items, obtained by rendering template against data. - tmpl: function( tmpl, data, options, parentItem ) { - var ret, topLevel = !parentItem; - if ( topLevel ) { - // This is a top-level tmpl call (not from a nested template using {{tmpl}}) - parentItem = topTmplItem; - tmpl = jQuery.template[tmpl] || jQuery.template( null, tmpl ); - wrappedItems = {}; // Any wrapped items will be rebuilt, since this is top level - } else if ( !tmpl ) { - // The template item is already associated with DOM - this is a refresh. - // Re-evaluate rendered template for the parentItem - tmpl = parentItem.tmpl; - newTmplItems[parentItem.key] = parentItem; - parentItem.nodes = []; - if ( parentItem.wrapped ) { - updateWrapped( parentItem, parentItem.wrapped ); - } - // Rebuild, without creating a new template item - return jQuery( build( parentItem, null, parentItem.tmpl( jQuery, parentItem ) )); - } - if ( !tmpl ) { - return []; // Could throw... - } - if ( typeof data === "function" ) { - data = data.call( parentItem || {} ); - } - if ( options && options.wrapped ) { - updateWrapped( options, options.wrapped ); - } - ret = jQuery.isArray( data ) ? - jQuery.map( data, function( dataItem ) { - return dataItem ? newTmplItem( options, parentItem, tmpl, dataItem ) : null; - }) : - [ newTmplItem( options, parentItem, tmpl, data ) ]; - return topLevel ? jQuery( build( parentItem, null, ret ) ) : ret; - }, - - // Return rendered template item for an element. - tmplItem: function( elem ) { - var tmplItem; - if ( elem instanceof jQuery ) { - elem = elem[0]; - } - while ( elem && elem.nodeType === 1 && !(tmplItem = jQuery.data( elem, "tmplItem" )) && (elem = elem.parentNode) ) {} - return tmplItem || topTmplItem; - }, - - // Set: - // Use $.template( name, tmpl ) to cache a named template, - // where tmpl is a template string, a script element or a jQuery instance wrapping a script element, etc. - // Use $( "selector" ).template( name ) to provide access by name to a script block template declaration. - - // Get: - // Use $.template( name ) to access a cached template. - // Also $( selectorToScriptBlock ).template(), or $.template( null, templateString ) - // will return the compiled template, without adding a name reference. - // If templateString includes at least one HTML tag, $.template( templateString ) is equivalent - // to $.template( null, templateString ) - template: function( name, tmpl ) { - if (tmpl) { - // Compile template and associate with name - if ( typeof tmpl === "string" ) { - // This is an HTML string being passed directly in. - tmpl = buildTmplFn( tmpl ) - } else if ( tmpl instanceof jQuery ) { - tmpl = tmpl[0] || {}; - } - if ( tmpl.nodeType ) { - // If this is a template block, use cached copy, or generate tmpl function and cache. - tmpl = jQuery.data( tmpl, "tmpl" ) || jQuery.data( tmpl, "tmpl", buildTmplFn( tmpl.innerHTML )); - } - return typeof name === "string" ? (jQuery.template[name] = tmpl) : tmpl; - } - // Return named compiled template - return name ? (typeof name !== "string" ? jQuery.template( null, name ): - (jQuery.template[name] || - // If not in map, treat as a selector. (If integrated with core, use quickExpr.exec) - jQuery.template( null, htmlExpr.test( name ) ? name : jQuery( name )))) : null; - }, - - encode: function( text ) { - // Do HTML encoding replacing < > & and ' and " by corresponding entities. - return ("" + text).split("<").join("<").split(">").join(">").split('"').join(""").split("'").join("'"); - } - }); - - jQuery.extend( jQuery.tmpl, { - tag: { - "tmpl": { - _default: { $2: "null" }, - open: "if($notnull_1){___$$$___=___$$$___.concat($item.nest($1,$2));}" - // tmpl target parameter can be of type function, so use $1, not $1a (so not auto detection of functions) - // This means that {{tmpl foo}} treats foo as a template (which IS a function). - // Explicit parens can be used if foo is a function that returns a template: {{tmpl foo()}}. - }, - "wrap": { - _default: { $2: "null" }, - open: "$item.calls(_,$1,$2);_=[];", - close: "call=$item.calls();___$$$___=call.___$$$___.concat($item.wrap(call,___$$$___));" - }, - "each": { - _default: { $2: "$index, $value" }, - open: "if($notnull_1){$.each($1a,function($2){with(this){", - close: "}});}" - }, - "if": { - open: "if(($notnull_1) && $1a){", - close: "}" - }, - "else": { - _default: { $1: "true" }, - open: "}else if(($notnull_1) && $1a){" - }, - "html": { - // Unecoded expression evaluation. - open: "if($notnull_1){___$$$___.push($1a);}" - }, - "=": { - // Encoded expression evaluation. Abbreviated form is ${}. - _default: { $1: "$data" }, - open: "if($notnull_1){___$$$___.push($.encode($1a));}" - }, - "!": { - // Comment tag. Skipped by parser - open: "" - } - }, - - // This stub can be overridden, e.g. in jquery.tmplPlus for providing rendered events - complete: function( items ) { - newTmplItems = {}; - }, - - // Call this from code which overrides domManip, or equivalent - // Manage cloning/storing template items etc. - afterManip: function afterManip( elem, fragClone, callback ) { - // Provides cloned fragment ready for fixup prior to and after insertion into DOM - var content = fragClone.nodeType === 11 ? - jQuery.makeArray(fragClone.childNodes) : - fragClone.nodeType === 1 ? [fragClone] : []; - - // Return fragment to original caller (e.g. append) for DOM insertion - callback.call( elem, fragClone ); - - // Fragment has been inserted:- Add inserted nodes to tmplItem data structure. Replace inserted element annotations by jQuery.data. - storeTmplItems( content ); - cloneIndex++; - } - }); - - //========================== Private helper functions, used by code above ========================== - - function build( tmplItem, nested, content ) { - // Convert hierarchical content into flat string array - // and finally return array of fragments ready for DOM insertion - var frag, ret = content ? jQuery.map( content, function( item ) { - return (typeof item === "string") ? - // Insert template item annotations, to be converted to jQuery.data( "tmplItem" ) when elems are inserted into DOM. - (tmplItem.key ? item.replace( /(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g, "$1 " + tmplItmAtt + "=\"" + tmplItem.key + "\" $2" ) : item) : - // This is a child template item. Build nested template. - build( item, tmplItem, item._ctnt ); - }) : - // If content is not defined, insert tmplItem directly. Not a template item. May be a string, or a string array, e.g. from {{html $item.html()}}. - tmplItem; - if ( nested ) { - return ret; - } - - // top-level template - ret = ret.join(""); - - // Support templates which have initial or final text nodes, or consist only of text - // Also support HTML entities within the HTML markup. - ret.replace( /^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/, function( all, before, middle, after) { - frag = jQuery( middle ).get(); - - storeTmplItems( frag ); - if ( before ) { - frag = unencode( before ).concat(frag); - } - if ( after ) { - frag = frag.concat(unencode( after )); - } - }); - return frag ? frag : unencode( ret ); - } - - function unencode( text ) { - // Use createElement, since createTextNode will not render HTML entities correctly - var el = document.createElement( "div" ); - el.innerHTML = text; - return jQuery.makeArray(el.childNodes); - } - - // Generate a reusable function that will serve to render a template against data - function buildTmplFn( markup ) { - return new Function("jQuery","$item", - "var $=jQuery,call,___$$$___=[],$data=$item.data;" + - - // Introduce the data as local variables using with(){} - "with($data){___$$$___.push('" + - - // Convert the template into pure JavaScript - jQuery.trim(markup) - .replace( /([\\'])/g, "\\$1" ) - .replace( /[\r\t\n]/g, " " ) - .replace( /\$\{([^\}]*)\}/g, "{{= $1}}" ) - .replace( /\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g, - function( all, slash, type, fnargs, target, parens, args ) { - var tag = jQuery.tmpl.tag[ type ], def, expr, exprAutoFnDetect; - if ( !tag ) { - throw "Template command not found: " + type; - } - def = tag._default || []; - if ( parens && !/\w$/.test(target)) { - target += parens; - parens = ""; - } - if ( target ) { - target = unescape( target ); - args = args ? ("," + unescape( args ) + ")") : (parens ? ")" : ""); - // Support for target being things like a.toLowerCase(); - // In that case don't call with template item as 'this' pointer. Just evaluate... - expr = parens ? (target.indexOf(".") > -1 ? target + parens : ("(" + target + ").call($item" + args)) : target; - exprAutoFnDetect = parens ? expr : "(typeof(" + target + ")==='function'?(" + target + ").call($item):(" + target + "))"; - } else { - exprAutoFnDetect = expr = def.$1 || "null"; - } - fnargs = unescape( fnargs ); - return "');" + - tag[ slash ? "close" : "open" ] - .split( "$notnull_1" ).join( target ? "typeof(" + target + ")!=='undefined' && (" + target + ")!=null" : "true" ) - .split( "$1a" ).join( exprAutoFnDetect ) - .split( "$1" ).join( expr ) - .split( "$2" ).join( fnargs ? - fnargs.replace( /\s*([^\(]+)\s*(\((.*?)\))?/g, function( all, name, parens, params ) { - params = params ? ("," + params + ")") : (parens ? ")" : ""); - return params ? ("(" + name + ").call($item" + params) : all; - }) - : (def.$2||"") - ) + - "___$$$___.push('"; - }) + - "');}return ___$$$___;" - ); - } - function updateWrapped( options, wrapped ) { - // Build the wrapped content. - options._wrap = build( options, true, - // Suport imperative scenario in which options.wrapped can be set to a selector or an HTML string. - jQuery.isArray( wrapped ) ? wrapped : [htmlExpr.test( wrapped ) ? wrapped : jQuery( wrapped ).html()] - ).join(""); - } - - function unescape( args ) { - return args ? args.replace( /\\'/g, "'").replace(/\\\\/g, "\\" ) : null; - } - function outerHtml( elem ) { - var div = document.createElement("div"); - div.appendChild( elem.cloneNode(true) ); - return div.innerHTML; - } - - // Store template items in jQuery.data(), ensuring a unique tmplItem data data structure for each rendered template instance. - function storeTmplItems( content ) { - var keySuffix = "_" + cloneIndex, elem, elems, newClonedItems = {}, i, l, m; - for ( i = 0, l = content.length; i < l; i++ ) { - if ( (elem = content[i]).nodeType !== 1 ) { - continue; - } - elems = elem.getElementsByTagName("*"); - for ( m = elems.length - 1; m >= 0; m-- ) { - processItemKey( elems[m] ); - } - processItemKey( elem ); - } - function processItemKey( el ) { - var pntKey, pntNode = el, pntItem, tmplItem, key; - // Ensure that each rendered template inserted into the DOM has its own template item, - if ( (key = el.getAttribute( tmplItmAtt ))) { - while ( pntNode.parentNode && (pntNode = pntNode.parentNode).nodeType === 1 && !(pntKey = pntNode.getAttribute( tmplItmAtt ))) { } - if ( pntKey !== key ) { - // The next ancestor with a _tmplitem expando is on a different key than this one. - // So this is a top-level element within this template item - // Set pntNode to the key of the parentNode, or to 0 if pntNode.parentNode is null, or pntNode is a fragment. - pntNode = pntNode.parentNode ? (pntNode.nodeType === 11 ? 0 : (pntNode.getAttribute( tmplItmAtt ) || 0)) : 0; - if ( !(tmplItem = newTmplItems[key]) ) { - // The item is for wrapped content, and was copied from the temporary parent wrappedItem. - tmplItem = wrappedItems[key]; - tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode], null, true ); - tmplItem.key = ++itemKey; - newTmplItems[itemKey] = tmplItem; - } - if ( cloneIndex ) { - cloneTmplItem( key ); - } - } - el.removeAttribute( tmplItmAtt ); - } else if ( cloneIndex && (tmplItem = jQuery.data( el, "tmplItem" )) ) { - // This was a rendered element, cloned during append or appendTo etc. - // TmplItem stored in jQuery data has already been cloned in cloneCopyEvent. We must replace it with a fresh cloned tmplItem. - cloneTmplItem( tmplItem.key ); - newTmplItems[tmplItem.key] = tmplItem; - pntNode = jQuery.data( el.parentNode, "tmplItem" ); - pntNode = pntNode ? pntNode.key : 0; - } - if ( tmplItem ) { - pntItem = tmplItem; - // Find the template item of the parent element. - // (Using !=, not !==, since pntItem.key is number, and pntNode may be a string) - while ( pntItem && pntItem.key != pntNode ) { - // Add this element as a top-level node for this rendered template item, as well as for any - // ancestor items between this item and the item of its parent element - pntItem.nodes.push( el ); - pntItem = pntItem.parent; - } - // Delete content built during rendering - reduce API surface area and memory use, and avoid exposing of stale data after rendering... - delete tmplItem._ctnt; - delete tmplItem._wrap; - // Store template item as jQuery data on the element - jQuery.data( el, "tmplItem", tmplItem ); - } - function cloneTmplItem( key ) { - key = key + keySuffix; - tmplItem = newClonedItems[key] = - (newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent, null, true )); - } - } - } - - //---- Helper functions for template item ---- - - function tiCalls( content, tmpl, data, options ) { - if ( !content ) { - return stack.pop(); - } - stack.push({ _: content, tmpl: tmpl, item:this, data: data, options: options }); - } - - function tiNest( tmpl, data, options ) { - // nested template, using {{tmpl}} tag - return jQuery.tmpl( jQuery.template( tmpl ), data, options, this ); - } - - function tiWrap( call, wrapped ) { - // nested template, using {{wrap}} tag - var options = call.options || {}; - options.wrapped = wrapped; - // Apply the template, which may incorporate wrapped content, - return jQuery.tmpl( jQuery.template( call.tmpl ), call.data, options, call.item ); - } - - function tiHtml( filter, textOnly ) { - var wrapped = this._wrap; - return jQuery.map( - jQuery( jQuery.isArray( wrapped ) ? wrapped.join("") : wrapped ).filter( filter || "*" ), - function(e) { - return textOnly ? - e.innerText || e.textContent : - e.outerHTML || outerHtml(e); - }); - } - - function tiUpdate() { - var coll = this.nodes; - jQuery.tmpl( null, null, null, this).insertBefore( coll[0] ); - jQuery( coll ).remove(); - } - - Sammy = Sammy || {}; - - Sammy.Tmpl = function(app, method_alias) { - - // *Helper:* Uses jQuery-tmpl to parse a template and interpolate and work with the passed data - // - // ### Arguments - // - // * `template` A String template. '${ }' tags are evaluated as Javascript and replaced with the elements in data. - // * `data` An Object containing the replacement values for the template. - // data is extended with the EventContext allowing you to call its methods within the template. - // * `name` An optional String name to cache the template. - // - var template = function(template, data, name) { - // use name for caching - if (typeof name == 'undefined') name = template; - - // check the cache - if (!jQuery.template[name]) jQuery.template(name, template); - - // we could also pass along jQuery-tmpl options as a last param? - return jQuery.tmpl(name, jQuery.extend({}, this, data)); - }; - - // set the default method name/extension - if (!method_alias) method_alias = 'tmpl'; - // create the helper at the method alias - app.helper(method_alias, template); - - }; -})( jQuery ); diff --git a/public/javascripts/sammy.js b/public/javascripts/sammy.js deleted file mode 100644 index d798892..0000000 --- a/public/javascripts/sammy.js +++ /dev/null @@ -1,1829 +0,0 @@ -// name: sammy -// version: 0.6.3 - -// Sammy.js / http://sammyjs.org - -(function($, window) { - - var Sammy, - PATH_REPLACER = "([^\/]+)", - PATH_NAME_MATCHER = /:([\w\d]+)/g, - QUERY_STRING_MATCHER = /\?([^#]*)$/, - // mainly for making `arguments` an Array - _makeArray = function(nonarray) { return Array.prototype.slice.call(nonarray); }, - // borrowed from jQuery - _isFunction = function( obj ) { return Object.prototype.toString.call(obj) === "[object Function]"; }, - _isArray = function( obj ) { return Object.prototype.toString.call(obj) === "[object Array]"; }, - _decode = function( str ) { return decodeURIComponent(str.replace(/\+/g, ' ')); }, - _encode = encodeURIComponent, - _escapeHTML = function(s) { - return String(s).replace(/&(?!\w+;)/g, '&').replace(//g, '>').replace(/"/g, '"'); - }, - _routeWrapper = function(verb) { - return function(path, callback) { return this.route.apply(this, [verb, path, callback]); }; - }, - _template_cache = {}, - loggers = []; - - - // `Sammy` (also aliased as $.sammy) is not only the namespace for a - // number of prototypes, its also a top level method that allows for easy - // creation/management of `Sammy.Application` instances. There are a - // number of different forms for `Sammy()` but each returns an instance - // of `Sammy.Application`. When a new instance is created using - // `Sammy` it is added to an Object called `Sammy.apps`. This - // provides for an easy way to get at existing Sammy applications. Only one - // instance is allowed per `element_selector` so when calling - // `Sammy('selector')` multiple times, the first time will create - // the application and the following times will extend the application - // already added to that selector. - // - // ### Example - // - // // returns the app at #main or a new app - // Sammy('#main') - // - // // equivilent to "new Sammy.Application", except appends to apps - // Sammy(); - // Sammy(function() { ... }); - // - // // extends the app at '#main' with function. - // Sammy('#main', function() { ... }); - // - Sammy = function() { - var args = _makeArray(arguments), - app, selector; - Sammy.apps = Sammy.apps || {}; - if (args.length === 0 || args[0] && _isFunction(args[0])) { // Sammy() - return Sammy.apply(Sammy, ['body'].concat(args)); - } else if (typeof (selector = args.shift()) == 'string') { // Sammy('#main') - app = Sammy.apps[selector] || new Sammy.Application(); - app.element_selector = selector; - if (args.length > 0) { - $.each(args, function(i, plugin) { - app.use(plugin); - }); - } - // if the selector changes make sure the refrence in Sammy.apps changes - if (app.element_selector != selector) { - delete Sammy.apps[selector]; - } - Sammy.apps[app.element_selector] = app; - return app; - } - }; - - Sammy.VERSION = '0.6.3'; - - // Add to the global logger pool. Takes a function that accepts an - // unknown number of arguments and should print them or send them somewhere - // The first argument is always a timestamp. - Sammy.addLogger = function(logger) { - loggers.push(logger); - }; - - // Sends a log message to each logger listed in the global - // loggers pool. Can take any number of arguments. - // Also prefixes the arguments with a timestamp. - Sammy.log = function() { - var args = _makeArray(arguments); - args.unshift("[" + Date() + "]"); - $.each(loggers, function(i, logger) { - logger.apply(Sammy, args); - }); - }; - - if (typeof window.console != 'undefined') { - if (_isFunction(window.console.log.apply)) { - Sammy.addLogger(function() { - window.console.log.apply(window.console, arguments); - }); - } else { - Sammy.addLogger(function() { - window.console.log(arguments); - }); - } - } else if (typeof console != 'undefined') { - Sammy.addLogger(function() { - console.log.apply(console, arguments); - }); - } - - $.extend(Sammy, { - makeArray: _makeArray, - isFunction: _isFunction, - isArray: _isArray - }) - - // Sammy.Object is the base for all other Sammy classes. It provides some useful - // functionality, including cloning, iterating, etc. - Sammy.Object = function(obj) { // constructor - return $.extend(this, obj || {}); - }; - - $.extend(Sammy.Object.prototype, { - - // Escape HTML in string, use in templates to prevent script injection. - // Also aliased as `h()` - escapeHTML: _escapeHTML, - h: _escapeHTML, - - // Returns a copy of the object with Functions removed. - toHash: function() { - var json = {}; - $.each(this, function(k,v) { - if (!_isFunction(v)) { - json[k] = v; - } - }); - return json; - }, - - // Renders a simple HTML version of this Objects attributes. - // Does not render functions. - // For example. Given this Sammy.Object: - // - // var s = new Sammy.Object({first_name: 'Sammy', last_name: 'Davis Jr.'}); - // s.toHTML() - // //=> 'first_name Sammy
      last_name Davis Jr.
      ' - // - toHTML: function() { - var display = ""; - $.each(this, function(k, v) { - if (!_isFunction(v)) { - display += "" + k + " " + v + "
      "; - } - }); - return display; - }, - - // Returns an array of keys for this object. If `attributes_only` - // is true will not return keys that map to a `function()` - keys: function(attributes_only) { - var keys = []; - for (var property in this) { - if (!_isFunction(this[property]) || !attributes_only) { - keys.push(property); - } - } - return keys; - }, - - // Checks if the object has a value at `key` and that the value is not empty - has: function(key) { - return this[key] && $.trim(this[key].toString()) != ''; - }, - - // convenience method to join as many arguments as you want - // by the first argument - useful for making paths - join: function() { - var args = _makeArray(arguments); - var delimiter = args.shift(); - return args.join(delimiter); - }, - - // Shortcut to Sammy.log - log: function() { - Sammy.log.apply(Sammy, arguments); - }, - - // Returns a string representation of this object. - // if `include_functions` is true, it will also toString() the - // methods of this object. By default only prints the attributes. - toString: function(include_functions) { - var s = []; - $.each(this, function(k, v) { - if (!_isFunction(v) || include_functions) { - s.push('"' + k + '": ' + v.toString()); - } - }); - return "Sammy.Object: {" + s.join(',') + "}"; - } - }); - - // The HashLocationProxy is the default location proxy for all Sammy applications. - // A location proxy is a prototype that conforms to a simple interface. The purpose - // of a location proxy is to notify the Sammy.Application its bound to when the location - // or 'external state' changes. The HashLocationProxy considers the state to be - // changed when the 'hash' (window.location.hash / '#') changes. It does this in two - // different ways depending on what browser you are using. The newest browsers - // (IE, Safari > 4, FF >= 3.6) support a 'onhashchange' DOM event, thats fired whenever - // the location.hash changes. In this situation the HashLocationProxy just binds - // to this event and delegates it to the application. In the case of older browsers - // a poller is set up to track changes to the hash. Unlike Sammy 0.3 or earlier, - // the HashLocationProxy allows the poller to be a global object, eliminating the - // need for multiple pollers even when thier are multiple apps on the page. - Sammy.HashLocationProxy = function(app, run_interval_every) { - this.app = app; - // set is native to false and start the poller immediately - this.is_native = false; - this._startPolling(run_interval_every); - }; - - Sammy.HashLocationProxy.prototype = { - - // bind the proxy events to the current app. - bind: function() { - var proxy = this, app = this.app; - $(window).bind('hashchange.' + this.app.eventNamespace(), function(e, non_native) { - // if we receive a native hash change event, set the proxy accordingly - // and stop polling - if (proxy.is_native === false && !non_native) { - Sammy.log('native hash change exists, using'); - proxy.is_native = true; - window.clearInterval(Sammy.HashLocationProxy._interval); - } - app.trigger('location-changed'); - }); - if (!Sammy.HashLocationProxy._bindings) { - Sammy.HashLocationProxy._bindings = 0; - } - Sammy.HashLocationProxy._bindings++; - }, - - // unbind the proxy events from the current app - unbind: function() { - $(window).unbind('hashchange.' + this.app.eventNamespace()); - Sammy.HashLocationProxy._bindings--; - if (Sammy.HashLocationProxy._bindings <= 0) { - window.clearInterval(Sammy.HashLocationProxy._interval); - } - }, - - // get the current location from the hash. - getLocation: function() { - // Bypass the `window.location.hash` attribute. If a question mark - // appears in the hash IE6 will strip it and all of the following - // characters from `window.location.hash`. - var matches = window.location.toString().match(/^[^#]*(#.+)$/); - return matches ? matches[1] : ''; - }, - - // set the current location to `new_location` - setLocation: function(new_location) { - return (window.location = new_location); - }, - - _startPolling: function(every) { - // set up interval - var proxy = this; - if (!Sammy.HashLocationProxy._interval) { - if (!every) { every = 10; } - var hashCheck = function() { - var current_location = proxy.getLocation(); - if (!Sammy.HashLocationProxy._last_location || - current_location != Sammy.HashLocationProxy._last_location) { - window.setTimeout(function() { - $(window).trigger('hashchange', [true]); - }, 13); - } - Sammy.HashLocationProxy._last_location = current_location; - }; - hashCheck(); - Sammy.HashLocationProxy._interval = window.setInterval(hashCheck, every); - } - } - }; - - - // Sammy.Application is the Base prototype for defining 'applications'. - // An 'application' is a collection of 'routes' and bound events that is - // attached to an element when `run()` is called. - // The only argument an 'app_function' is evaluated within the context of the application. - Sammy.Application = function(app_function) { - var app = this; - this.routes = {}; - this.listeners = new Sammy.Object({}); - this.arounds = []; - this.befores = []; - // generate a unique namespace - this.namespace = (new Date()).getTime() + '-' + parseInt(Math.random() * 1000, 10); - this.context_prototype = function() { Sammy.EventContext.apply(this, arguments); }; - this.context_prototype.prototype = new Sammy.EventContext(); - - if (_isFunction(app_function)) { - app_function.apply(this, [this]); - } - // set the location proxy if not defined to the default (HashLocationProxy) - if (!this._location_proxy) { - this.setLocationProxy(new Sammy.HashLocationProxy(this, this.run_interval_every)); - } - if (this.debug) { - this.bindToAllEvents(function(e, data) { - app.log(app.toString(), e.cleaned_type, data || {}); - }); - } - }; - - Sammy.Application.prototype = $.extend({}, Sammy.Object.prototype, { - - // the four route verbs - ROUTE_VERBS: ['get','post','put','delete'], - - // An array of the default events triggered by the - // application during its lifecycle - APP_EVENTS: ['run', 'unload', 'lookup-route', 'run-route', 'route-found', 'event-context-before', 'event-context-after', 'changed', 'error', 'check-form-submission', 'redirect', 'location-changed'], - - _last_route: null, - _location_proxy: null, - _running: false, - - // Defines what element the application is bound to. Provide a selector - // (parseable by `jQuery()`) and this will be used by `$element()` - element_selector: 'body', - - // When set to true, logs all of the default events using `log()` - debug: false, - - // When set to true, and the error() handler is not overriden, will actually - // raise JS errors in routes (500) and when routes can't be found (404) - raise_errors: false, - - // The time in milliseconds that the URL is queried for changes - run_interval_every: 50, - - // The default template engine to use when using `partial()` in an - // `EventContext`. `template_engine` can either be a string that - // corresponds to the name of a method/helper on EventContext or it can be a function - // that takes two arguments, the content of the unrendered partial and an optional - // JS object that contains interpolation data. Template engine is only called/refered - // to if the extension of the partial is null or unknown. See `partial()` - // for more information - template_engine: null, - - // //=> Sammy.Application: body - toString: function() { - return 'Sammy.Application:' + this.element_selector; - }, - - // returns a jQuery object of the Applications bound element. - $element: function(selector) { - return selector ? $(this.element_selector).find(selector) : $(this.element_selector); - }, - - // `use()` is the entry point for including Sammy plugins. - // The first argument to use should be a function() that is evaluated - // in the context of the current application, just like the `app_function` - // argument to the `Sammy.Application` constructor. - // - // Any additional arguments are passed to the app function sequentially. - // - // For much more detail about plugins, check out: - // [http://sammyjs.org/docs/plugins](http://sammyjs.org/docs/plugins) - // - // ### Example - // - // var MyPlugin = function(app, prepend) { - // - // this.helpers({ - // myhelper: function(text) { - // alert(prepend + " " + text); - // } - // }); - // - // }; - // - // var app = $.sammy(function() { - // - // this.use(MyPlugin, 'This is my plugin'); - // - // this.get('#/', function() { - // this.myhelper('and dont you forget it!'); - // //=> Alerts: This is my plugin and dont you forget it! - // }); - // - // }); - // - // If plugin is passed as a string it assumes your are trying to load - // Sammy."Plugin". This is the prefered way of loading core Sammy plugins - // as it allows for better error-messaging. - // - // ### Example - // - // $.sammy(function() { - // this.use('Mustache'); //=> Sammy.Mustache - // this.use('Storage'); //=> Sammy.Storage - // }); - // - use: function() { - // flatten the arguments - var args = _makeArray(arguments), - plugin = args.shift(), - plugin_name = plugin || ''; - try { - args.unshift(this); - if (typeof plugin == 'string') { - plugin_name = 'Sammy.' + plugin; - plugin = Sammy[plugin]; - } - plugin.apply(this, args); - } catch(e) { - if (typeof plugin === 'undefined') { - this.error("Plugin Error: called use() but plugin (" + plugin_name.toString() + ") is not defined", e); - } else if (!_isFunction(plugin)) { - this.error("Plugin Error: called use() but '" + plugin_name.toString() + "' is not a function", e); - } else { - this.error("Plugin Error", e); - } - } - return this; - }, - - // Sets the location proxy for the current app. By default this is set to - // a new `Sammy.HashLocationProxy` on initialization. However, you can set - // the location_proxy inside you're app function to give your app a custom - // location mechanism. See `Sammy.HashLocationProxy` and `Sammy.DataLocationProxy` - // for examples. - // - // `setLocationProxy()` takes an initialized location proxy. - // - // ### Example - // - // // to bind to data instead of the default hash; - // var app = $.sammy(function() { - // this.setLocationProxy(new Sammy.DataLocationProxy(this)); - // }); - // - setLocationProxy: function(new_proxy) { - var original_proxy = this._location_proxy; - this._location_proxy = new_proxy; - if (this.isRunning()) { - if (original_proxy) { - // if there is already a location proxy, unbind it. - original_proxy.unbind(); - } - this._location_proxy.bind(); - } - }, - - // `route()` is the main method for defining routes within an application. - // For great detail on routes, check out: - // [http://sammyjs.org/docs/routes](http://sammyjs.org/docs/routes) - // - // This method also has aliases for each of the different verbs (eg. `get()`, `post()`, etc.) - // - // ### Arguments - // - // * `verb` A String in the set of ROUTE_VERBS or 'any'. 'any' will add routes for each - // of the ROUTE_VERBS. If only two arguments are passed, - // the first argument is the path, the second is the callback and the verb - // is assumed to be 'any'. - // * `path` A Regexp or a String representing the path to match to invoke this verb. - // * `callback` A Function that is called/evaluated whent the route is run see: `runRoute()`. - // It is also possible to pass a string as the callback, which is looked up as the name - // of a method on the application. - // - route: function(verb, path, callback) { - var app = this, param_names = [], add_route, path_match; - - // if the method signature is just (path, callback) - // assume the verb is 'any' - if (!callback && _isFunction(path)) { - path = verb; - callback = path; - verb = 'any'; - } - - verb = verb.toLowerCase(); // ensure verb is lower case - - // if path is a string turn it into a regex - if (path.constructor == String) { - - // Needs to be explicitly set because IE will maintain the index unless NULL is returned, - // which means that with two consecutive routes that contain params, the second set of params will not be found and end up in splat instead of params - // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/lastIndex - PATH_NAME_MATCHER.lastIndex = 0; - - // find the names - while ((path_match = PATH_NAME_MATCHER.exec(path)) !== null) { - param_names.push(path_match[1]); - } - // replace with the path replacement - path = new RegExp("^" + path.replace(PATH_NAME_MATCHER, PATH_REPLACER) + "$"); - } - // lookup callback - if (typeof callback == 'string') { - callback = app[callback]; - } - - add_route = function(with_verb) { - var r = {verb: with_verb, path: path, callback: callback, param_names: param_names}; - // add route to routes array - app.routes[with_verb] = app.routes[with_verb] || []; - // place routes in order of definition - app.routes[with_verb].push(r); - }; - - if (verb === 'any') { - $.each(this.ROUTE_VERBS, function(i, v) { add_route(v); }); - } else { - add_route(verb); - } - - // return the app - return this; - }, - - // Alias for route('get', ...) - get: _routeWrapper('get'), - - // Alias for route('post', ...) - post: _routeWrapper('post'), - - // Alias for route('put', ...) - put: _routeWrapper('put'), - - // Alias for route('delete', ...) - del: _routeWrapper('delete'), - - // Alias for route('any', ...) - any: _routeWrapper('any'), - - // `mapRoutes` takes an array of arrays, each array being passed to route() - // as arguments, this allows for mass definition of routes. Another benefit is - // this makes it possible/easier to load routes via remote JSON. - // - // ### Example - // - // var app = $.sammy(function() { - // - // this.mapRoutes([ - // ['get', '#/', function() { this.log('index'); }], - // // strings in callbacks are looked up as methods on the app - // ['post', '#/create', 'addUser'], - // // No verb assumes 'any' as the verb - // [/dowhatever/, function() { this.log(this.verb, this.path)}]; - // ]); - // }); - // - mapRoutes: function(route_array) { - var app = this; - $.each(route_array, function(i, route_args) { - app.route.apply(app, route_args); - }); - return this; - }, - - // A unique event namespace defined per application. - // All events bound with `bind()` are automatically bound within this space. - eventNamespace: function() { - return ['sammy-app', this.namespace].join('-'); - }, - - // Works just like `jQuery.fn.bind()` with a couple noteable differences. - // - // * It binds all events to the application element - // * All events are bound within the `eventNamespace()` - // * Events are not actually bound until the application is started with `run()` - // * callbacks are evaluated within the context of a Sammy.EventContext - // - bind: function(name, data, callback) { - var app = this; - // build the callback - // if the arity is 2, callback is the second argument - if (typeof callback == 'undefined') { callback = data; } - var listener_callback = function() { - // pull off the context from the arguments to the callback - var e, context, data; - e = arguments[0]; - data = arguments[1]; - if (data && data.context) { - context = data.context; - delete data.context; - } else { - context = new app.context_prototype(app, 'bind', e.type, data, e.target); - } - e.cleaned_type = e.type.replace(app.eventNamespace(), ''); - callback.apply(context, [e, data]); - }; - - // it could be that the app element doesnt exist yet - // so attach to the listeners array and then run() - // will actually bind the event. - if (!this.listeners[name]) { this.listeners[name] = []; } - this.listeners[name].push(listener_callback); - if (this.isRunning()) { - // if the app is running - // *actually* bind the event to the app element - this._listen(name, listener_callback); - } - return this; - }, - - // Triggers custom events defined with `bind()` - // - // ### Arguments - // - // * `name` The name of the event. Automatically prefixed with the `eventNamespace()` - // * `data` An optional Object that can be passed to the bound callback. - // * `context` An optional context/Object in which to execute the bound callback. - // If no context is supplied a the context is a new `Sammy.EventContext` - // - trigger: function(name, data) { - this.$element().trigger([name, this.eventNamespace()].join('.'), [data]); - return this; - }, - - // Reruns the current route - refresh: function() { - this.last_location = null; - this.trigger('location-changed'); - return this; - }, - - // Takes a single callback that is pushed on to a stack. - // Before any route is run, the callbacks are evaluated in order within - // the current `Sammy.EventContext` - // - // If any of the callbacks explicitly return false, execution of any - // further callbacks and the route itself is halted. - // - // You can also provide a set of options that will define when to run this - // before based on the route it proceeds. - // - // ### Example - // - // var app = $.sammy(function() { - // - // // will run at #/route but not at #/ - // this.before('#/route', function() { - // //... - // }); - // - // // will run at #/ but not at #/route - // this.before({except: {path: '#/route'}}, function() { - // this.log('not before #/route'); - // }); - // - // this.get('#/', function() {}); - // - // this.get('#/route', function() {}); - // - // }); - // - // See `contextMatchesOptions()` for a full list of supported options - // - before: function(options, callback) { - if (_isFunction(options)) { - callback = options; - options = {}; - } - this.befores.push([options, callback]); - return this; - }, - - // A shortcut for binding a callback to be run after a route is executed. - // After callbacks have no guarunteed order. - after: function(callback) { - return this.bind('event-context-after', callback); - }, - - - // Adds an around filter to the application. around filters are functions - // that take a single argument `callback` which is the entire route - // execution path wrapped up in a closure. This means you can decide whether - // or not to proceed with execution by not invoking `callback` or, - // more usefuly wrapping callback inside the result of an asynchronous execution. - // - // ### Example - // - // The most common use case for around() is calling a _possibly_ async function - // and executing the route within the functions callback: - // - // var app = $.sammy(function() { - // - // var current_user = false; - // - // function checkLoggedIn(callback) { - // // /session returns a JSON representation of the logged in user - // // or an empty object - // if (!current_user) { - // $.getJSON('/session', function(json) { - // if (json.login) { - // // show the user as logged in - // current_user = json; - // // execute the route path - // callback(); - // } else { - // // show the user as not logged in - // current_user = false; - // // the context of aroundFilters is an EventContext - // this.redirect('#/login'); - // } - // }); - // } else { - // // execute the route path - // callback(); - // } - // }; - // - // this.around(checkLoggedIn); - // - // }); - // - around: function(callback) { - this.arounds.push(callback); - return this; - }, - - // Returns `true` if the current application is running. - isRunning: function() { - return this._running; - }, - - // Helpers extends the EventContext prototype specific to this app. - // This allows you to define app specific helper functions that can be used - // whenever you're inside of an event context (templates, routes, bind). - // - // ### Example - // - // var app = $.sammy(function() { - // - // helpers({ - // upcase: function(text) { - // return text.toString().toUpperCase(); - // } - // }); - // - // get('#/', function() { with(this) { - // // inside of this context I can use the helpers - // $('#main').html(upcase($('#main').text()); - // }}); - // - // }); - // - // - // ### Arguments - // - // * `extensions` An object collection of functions to extend the context. - // - helpers: function(extensions) { - $.extend(this.context_prototype.prototype, extensions); - return this; - }, - - // Helper extends the event context just like `helpers()` but does it - // a single method at a time. This is especially useful for dynamically named - // helpers - // - // ### Example - // - // // Trivial example that adds 3 helper methods to the context dynamically - // var app = $.sammy(function(app) { - // - // $.each([1,2,3], function(i, num) { - // app.helper('helper' + num, function() { - // this.log("I'm helper number " + num); - // }); - // }); - // - // this.get('#/', function() { - // this.helper2(); //=> I'm helper number 2 - // }); - // }); - // - // ### Arguments - // - // * `name` The name of the method - // * `method` The function to be added to the prototype at `name` - // - helper: function(name, method) { - this.context_prototype.prototype[name] = method; - return this; - }, - - // Actually starts the application's lifecycle. `run()` should be invoked - // within a document.ready block to ensure the DOM exists before binding events, etc. - // - // ### Example - // - // var app = $.sammy(function() { ... }); // your application - // $(function() { // document.ready - // app.run(); - // }); - // - // ### Arguments - // - // * `start_url` Optionally, a String can be passed which the App will redirect to - // after the events/routes have been bound. - run: function(start_url) { - if (this.isRunning()) { return false; } - var app = this; - - // actually bind all the listeners - $.each(this.listeners.toHash(), function(name, callbacks) { - $.each(callbacks, function(i, listener_callback) { - app._listen(name, listener_callback); - }); - }); - - this.trigger('run', {start_url: start_url}); - this._running = true; - // set last location - this.last_location = null; - if (this.getLocation() == '' && typeof start_url != 'undefined') { - this.setLocation(start_url); - } - // check url - this._checkLocation(); - this._location_proxy.bind(); - this.bind('location-changed', function() { - app._checkLocation(); - }); - - // bind to submit to capture post/put/delete routes - this.bind('submit', function(e) { - var returned = app._checkFormSubmission($(e.target).closest('form')); - return (returned === false) ? e.preventDefault() : false; - }); - - // bind unload to body unload - $(window).bind('beforeunload', function() { - app.unload(); - }); - - // trigger html changed - return this.trigger('changed'); - }, - - // The opposite of `run()`, un-binds all event listeners and intervals - // `run()` Automaticaly binds a `onunload` event to run this when - // the document is closed. - unload: function() { - if (!this.isRunning()) { return false; } - var app = this; - this.trigger('unload'); - // clear interval - this._location_proxy.unbind(); - // unbind form submits - this.$element().unbind('submit').removeClass(app.eventNamespace()); - // unbind all events - $.each(this.listeners.toHash() , function(name, listeners) { - $.each(listeners, function(i, listener_callback) { - app._unlisten(name, listener_callback); - }); - }); - this._running = false; - return this; - }, - - // Will bind a single callback function to every event that is already - // being listened to in the app. This includes all the `APP_EVENTS` - // as well as any custom events defined with `bind()`. - // - // Used internally for debug logging. - bindToAllEvents: function(callback) { - var app = this; - // bind to the APP_EVENTS first - $.each(this.APP_EVENTS, function(i, e) { - app.bind(e, callback); - }); - // next, bind to listener names (only if they dont exist in APP_EVENTS) - $.each(this.listeners.keys(true), function(i, name) { - if (app.APP_EVENTS.indexOf(name) == -1) { - app.bind(name, callback); - } - }); - return this; - }, - - // Returns a copy of the given path with any query string after the hash - // removed. - routablePath: function(path) { - return path.replace(QUERY_STRING_MATCHER, ''); - }, - - // Given a verb and a String path, will return either a route object or false - // if a matching route can be found within the current defined set. - lookupRoute: function(verb, path) { - var app = this, routed = false; - this.trigger('lookup-route', {verb: verb, path: path}); - if (typeof this.routes[verb] != 'undefined') { - $.each(this.routes[verb], function(i, route) { - if (app.routablePath(path).match(route.path)) { - routed = route; - return false; - } - }); - } - return routed; - }, - - // First, invokes `lookupRoute()` and if a route is found, parses the - // possible URL params and then invokes the route's callback within a new - // `Sammy.EventContext`. If the route can not be found, it calls - // `notFound()`. If `raise_errors` is set to `true` and - // the `error()` has not been overriden, it will throw an actual JS - // error. - // - // You probably will never have to call this directly. - // - // ### Arguments - // - // * `verb` A String for the verb. - // * `path` A String path to lookup. - // * `params` An Object of Params pulled from the URI or passed directly. - // - // ### Returns - // - // Either returns the value returned by the route callback or raises a 404 Not Found error. - // - runRoute: function(verb, path, params, target) { - var app = this, - route = this.lookupRoute(verb, path), - context, - wrapped_route, - arounds, - around, - befores, - before, - callback_args, - path_params, - final_returned; - - this.log('runRoute', [verb, path].join(' ')); - this.trigger('run-route', {verb: verb, path: path, params: params}); - if (typeof params == 'undefined') { params = {}; } - - $.extend(params, this._parseQueryString(path)); - - if (route) { - this.trigger('route-found', {route: route}); - // pull out the params from the path - if ((path_params = route.path.exec(this.routablePath(path))) !== null) { - // first match is the full path - path_params.shift(); - // for each of the matches - $.each(path_params, function(i, param) { - // if theres a matching param name - if (route.param_names[i]) { - // set the name to the match - params[route.param_names[i]] = _decode(param); - } else { - // initialize 'splat' - if (!params.splat) { params.splat = []; } - params.splat.push(_decode(param)); - } - }); - } - - // set event context - context = new this.context_prototype(this, verb, path, params, target); - // ensure arrays - arounds = this.arounds.slice(0); - befores = this.befores.slice(0); - // set the callback args to the context + contents of the splat - callback_args = [context].concat(params.splat); - // wrap the route up with the before filters - wrapped_route = function() { - var returned; - while (befores.length > 0) { - before = befores.shift(); - // check the options - if (app.contextMatchesOptions(context, before[0])) { - returned = before[1].apply(context, [context]); - if (returned === false) { return false; } - } - } - app.last_route = route; - context.trigger('event-context-before', {context: context}); - returned = route.callback.apply(context, callback_args); - context.trigger('event-context-after', {context: context}); - return returned; - }; - $.each(arounds.reverse(), function(i, around) { - var last_wrapped_route = wrapped_route; - wrapped_route = function() { return around.apply(context, [last_wrapped_route]); }; - }); - try { - final_returned = wrapped_route(); - } catch(e) { - this.error(['500 Error', verb, path].join(' '), e); - } - return final_returned; - } else { - return this.notFound(verb, path); - } - }, - - // Matches an object of options against an `EventContext` like object that - // contains `path` and `verb` attributes. Internally Sammy uses this - // for matching `before()` filters against specific options. You can set the - // object to _only_ match certain paths or verbs, or match all paths or verbs _except_ - // those that match the options. - // - // ### Example - // - // var app = $.sammy(), - // context = {verb: 'get', path: '#/mypath'}; - // - // // match against a path string - // app.contextMatchesOptions(context, '#/mypath'); //=> true - // app.contextMatchesOptions(context, '#/otherpath'); //=> false - // // equivilent to - // app.contextMatchesOptions(context, {only: {path:'#/mypath'}}); //=> true - // app.contextMatchesOptions(context, {only: {path:'#/otherpath'}}); //=> false - // // match against a path regexp - // app.contextMatchesOptions(context, /path/); //=> true - // app.contextMatchesOptions(context, /^path/); //=> false - // // match only a verb - // app.contextMatchesOptions(context, {only: {verb:'get'}}); //=> true - // app.contextMatchesOptions(context, {only: {verb:'post'}}); //=> false - // // match all except a verb - // app.contextMatchesOptions(context, {except: {verb:'post'}}); //=> true - // app.contextMatchesOptions(context, {except: {verb:'get'}}); //=> false - // // match all except a path - // app.contextMatchesOptions(context, {except: {path:'#/otherpath'}}); //=> true - // app.contextMatchesOptions(context, {except: {path:'#/mypath'}}); //=> false - // - contextMatchesOptions: function(context, match_options, positive) { - // empty options always match - var options = match_options; - if (typeof options === 'undefined' || options == {}) { - return true; - } - if (typeof positive === 'undefined') { - positive = true; - } - // normalize options - if (typeof options === 'string' || _isFunction(options.test)) { - options = {path: options}; - } - if (options.only) { - return this.contextMatchesOptions(context, options.only, true); - } else if (options.except) { - return this.contextMatchesOptions(context, options.except, false); - } - var path_matched = true, verb_matched = true; - if (options.path) { - // wierd regexp test - if (_isFunction(options.path.test)) { - path_matched = options.path.test(context.path); - } else { - path_matched = (options.path.toString() === context.path); - } - } - if (options.verb) { - verb_matched = options.verb === context.verb; - } - return positive ? (verb_matched && path_matched) : !(verb_matched && path_matched); - }, - - - // Delegates to the `location_proxy` to get the current location. - // See `Sammy.HashLocationProxy` for more info on location proxies. - getLocation: function() { - return this._location_proxy.getLocation(); - }, - - // Delegates to the `location_proxy` to set the current location. - // See `Sammy.HashLocationProxy` for more info on location proxies. - // - // ### Arguments - // - // * `new_location` A new location string (e.g. '#/') - // - setLocation: function(new_location) { - return this._location_proxy.setLocation(new_location); - }, - - // Swaps the content of `$element()` with `content` - // You can override this method to provide an alternate swap behavior - // for `EventContext.partial()`. - // - // ### Example - // - // var app = $.sammy(function() { - // - // // implements a 'fade out'/'fade in' - // this.swap = function(content) { - // this.$element().hide('slow').html(content).show('slow'); - // } - // - // get('#/', function() { - // this.partial('index.html.erb') // will fade out and in - // }); - // - // }); - // - swap: function(content) { - return this.$element().html(content); - }, - - // a simple global cache for templates. Uses the same semantics as - // `Sammy.Cache` and `Sammy.Storage` so can easily be replaced with - // a persistant storage that lasts beyond the current request. - templateCache: function(key, value) { - if (typeof value != 'undefined') { - return _template_cache[key] = value; - } else { - return _template_cache[key]; - } - }, - - // clear the templateCache - clearTemplateCache: function() { - return _template_cache = {}; - }, - - // This thows a '404 Not Found' error by invoking `error()`. - // Override this method or `error()` to provide custom - // 404 behavior (i.e redirecting to / or showing a warning) - notFound: function(verb, path) { - var ret = this.error(['404 Not Found', verb, path].join(' ')); - return (verb === 'get') ? ret : true; - }, - - // The base error handler takes a string `message` and an `Error` - // object. If `raise_errors` is set to `true` on the app level, - // this will re-throw the error to the browser. Otherwise it will send the error - // to `log()`. Override this method to provide custom error handling - // e.g logging to a server side component or displaying some feedback to the - // user. - error: function(message, original_error) { - if (!original_error) { original_error = new Error(); } - original_error.message = [message, original_error.message].join(' '); - this.trigger('error', {message: original_error.message, error: original_error}); - if (this.raise_errors) { - throw(original_error); - } else { - this.log(original_error.message, original_error); - } - }, - - _checkLocation: function() { - var location, returned; - // get current location - location = this.getLocation(); - // compare to see if hash has changed - if (!this.last_location || this.last_location[0] != 'get' || this.last_location[1] != location) { - // reset last location - this.last_location = ['get', location]; - // lookup route for current hash - returned = this.runRoute('get', location); - } - return returned; - }, - - _getFormVerb: function(form) { - var $form = $(form), verb, $_method; - $_method = $form.find('input[name="_method"]'); - if ($_method.length > 0) { verb = $_method.val(); } - if (!verb) { verb = $form[0].getAttribute('method'); } - if (!verb || verb == '') { verb = 'get'; } - return $.trim(verb.toString().toLowerCase()); - }, - - _checkFormSubmission: function(form) { - var $form, path, verb, params, returned; - this.trigger('check-form-submission', {form: form}); - $form = $(form); - path = $form.attr('action'); - verb = this._getFormVerb($form); - this.log('_checkFormSubmission', $form, path, verb); - if (verb === 'get') { - this.setLocation(path + '?' + this._serializeFormParams($form)); - returned = false; - } else { - params = $.extend({}, this._parseFormParams($form)); - returned = this.runRoute(verb, path, params, form.get(0)); - }; - return (typeof returned == 'undefined') ? false : returned; - }, - - _serializeFormParams: function($form) { - var queryString = "", - fields = $form.serializeArray(), - i; - if (fields.length > 0) { - queryString = this._encodeFormPair(fields[0].name, fields[0].value); - for (i = 1; i < fields.length; i++) { - queryString = queryString + "&" + this._encodeFormPair(fields[i].name, fields[i].value); - } - } - return queryString; - }, - - _encodeFormPair: function(name, value){ - return _encode(name) + "=" + _encode(value); - }, - - _parseFormParams: function($form) { - var params = {}, - form_fields = $form.serializeArray(), - i; - for (i = 0; i < form_fields.length; i++) { - params = this._parseParamPair(params, form_fields[i].name, form_fields[i].value); - } - return params; - }, - - _parseQueryString: function(path) { - var params = {}, parts, pairs, pair, i; - - parts = path.match(QUERY_STRING_MATCHER); - if (parts) { - pairs = parts[1].split('&'); - for (i = 0; i < pairs.length; i++) { - pair = pairs[i].split('='); - params = this._parseParamPair(params, _decode(pair[0]), _decode(pair[1] || "")); - } - } - return params; - }, - - _parseParamPair: function(params, key, value) { - if (params[key]) { - if (_isArray(params[key])) { - params[key].push(value); - } else { - params[key] = [params[key], value]; - } - } else { - params[key] = value; - } - return params; - }, - - _listen: function(name, callback) { - return this.$element().bind([name, this.eventNamespace()].join('.'), callback); - }, - - _unlisten: function(name, callback) { - return this.$element().unbind([name, this.eventNamespace()].join('.'), callback); - } - - }); - - // `Sammy.RenderContext` is an object that makes sequential template loading, - // rendering and interpolation seamless even when dealing with asyncronous - // operations. - // - // `RenderContext` objects are not usually created directly, rather they are - // instatiated from an `Sammy.EventContext` by using `render()`, `load()` or - // `partial()` which all return `RenderContext` objects. - // - // `RenderContext` methods always returns a modified `RenderContext` - // for chaining (like jQuery itself). - // - // The core magic is in the `then()` method which puts the callback passed as - // an argument into a queue to be executed once the previous callback is complete. - // All the methods of `RenderContext` are wrapped in `then()` which allows you - // to queue up methods by chaining, but maintaing a guarunteed execution order - // even with remote calls to fetch templates. - // - Sammy.RenderContext = function(event_context) { - this.event_context = event_context; - this.callbacks = []; - this.previous_content = null; - this.content = null; - this.next_engine = false; - this.waiting = false; - }; - - Sammy.RenderContext.prototype = $.extend({}, Sammy.Object.prototype, { - - // The "core" of the `RenderContext` object, adds the `callback` to the - // queue. If the context is `waiting` (meaning an async operation is happening) - // then the callback will be executed in order, once the other operations are - // complete. If there is no currently executing operation, the `callback` - // is executed immediately. - // - // The value returned from the callback is stored in `content` for the - // subsiquent operation. If you return `false`, the queue will pause, and - // the next callback in the queue will not be executed until `next()` is - // called. This allows for the guarunteed order of execution while working - // with async operations. - // - // If then() is passed a string instead of a function, the string is looked - // up as a helper method on the event context. - // - // ### Example - // - // this.get('#/', function() { - // // initialize the RenderContext - // // Even though `load()` executes async, the next `then()` - // // wont execute until the load finishes - // this.load('myfile.txt') - // .then(function(content) { - // // the first argument to then is the content of the - // // prev operation - // $('#main').html(content); - // }); - // }); - // - then: function(callback) { - if (!_isFunction(callback)) { - // if a string is passed to then, assume we want to call - // a helper on the event context in its context - if (typeof callback === 'string' && callback in this.event_context) { - var helper = this.event_context[callback]; - callback = function(content) { - return helper.apply(this.event_context, [content]); - }; - } else { - return this; - } - } - var context = this; - if (this.waiting) { - this.callbacks.push(callback); - } else { - this.wait(); - window.setTimeout(function() { - var returned = callback.apply(context, [context.content, context.previous_content]); - if (returned !== false) { - context.next(returned); - } - }, 13); - } - return this; - }, - - // Pause the `RenderContext` queue. Combined with `next()` allows for async - // operations. - // - // ### Example - // - // this.get('#/', function() { - // this.load('mytext.json') - // .then(function(content) { - // var context = this, - // data = JSON.parse(content); - // // pause execution - // context.wait(); - // // post to a url - // $.post(data.url, {}, function(response) { - // context.next(JSON.parse(response)); - // }); - // }) - // .then(function(data) { - // // data is json from the previous post - // $('#message').text(data.status); - // }); - // }); - wait: function() { - this.waiting = true; - }, - - // Resume the queue, setting `content` to be used in the next operation. - // See `wait()` for an example. - next: function(content) { - this.waiting = false; - if (typeof content !== 'undefined') { - this.previous_content = this.content; - this.content = content; - } - if (this.callbacks.length > 0) { - this.then(this.callbacks.shift()); - } - }, - - // Load a template into the context. - // The `location` can either be a string specifiying the remote path to the - // file, a jQuery object, or a DOM element. - // - // No interpolation happens by default, the content is stored in - // `content`. - // - // In the case of a path, unless the option `{cache: false}` is passed the - // data is stored in the app's `templateCache()`. - // - // If a jQuery or DOM object is passed the `innerHTML` of the node is pulled in. - // This is useful for nesting templates as part of the initial page load wrapped - // in invisible elements or `