r3819 - in branches/pollen-2.0/pollen-ui-js/src/main/webapp: . js js/controls js/libs js/models views
Author: kmorin Date: 2013-06-08 14:12:35 +0200 (Sat, 08 Jun 2013) New Revision: 3819 Url: http://chorem.org/projects/pollen/repository/revisions/3819 Log: second commit Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/menu.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/poll_form.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/poll_summary.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/vote.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/bootstrap.min.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.fixture.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.jquery.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.jquery.min.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.object.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.observe.attributes.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.observe.delegate.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/models/ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/models/comments.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/models/polls.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/models/votes.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/route.js branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/ branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/menu.ejs branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/poll_form.ejs branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/summary.ejs branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/vote.ejs Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/menu.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/menu.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/menu.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,5 @@ +var Menu = can.Control({ + init: function() { + this.element.html(can.view('views/menu.ejs')); + }, +}); \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/poll_form.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/poll_form.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/poll_form.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,87 @@ +var PollForm = can.Control({ + + defaults: { + poll: new Poll() + } + +}, { + init: function() { + + this.element.html(can.view('views/poll_form.ejs', { + poll: this.options.poll + })); + + }, + + '{can.route} change': function(data, ev, prop, how, newVal, oldVal ) { + if (prop === "type" && (how === "set" || how === "add")) { + if (newVal != "poll") { + this.element.hide(); + } + } else if (prop === "action" && (how === "set" || how === "add")) { + if (newVal != "edit") { + this.element.hide(); + } + } + }, + + ':type/:action route': function(data) { + if (data.type === "poll" && data.action === "edit") { + this.editPoll(new Poll()); + } + }, + + ':type/:id/:action route': function(data) { + if (data.type === "poll" && data.action === "edit") { + var self = this; + $.when(Poll.findOne({id: data.id})).then( + function(poll) { + self.editPoll(poll); + }); + } + }, + + '.save click': function() { + this.updatePoll(); + }, + + '.create click': function() { + can.route.attr({ type: "poll", action: "edit" }, true); + }, + + '[data-id] click': function(el, ev) { + can.route.attr({ type: "poll", action: "edit", id: el.data('id') }); + }, + + '{poll} created': function(data) { + can.route.attr({ type: "poll", action: "summary", id: data.id }); + }, + + '{poll} updated': function(data) { + can.route.attr({ type: "poll", action: "summary", id: data.id }); + }, + + updatePoll: function() { + var form = this.element.find('form'), + values = can.deparam(form.serialize()); + + this.options.poll.attr(values).save(); + }, + + editPoll: function(poll) { + console.log("edit poll " + poll.id + " : " + poll.description); + this.options.poll = poll; + this.on(); + this.refreshForm(); + this.element.show(); + }, + + refreshForm: function() { + var form = this.element.find('form'); + form[0].reset(); + for (var k in this.options.poll._data) { + form.find("[name=" + k + "]").val(this.options.poll[k]); + } + } + +}); \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/poll_summary.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/poll_summary.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/poll_summary.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,32 @@ +var PollSummary = can.Control({ + + '{can.route} change': function(data, ev, prop, how, newVal, oldVal ) { + if (prop === "type" && (how === "set" || how === "add")) { + if (newVal != "poll") { + this.element.hide(); + } + } else if (prop === "action" && (how === "set" || how === "add")) { + if (newVal != "summary") { + this.element.hide(); + } + } + }, + + ':type/:id/:action route': function(data) { + if (data.type === "poll" && data.action === "summary") { + var self = this; + $.when(Poll.findOne({id: data.id})).then( + function(poll) { + self.showSummary(poll); + }); + } + }, + + showSummary: function(poll) { + this.element.html(can.view('views/summary.ejs', { + poll: poll + })); + this.element.show(); + } + +}); \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/vote.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/vote.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/controls/vote.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,36 @@ +var PollVote = can.Control({ + + '{can.route} change': function(data, ev, prop, how, newVal, oldVal ) { + if (prop === "type" && (how === "set" || how === "add")) { + if (newVal != "poll") { + this.element.hide(); + } + } else if (prop === "action" && (how === "set" || how === "add")) { + if (newVal != "vote") { + this.element.hide(); + } + } + }, + + ':type/:id/:action route': function(data) { + if (data.type === "poll" && data.action === "vote") { + var self = this; + $.when(Poll.findOne({id: data.id}), + Comment.findAll({pollId: data.id}), + Vote.findAll({pollId: data.id})).then( + function(poll, comments, votes) { + self.showPoll(poll, comments, votes); + }); + } + }, + + showPoll: function(poll, comments, votes) { + this.element.html(can.view('views/vote.ejs', { + poll: poll, + comments: comments, + votes: votes + })); + this.element.show(); + } + +}); \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/bootstrap.min.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/bootstrap.min.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/bootstrap.min.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,6 @@ +/*! +* Bootstrap.js by @fat & @mdo +* Copyright 2012 Twitter, Inc. +* http://www.apache.org/licenses/LICENSE-2.0.txt +*/ +!function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(".dropdown-backdrop").remove(),e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||("ontouchstart"in document.documentElement&&e('<div class="dropdown-backdrop"/>').insertBefore(e(this)).on("click",r),s.toggleClass("open")),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f<s.length-1&&f++,~f||(f=0),s.eq(f).focus()}};var s=e.fn.dropdown;e.fn.dropdown=function(t){return this.each(function(){var r=e(this),i=r.data("dropdown");i||r.data("dropdown",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.dropdown.Constructor=n,e.fn.dropdown.noConflict=function(){return e.fn.dropdown=s,this},e(document).on("click.dropdown.data-api",r).on("click.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("click.dropdown.data-api",t,n.prototype.toggle).on("keydown.dropdown.data-api",t+", [role=menu]",n.prototype.keydown)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=n,this.$element=e(t).delegate('[data-dismiss="modal"]',"click.dismiss.modal",e.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};t.prototype={constructor:t,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var t=this,n=e.Event("show");this.$element.trigger(n);if(this.isShown||n.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var n=e.support.transition&&t.$element.hasClass("fade");t.$element.parent().length||t.$element.appendTo(document.body),t.$element.show(),n&&t.$element[0].offsetWidth,t.$element.addClass("in").attr("aria-hidden",!1),t.enforceFocus(),n?t.$element.one(e.support.transition.end,function(){t.$element.focus().trigger("shown")}):t.$element.focus().trigger("shown")})},hide:function(t){t&&t.preventDefault();var n=this;t=e.Event("hide"),this.$element.trigger(t);if(!this.isShown||t.isDefaultPrevented())return;this.isShown=!1,this.escape(),e(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),e.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var t=this;e(document).on("focusin.modal",function(e){t.$element[0]!==e.target&&!t.$element.has(e.target).length&&t.$element.focus()})},escape:function(){var e=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(t){t.which==27&&e.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var t=this,n=setTimeout(function(){t.$element.off(e.support.transition.end),t.hideModal()},500);this.$element.one(e.support.transition.end,function(){clearTimeout(n),t.hideModal()})},hideModal:function(){var e=this;this.$element.hide(),this.backdrop(function(){e.removeBackdrop(),e.$element.trigger("hidden")})},removeBackdrop:function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},backdrop:function(t){var n=this,r=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var i=e.support.transition&&r;this.$backdrop=e('<div class="modal-backdrop '+r+'" />').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e.fn[this.type].defaults,r={},i;this._options&&e.each(this._options,function(e,t){n[e]!=t&&(r[e]=t)},this),i=e(t.currentTarget)[this.type](r).data(this.type);if(!i.options.delay||!i.options.delay.show)return i.show();clearTimeout(this.timeout),i.hoverState="in",this.timeout=setTimeout(function(){i.hoverState=="in"&&i.show()},i.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length<this.options.minLength?this.shown?this.hide():this:(n=e.isFunction(this.source)?this.source(this.query,e.proxy(this.process,this)):this.source,n?this.process(n):this)},process:function(t){var n=this;return t=e.grep(t,function(e){return n.matcher(e)}),t=this.sorter(t),t.length?this.render(t.slice(0,this.options.items)).show():this.shown?this.hide():this},matcher:function(e){return~e.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(e){var t=[],n=[],r=[],i;while(i=e.shift())i.toLowerCase().indexOf(this.query.toLowerCase())?~i.indexOf(this.query)?n.push(i):r.push(i):t.push(i);return t.concat(n,r)},highlighter:function(e){var t=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return e.replace(new RegExp("("+t+")","ig"),function(e,t){return"<strong>"+t+"</strong>"})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery); \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.fixture.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.fixture.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.fixture.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,614 @@ +/*! + * CanJS - 1.1.6 + * http://canjs.us/ + * Copyright (c) 2013 Bitovi + * Wed, 29 May 2013 18:59:29 GMT + * Licensed MIT + * Includes: can/util/fixture + * Download from: http://canjs.com + */ +(function(can) { + + // Get the URL from old Steal root, new Steal config or can.fixture.rootUrl + var getUrl = function(url) { + if (typeof steal !== 'undefined') { + if (can.isFunction(steal.config)) { + return steal.config().root.mapJoin(url).toString(); + } + return steal.root.join(url).toString(); + } + return (can.fixture.rootUrl || '') + url; + } + + var updateSettings = function(settings, originalOptions) { + if (!can.fixture.on) { + return; + } + + //simple wrapper for logging + var _logger = function(type, arr) { + if (console.log.apply) { + Function.prototype.call.apply(console[type], [console].concat(arr)); + // console[type].apply(console, arr) + } else { + console[type](arr) + } + }, + log = function() { + if (typeof steal !== 'undefined' && steal.dev) { + steal.dev.log('fixture INFO: ' + Array.prototype.slice.call(arguments).join(' ')); + } + } + + // We always need the type which can also be called method, default to GET + settings.type = settings.type || settings.method || 'GET'; + + // add the fixture option if programmed in + var data = overwrite(settings); + + // if we don't have a fixture, do nothing + if (!settings.fixture) { + if (window.location.protocol === "file:") { + log("ajax request to " + settings.url + ", no fixture found"); + } + return; + } + + //if referencing something else, update the fixture option + if (typeof settings.fixture === "string" && can.fixture[settings.fixture]) { + settings.fixture = can.fixture[settings.fixture]; + } + + // if a string, we just point to the right url + if (typeof settings.fixture == "string") { + var url = settings.fixture; + + if (/^\/\//.test(url)) { + // this lets us use rootUrl w/o having steal... + url = getUrl(settings.fixture.substr(2)); + } + + if (data) { + // Template static fixture URLs + url = can.sub(url, data); + } + + delete settings.fixture; + + + + settings.url = url; + settings.data = null; + settings.type = "GET"; + if (!settings.error) { + settings.error = function(xhr, error, message) { + throw "fixtures.js Error " + error + " " + message; + }; + } + } else { + + + //it's a function ... add the fixture datatype so our fixture transport handles it + // TODO: make everything go here for timing and other fun stuff + // add to settings data from fixture ... + settings.dataTypes && settings.dataTypes.splice(0, 0, "fixture"); + + if (data && originalOptions) { + can.extend(originalOptions.data, data) + } + } + }, + // A helper function that takes what's called with response + // and moves some common args around to make it easier to call + extractResponse = function(status, statusText, responses, headers) { + // if we get response(RESPONSES, HEADERS) + if (typeof status != "number") { + headers = statusText; + responses = status; + statusText = "success" + status = 200; + } + // if we get response(200, RESPONSES, HEADERS) + if (typeof statusText != "string") { + headers = responses; + responses = statusText; + statusText = "success"; + } + if (status >= 400 && status <= 599) { + this.dataType = "text" + } + return [status, statusText, extractResponses(this, responses), headers]; + }, + // If we get data instead of responses, + // make sure we provide a response type that matches the first datatype (typically json) + extractResponses = function(settings, responses) { + var next = settings.dataTypes ? settings.dataTypes[0] : (settings.dataType || 'json'); + if (!responses || !responses[next]) { + var tmp = {} + tmp[next] = responses; + responses = tmp; + } + return responses; + }; + + //used to check urls + // check if jQuery + if (can.ajaxPrefilter && can.ajaxTransport) { + + // the pre-filter needs to re-route the url + can.ajaxPrefilter(updateSettings); + + can.ajaxTransport("fixture", function(s, original) { + // remove the fixture from the datatype + s.dataTypes.shift(); + + //we'll return the result of the next data type + var timeout, stopped = false; + + return { + send: function(headers, callback) { + // we'll immediately wait the delay time for all fixtures + timeout = setTimeout(function() { + // if the user wants to call success on their own, we allow it ... + var success = function() { + if (stopped === false) { + callback.apply(null, extractResponse.apply(s, arguments)); + } + }, + // get the result form the fixture + result = s.fixture(original, success, headers, s); + if (result !== undefined) { + // make sure the result has the right dataType + callback(200, "success", extractResponses(s, result), {}); + } + }, can.fixture.delay); + }, + abort: function() { + stopped = true; + clearTimeout(timeout) + } + }; + }); + } else { + var AJAX = can.ajax; + can.ajax = function(settings) { + updateSettings(settings, settings); + if (settings.fixture) { + var timeout, d = new can.Deferred(), + stopped = false; + + //TODO this should work with response + d.getResponseHeader = function() {} + + // call success and fail + d.then(settings.success, settings.fail); + + // abort should stop the timeout and calling success + d.abort = function() { + clearTimeout(timeout); + stopped = true; + d.reject(d) + } + // set a timeout that simulates making a request .... + timeout = setTimeout(function() { + // if the user wants to call success on their own, we allow it ... + var success = function() { + var response = extractResponse.apply(settings, arguments), + status = response[0]; + + if ((status >= 200 && status < 300 || status === 304) && stopped === false) { + d.resolve(response[2][settings.dataType]) + } else { + // TODO probably resolve better + d.reject(d, 'error', response[1]); + } + }, + // get the result form the fixture + result = settings.fixture(settings, success, settings.headers, settings); + if (result !== undefined) { + d.resolve(result) + } + }, can.fixture.delay); + + return d; + } else { + return AJAX(settings); + } + } + } + + var typeTest = /^(script|json|text|jsonp)$/, + // a list of 'overwrite' settings object + overwrites = [], + // returns the index of an overwrite function + find = function(settings, exact) { + for (var i = 0; i < overwrites.length; i++) { + if ($fixture._similar(settings, overwrites[i], exact)) { + return i; + } + } + return -1; + }, + // overwrites the settings fixture if an overwrite matches + overwrite = function(settings) { + var index = find(settings); + if (index > -1) { + settings.fixture = overwrites[index].fixture; + return $fixture._getData(overwrites[index].url, settings.url) + } + + }, + // Makes an attempt to guess where the id is at in the url and returns it. + getId = function(settings) { + var id = settings.data.id; + + if (id === undefined && typeof settings.data === "number") { + id = settings.data; + } + + + + if (id === undefined) { + settings.url.replace(/\/(\d+)(\/|$|\.)/g, function(all, num) { + id = num; + }); + } + + if (id === undefined) { + id = settings.url.replace(/\/(\w+)(\/|$|\.)/g, function(all, num) { + if (num != 'update') { + id = num; + } + }) + } + + if (id === undefined) { // if still not set, guess a random number + id = Math.round(Math.random() * 1000) + } + + return id; + }; + + var $fixture = can.fixture = function(settings, fixture) { + // if we provide a fixture ... + if (fixture !== undefined) { + if (typeof settings == 'string') { + // handle url strings + var matches = settings.match(/(GET|POST|PUT|DELETE) (.+)/i); + if (!matches) { + settings = { + url: settings + }; + } else { + settings = { + url: matches[2], + type: matches[1] + }; + } + + } + + //handle removing. An exact match if fixture was provided, otherwise, anything similar + var index = find(settings, !! fixture); + if (index > -1) { + overwrites.splice(index, 1) + } + if (fixture == null) { + return + } + settings.fixture = fixture; + overwrites.push(settings) + } else { + can.each(settings, function(fixture, url) { + $fixture(url, fixture); + }) + } + }; + var replacer = can.replacer; + + can.extend(can.fixture, { + // given ajax settings, find an overwrite + _similar: function(settings, overwrite, exact) { + if (exact) { + return can.Object.same(settings, overwrite, { + fixture: null + }) + } else { + return can.Object.subset(settings, overwrite, can.fixture._compare) + } + }, + _compare: { + url: function(a, b) { + return !!$fixture._getData(b, a) + }, + fixture: null, + type: "i" + }, + // gets data from a url like "/todo/{id}" given "todo/5" + _getData: function(fixtureUrl, url) { + var order = [], + fixtureUrlAdjusted = fixtureUrl.replace('.', '\\.').replace('?', '\\?'), + res = new RegExp(fixtureUrlAdjusted.replace(replacer, function(whole, part) { + order.push(part) + return "([^\/]+)" + }) + "$").exec(url), + data = {}; + + if (!res) { + return null; + } + res.shift(); + can.each(order, function(name) { + data[name] = res.shift() + }) + return data; + }, + + store: function(types, count, make, filter) { + + var items = [], // TODO: change this to a hash + currentId = 0, + findOne = function(id) { + for (var i = 0; i < items.length; i++) { + if (id == items[i].id) { + return items[i]; + } + } + }, + methods = {}; + + if (typeof types === "string") { + types = [types + "s", types] + } else if (!can.isArray(types)) { + filter = make; + make = count; + count = types; + } + + // make all items + can.extend(methods, { + + findAll: function(request) { + request = request || {} + //copy array of items + var retArr = items.slice(0); + request.data = request.data || {}; + //sort using order + //order looks like ["age ASC","gender DESC"] + can.each((request.data.order || []).slice(0).reverse(), function(name) { + var split = name.split(" "); + retArr = retArr.sort(function(a, b) { + if (split[1].toUpperCase() !== "ASC") { + if (a[split[0]] < b[split[0]]) { + return 1; + } else if (a[split[0]] == b[split[0]]) { + return 0 + } else { + return -1; + } + } else { + if (a[split[0]] < b[split[0]]) { + return -1; + } else if (a[split[0]] == b[split[0]]) { + return 0 + } else { + return 1; + } + } + }); + }); + + //group is just like a sort + can.each((request.data.group || []).slice(0).reverse(), function(name) { + var split = name.split(" "); + retArr = retArr.sort(function(a, b) { + return a[split[0]] > b[split[0]]; + }); + }); + + var offset = parseInt(request.data.offset, 10) || 0, + limit = parseInt(request.data.limit, 10) || (items.length - offset), + i = 0; + + //filter results if someone added an attr like parentId + for (var param in request.data) { + i = 0; + if (request.data[param] !== undefined && // don't do this if the value of the param is null (ignore it) + (param.indexOf("Id") != -1 || param.indexOf("_id") != -1)) { + while (i < retArr.length) { + if (request.data[param] != retArr[i][param]) { + retArr.splice(i, 1); + } else { + i++; + } + } + } + } + + if (filter) { + i = 0; + while (i < retArr.length) { + if (!filter(retArr[i], request)) { + retArr.splice(i, 1); + } else { + i++; + } + } + } + + //return data spliced with limit and offset + return { + "count": retArr.length, + "limit": request.data.limit, + "offset": request.data.offset, + "data": retArr.slice(offset, offset + limit) + }; + }, + + findOne: function(request, response) { + var item = findOne(getId(request)); + response(item ? item : undefined); + }, + + update: function(request, response) { + var id = getId(request); + + // TODO: make it work with non-linear ids .. + can.extend(findOne(id), request.data); + response({ + id: getId(request) + }, { + location: request.url || "/" + getId(request) + }); + }, + + destroy: function(request) { + var id = getId(request); + for (var i = 0; i < items.length; i++) { + if (items[i].id == id) { + items.splice(i, 1); + break; + } + } + + // TODO: make it work with non-linear ids .. + can.extend(findOne(id) || {}, request.data); + return {}; + }, + + create: function(settings, response) { + var item = make(items.length, items); + + can.extend(item, settings.data); + + if (!item.id) { + item.id = currentId++; + } + + items.push(item); + response({ + id: item.id + }, { + location: settings.url + "/" + item.id + }) + } + }); + + var reset = function() { + items = []; + for (var i = 0; i < (count); i++) { + //call back provided make + var item = make(i, items); + + if (!item.id) { + item.id = i; + } + currentId = Math.max(item.id + 1, currentId + 1) || items.length; + items.push(item); + } + if (can.isArray(types)) { + can.fixture["~" + types[0]] = items; + can.fixture["-" + types[0]] = methods.findAll; + can.fixture["-" + types[1]] = methods.findOne; + can.fixture["-" + types[1] + "Update"] = methods.update; + can.fixture["-" + types[1] + "Destroy"] = methods.destroy; + can.fixture["-" + types[1] + "Create"] = methods.create; + } + + } + reset() + // if we have types given add them to can.fixture + + + return can.extend({ + getId: getId, + + find: function(settings) { + return findOne(getId(settings)); + }, + + reset: reset + }, methods); + }, + + rand: function(arr, min, max) { + if (typeof arr == 'number') { + if (typeof min == 'number') { + return arr + Math.floor(Math.random() * (min - arr)); + } else { + return Math.floor(Math.random() * arr); + } + + } + var rand = arguments.callee; + // get a random set + if (min === undefined) { + return rand(arr, rand(arr.length + 1)) + } + // get a random selection of arr + var res = []; + arr = arr.slice(0); + // set max + if (!max) { + max = min; + } + //random max + max = min + Math.round(rand(max - min)) + for (var i = 0; i < max; i++) { + res.push(arr.splice(rand(arr.length), 1)[0]) + } + return res; + }, + + xhr: function(xhr) { + return can.extend({}, { + abort: can.noop, + getAllResponseHeaders: function() { + return ""; + }, + getResponseHeader: function() { + return ""; + }, + open: can.noop, + overrideMimeType: can.noop, + readyState: 4, + responseText: "", + responseXML: null, + send: can.noop, + setRequestHeader: can.noop, + status: 200, + statusText: "OK" + }, xhr); + }, + + on: true + }); + + can.fixture.delay = 200; + + + can.fixture.rootUrl = getUrl(''); + + can.fixture["-handleFunction"] = function(settings) { + if (typeof settings.fixture === "string" && can.fixture[settings.fixture]) { + settings.fixture = can.fixture[settings.fixture]; + } + if (typeof settings.fixture == "function") { + setTimeout(function() { + if (settings.success) { + settings.success.apply(null, settings.fixture(settings, "success")); + } + if (settings.complete) { + settings.complete.apply(null, settings.fixture(settings, "complete")); + } + }, can.fixture.delay); + return true; + } + return false; + }; + + //Expose this for fixture debugging + can.fixture.overwrites = overwrites; + can.fixture.make = can.fixture.store; + return can.fixture; + })(can); \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.jquery.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.jquery.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.jquery.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,4367 @@ +/*! + * CanJS - 1.1.6 + * http://canjs.us/ + * Copyright (c) 2013 Bitovi + * Wed, 29 May 2013 18:59:27 GMT + * Licensed MIT + * Includes: can/construct,can/observe,can/observe/compute,can/model,can/view,can/view/ejs,can/control,can/route,can/control/route + * Download from: http://canjs.com + */ +(function(undefined) { + + // ## util/can.js + var __m5 = (function() { + var can = window.can || {}; + if (typeof GLOBALCAN === 'undefined' || GLOBALCAN !== false) { + window.can = can; + } + + can.isDeferred = function(obj) { + var isFunction = this.isFunction; + // Returns `true` if something looks like a deferred. + return obj && isFunction(obj.then) && isFunction(obj.pipe); + }; + + var cid = 0; + can.cid = function(object, name) { + if (object._cid) { + return object._cid + } else { + return object._cid = (name || "") + (++cid) + } + } + can.VERSION = '@EDGE'; + return can; + })(); + + // ## util/array/each.js + var __m6 = (function(can) { + can.each = function(elements, callback, context) { + var i = 0, + key; + if (elements) { + if (typeof elements.length === 'number' && elements.pop) { + if (elements.attr) { + elements.attr('length'); + } + for (key = elements.length; i < key; i++) { + if (callback.call(context || elements[i], elements[i], i, elements) === false) { + break; + } + } + } else if (elements.hasOwnProperty) { + for (key in elements) { + if (elements.hasOwnProperty(key)) { + if (callback.call(context || elements[key], elements[key], key, elements) === false) { + break; + } + } + } + } + } + return elements; + }; + + return can; + })(__m5); + + // ## util/jquery/jquery.js + var __m3 = (function($, can) { + // _jQuery node list._ + $.extend(can, $, { + trigger: function(obj, event, args) { + if (obj.trigger) { + obj.trigger(event, args); + } else { + $.event.trigger(event, args, obj, true); + } + }, + addEvent: function(ev, cb) { + $([this]).bind(ev, cb); + return this; + }, + removeEvent: function(ev, cb) { + $([this]).unbind(ev, cb); + return this; + }, + // jquery caches fragments, we always needs a new one + buildFragment: function(elems, context) { + var oldFragment = $.buildFragment, + ret; + + elems = [elems]; + // Set context per 1.8 logic + context = context || document; + context = !context.nodeType && context[0] || context; + context = context.ownerDocument || context; + + ret = oldFragment.call(jQuery, elems, context); + + return ret.cacheable ? $.clone(ret.fragment) : ret.fragment || ret; + }, + $: $, + each: can.each + }); + + // Wrap binding functions. + $.each(['bind', 'unbind', 'undelegate', 'delegate'], function(i, func) { + can[func] = function() { + var t = this[func] ? this : $([this]); + t[func].apply(t, arguments); + return this; + }; + }); + + // Wrap modifier functions. + $.each(["append", "filter", "addClass", "remove", "data", "get"], function(i, name) { + can[name] = function(wrapped) { + return wrapped[name].apply(wrapped, can.makeArray(arguments).slice(1)); + }; + }); + + // Memory safe destruction. + var oldClean = $.cleanData; + + $.cleanData = function(elems) { + $.each(elems, function(i, elem) { + if (elem) { + can.trigger(elem, "destroyed", [], false); + } + }); + oldClean(elems); + }; + + return can; + })(jQuery, __m5, __m6); + + // ## util/string/string.js + var __m2 = (function(can) { + // ##string.js + // _Miscellaneous string utility functions._ + + // Several of the methods in this plugin use code adapated from Prototype + // Prototype JavaScript framework, version 1.6.0.1. + // © 2005-2007 Sam Stephenson + var strUndHash = /_|-/, + strColons = /\=\=/, + strWords = /([A-Z]+)([A-Z][a-z])/g, + strLowUp = /([a-z\d])([A-Z])/g, + strDash = /([a-z\d])([A-Z])/g, + strReplacer = /\{([^\}]+)\}/g, + strQuote = /"/g, + strSingleQuote = /'/g, + + // Returns the `prop` property from `obj`. + // If `add` is true and `prop` doesn't exist in `obj`, create it as an + // empty object. + getNext = function(obj, prop, add) { + var result = obj[prop]; + + if (result === undefined && add === true) { + result = obj[prop] = {} + } + return result + }, + + // Returns `true` if the object can have properties (no `null`s). + isContainer = function(current) { + return (/^f|^o/).test(typeof current); + }; + + can.extend(can, { + // Escapes strings for HTML. + + esc: function(content) { + // Convert bad values into empty strings + var isInvalid = content === null || content === undefined || (isNaN(content) && ("" + content === 'NaN')); + return ("" + (isInvalid ? '' : content)) + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(strQuote, '"') + .replace(strSingleQuote, "'"); + }, + + + getObject: function(name, roots, add) { + + // The parts of the name we are looking up + // `['App','Models','Recipe']` + var parts = name ? name.split('.') : [], + length = parts.length, + current, + r = 0, + i, container, rootsLength; + + // Make sure roots is an `array`. + roots = can.isArray(roots) ? roots : [roots || window]; + + rootsLength = roots.length + + if (!length) { + return roots[0]; + } + + // For each root, mark it as current. + for (r; r < rootsLength; r++) { + current = roots[r]; + container = undefined; + + // Walk current to the 2nd to last object or until there + // is not a container. + for (i = 0; i < length && isContainer(current); i++) { + container = current; + current = getNext(container, parts[i]); + } + + // If we found property break cycle + if (container !== undefined && current !== undefined) { + break + } + } + + // Remove property from found container + if (add === false && current !== undefined) { + delete container[parts[i - 1]] + } + + // When adding property add it to the first root + if (add === true && current === undefined) { + current = roots[0] + + for (i = 0; i < length && isContainer(current); i++) { + current = getNext(current, parts[i], true); + } + } + + return current; + }, + // Capitalizes a string. + + capitalize: function(s, cache) { + // Used to make newId. + return s.charAt(0).toUpperCase() + s.slice(1); + }, + + // Underscores a string. + + underscore: function(s) { + return s + .replace(strColons, '/') + .replace(strWords, '$1_$2') + .replace(strLowUp, '$1_$2') + .replace(strDash, '_') + .toLowerCase(); + }, + // Micro-templating. + + sub: function(str, data, remove) { + var obs = []; + + str = str || ''; + + obs.push(str.replace(strReplacer, function(whole, inside) { + + // Convert inside to type. + var ob = can.getObject(inside, data, remove === true ? false : undefined); + + if (ob === undefined) { + obs = null; + return ""; + } + + // If a container, push into objs (which will return objects found). + if (isContainer(ob) && obs) { + obs.push(ob); + return ""; + } + + return "" + ob; + })); + + return obs === null ? obs : (obs.length <= 1 ? obs[0] : obs); + }, + + // These regex's are used throughout the rest of can, so let's make + // them available. + replacer: strReplacer, + undHash: strUndHash + }); + return can; + })(__m3); + + // ## construct/construct.js + var __m1 = (function(can) { + + // ## construct.js + // `can.Construct` + // _This is a modified version of + // [John Resig's class](http://ejohn.org/blog/simple-javascript-inheritance/). + // It provides class level inheritance and callbacks._ + + // A private flag used to initialize a new class instance without + // initializing it's bindings. + var initializing = 0; + + + can.Construct = function() { + if (arguments.length) { + return can.Construct.extend.apply(can.Construct, arguments); + } + }; + + + can.extend(can.Construct, { + + newInstance: function() { + // Get a raw instance object (`init` is not called). + var inst = this.instance(), + arg = arguments, + args; + + // Call `setup` if there is a `setup` + if (inst.setup) { + args = inst.setup.apply(inst, arguments); + } + + // Call `init` if there is an `init` + // If `setup` returned `args`, use those as the arguments + if (inst.init) { + inst.init.apply(inst, args || arguments); + } + + return inst; + }, + // Overwrites an object with methods. Used in the `super` plugin. + // `newProps` - New properties to add. + // `oldProps` - Where the old properties might be (used with `super`). + // `addTo` - What we are adding to. + _inherit: function(newProps, oldProps, addTo) { + can.extend(addTo || newProps, newProps || {}) + }, + // used for overwriting a single property. + // this should be used for patching other objects + // the super plugin overwrites this + _overwrite: function(what, oldProps, propName, val) { + what[propName] = val; + }, + // Set `defaults` as the merger of the parent `defaults` and this + // object's `defaults`. If you overwrite this method, make sure to + // include option merging logic. + + setup: function(base, fullName) { + this.defaults = can.extend(true, {}, base.defaults, this.defaults); + }, + // Create's a new `class` instance without initializing by setting the + // `initializing` flag. + instance: function() { + + // Prevents running `init`. + initializing = 1; + + var inst = new this(); + + // Allow running `init`. + initializing = 0; + + return inst; + }, + // Extends classes. + + extend: function(fullName, klass, proto) { + // Figure out what was passed and normalize it. + if (typeof fullName != 'string') { + proto = klass; + klass = fullName; + fullName = null; + } + + if (!proto) { + proto = klass; + klass = null; + } + proto = proto || {}; + + var _super_class = this, + _super = this.prototype, + name, shortName, namespace, prototype; + + // Instantiate a base class (but only create the instance, + // don't run the init constructor). + prototype = this.instance(); + + // Copy the properties over onto the new prototype. + can.Construct._inherit(proto, _super, prototype); + + // The dummy class constructor. + + function Constructor() { + // All construction is actually done in the init method. + if (!initializing) { + return this.constructor !== Constructor && arguments.length ? + // We are being called without `new` or we are extending. + arguments.callee.extend.apply(arguments.callee, arguments) : + // We are being called with `new`. + this.constructor.newInstance.apply(this.constructor, arguments); + } + } + + // Copy old stuff onto class (can probably be merged w/ inherit) + for (name in _super_class) { + if (_super_class.hasOwnProperty(name)) { + Constructor[name] = _super_class[name]; + } + } + + // Copy new static properties on class. + can.Construct._inherit(klass, _super_class, Constructor); + + // Setup namespaces. + if (fullName) { + + var parts = fullName.split('.'), + shortName = parts.pop(), + current = can.getObject(parts.join('.'), window, true), + namespace = current, + _fullName = can.underscore(fullName.replace(/\./g, "_")), + _shortName = can.underscore(shortName); + + + + current[shortName] = Constructor; + } + + // Set things that shouldn't be overwritten. + can.extend(Constructor, { + constructor: Constructor, + prototype: prototype, + + namespace: namespace, + + _shortName: _shortName, + + fullName: fullName, + _fullName: _fullName + }); + + // Dojo and YUI extend undefined + if (shortName !== undefined) { + Constructor.shortName = shortName; + } + + // Make sure our prototype looks nice. + Constructor.prototype.constructor = Constructor; + + + // Call the class `setup` and `init` + var t = [_super_class].concat(can.makeArray(arguments)), + args = Constructor.setup.apply(Constructor, t); + + if (Constructor.init) { + Constructor.init.apply(Constructor, args || t); + } + + + return Constructor; + + } + + }); + return can.Construct; + })(__m2); + + // ## util/bind/bind.js + var __m8 = (function(can) { + + + // ## Bind helpers + can.bindAndSetup = function() { + // Add the event to this object + can.addEvent.apply(this, arguments); + // If not initializing, and the first binding + // call bindsetup if the function exists. + if (!this._init) { + if (!this._bindings) { + this._bindings = 1; + // setup live-binding + this._bindsetup && this._bindsetup(); + + } else { + this._bindings++; + } + + } + + return this; + }; + + can.unbindAndTeardown = function(ev, handler) { + // Remove the event handler + can.removeEvent.apply(this, arguments); + + this._bindings--; + // If there are no longer any bindings and + // there is a bindteardown method, call it. + if (!this._bindings) { + this._bindteardown && this._bindteardown(); + } + return this; + } + + return can; + + })(__m3); + + // ## observe/observe.js + var __m7 = (function(can, bind) { + // ## observe.js + // `can.Observe` + // _Provides the observable pattern for JavaScript Objects._ + // Returns `true` if something is an object with properties of its own. + var canMakeObserve = function(obj) { + return obj && !can.isDeferred(obj) && (can.isArray(obj) || can.isPlainObject(obj) || (obj instanceof can.Observe)); + }, + + // Removes all listeners. + unhookup = function(items, namespace) { + return can.each(items, function(item) { + if (item && item.unbind) { + item.unbind("change" + namespace); + } + }); + }, + // Listens to changes on `child` and "bubbles" the event up. + // `child` - The object to listen for changes on. + // `prop` - The property name is at on. + // `parent` - The parent object of prop. + // `ob` - (optional) The Observe object constructor + // `list` - (optional) The observable list constructor + hookupBubble = function(child, prop, parent, Ob, List) { + Ob = Ob || Observe; + List = List || Observe.List; + + // If it's an `array` make a list, otherwise a child. + if (child instanceof Observe) { + // We have an `observe` already... + // Make sure it is not listening to this already + // It's only listening if it has bindings already. + parent._bindings && unhookup([child], parent._cid); + } else if (can.isArray(child)) { + child = new List(child); + } else { + child = new Ob(child); + } + // only listen if something is listening to you + if (parent._bindings) { + // Listen to all changes and `batchTrigger` upwards. + bindToChildAndBubbleToParent(child, prop, parent) + } + + + return child; + }, + bindToChildAndBubbleToParent = function(child, prop, parent) { + child.bind("change" + parent._cid, function() { + // `batchTrigger` the type on this... + var args = can.makeArray(arguments), + ev = args.shift(); + args[0] = (prop === "*" ? [parent.indexOf(child), args[0]] : [prop, args[0]]).join("."); + + // track objects dispatched on this observe + ev.triggeredNS = ev.triggeredNS || {}; + + // if it has already been dispatched exit + if (ev.triggeredNS[parent._cid]) { + return; + } + + ev.triggeredNS[parent._cid] = true; + // send change event with modified attr to parent + can.trigger(parent, ev, args); + // send modified attr event to parent + //can.trigger(parent, args[0], args); + }); + } + // An `id` to track events for a given observe. + observeId = 0, + // A helper used to serialize an `Observe` or `Observe.List`. + // `observe` - The observable. + // `how` - To serialize with `attr` or `serialize`. + // `where` - To put properties, in an `{}` or `[]`. + serialize = function(observe, how, where) { + // Go through each property. + observe.each(function(val, name) { + // If the value is an `object`, and has an `attrs` or `serialize` function. + where[name] = canMakeObserve(val) && can.isFunction(val[how]) ? + // Call `attrs` or `serialize` to get the original data back. + val[how]() : + // Otherwise return the value. + val; + }); + return where; + }, + attrParts = function(attr, keepKey) { + if (keepKey) { + return [attr]; + } + return can.isArray(attr) ? attr : ("" + attr).split("."); + }, + // Which batch of events this is for -- might not want to send multiple + // messages on the same batch. This is mostly for event delegation. + batchNum = 1, + // how many times has start been called without a stop + transactions = 0, + // an array of events within a transaction + batchEvents = [], + stopCallbacks = [], + makeBindSetup = function(wildcard) { + return function() { + var parent = this; + this._each(function(child, prop) { + if (child && child.bind) { + bindToChildAndBubbleToParent(child, wildcard || prop, parent) + } + }) + }; + }; + + + var Observe = can.Map = can.Observe = can.Construct({ + + // keep so it can be overwritten + bind: can.bindAndSetup, + unbind: can.unbindAndTeardown, + id: "id", + canMakeObserve: canMakeObserve, + // starts collecting events + // takes a callback for after they are updated + // how could you hook into after ejs + + startBatch: function(batchStopHandler) { + transactions++; + batchStopHandler && stopCallbacks.push(batchStopHandler); + }, + + stopBatch: function(force, callStart) { + if (force) { + transactions = 0; + } else { + transactions--; + } + + if (transactions == 0) { + var items = batchEvents.slice(0), + callbacks = stopCallbacks.slice(0); + batchEvents = []; + stopCallbacks = []; + batchNum++; + callStart && this.startBatch(); + can.each(items, function(args) { + can.trigger.apply(can, args); + }); + can.each(callbacks, function(cb) { + cb(); + }); + } + }, + + triggerBatch: function(item, event, args) { + // Don't send events if initalizing. + if (!item._init) { + if (transactions == 0) { + return can.trigger(item, event, args); + } else { + event = typeof event === "string" ? { + type: event + } : + event; + event.batchNum = batchNum; + batchEvents.push([ + item, + event, + args + ]); + } + } + }, + + keys: function(observe) { + var keys = []; + Observe.__reading && Observe.__reading(observe, '__keys'); + for (var keyName in observe._data) { + keys.push(keyName); + } + return keys; + } + }, + + { + setup: function(obj) { + // `_data` is where we keep the properties. + this._data = {}; + + // The namespace this `object` uses to listen to events. + can.cid(this, ".observe"); + // Sets all `attrs`. + this._init = 1; + this.attr(obj); + this.bind('change' + this._cid, can.proxy(this._changes, this)); + delete this._init; + }, + _bindsetup: makeBindSetup(), + _bindteardown: function() { + var cid = this._cid; + this._each(function(child) { + unhookup([child], cid) + }) + }, + _changes: function(ev, attr, how, newVal, oldVal) { + Observe.triggerBatch(this, { + type: attr, + batchNum: ev.batchNum + }, [newVal, oldVal]); + }, + _triggerChange: function(attr, how, newVal, oldVal) { + Observe.triggerBatch(this, "change", can.makeArray(arguments)) + }, + // no live binding iterator + _each: function(callback) { + var data = this.__get(); + for (var prop in data) { + if (data.hasOwnProperty(prop)) { + callback(data[prop], prop) + } + } + }, + + attr: function(attr, val) { + // This is super obfuscated for space -- basically, we're checking + // if the type of the attribute is not a `number` or a `string`. + var type = typeof attr; + if (type !== "string" && type !== "number") { + return this._attrs(attr, val) + } else if (val === undefined) { // If we are getting a value. + // Let people know we are reading. + Observe.__reading && Observe.__reading(this, attr) + return this._get(attr) + } else { + // Otherwise we are setting. + this._set(attr, val); + return this; + } + }, + + each: function() { + Observe.__reading && Observe.__reading(this, '__keys'); + return can.each.apply(undefined, [this.__get()].concat(can.makeArray(arguments))) + }, + + removeAttr: function(attr) { + // Info if this is List or not + var isList = this instanceof can.Observe.List, + // Convert the `attr` into parts (if nested). + parts = attrParts(attr), + // The actual property to remove. + prop = parts.shift(), + // The current value. + current = isList ? this[prop] : this._data[prop]; + + // If we have more parts, call `removeAttr` on that part. + if (parts.length) { + return current.removeAttr(parts) + } else { + if (isList) { + this.splice(prop, 1) + } else if (prop in this._data) { + // Otherwise, `delete`. + delete this._data[prop]; + // Create the event. + if (!(prop in this.constructor.prototype)) { + delete this[prop] + } + // Let others know the number of keys have changed + Observe.triggerBatch(this, "__keys"); + this._triggerChange(prop, "remove", undefined, current); + + } + return current; + } + }, + // Reads a property from the `object`. + _get: function(attr) { + var value = typeof attr === 'string' && !! ~attr.indexOf('.') && this.__get(attr); + if (value) { + return value; + } + + // break up the attr (`"foo.bar"`) into `["foo","bar"]` + var parts = attrParts(attr), + // get the value of the first attr name (`"foo"`) + current = this.__get(parts.shift()); + // if there are other attributes to read + return parts.length ? + // and current has a value + current ? + // lookup the remaining attrs on current + current._get(parts) : + // or if there's no current, return undefined + undefined : + // if there are no more parts, return current + current; + }, + // Reads a property directly if an `attr` is provided, otherwise + // returns the "real" data object itself. + __get: function(attr) { + return attr ? this._data[attr] : this._data; + }, + // Sets `attr` prop as value on this object where. + // `attr` - Is a string of properties or an array of property values. + // `value` - The raw value to set. + _set: function(attr, value, keepKey) { + // Convert `attr` to attr parts (if it isn't already). + var parts = attrParts(attr, keepKey), + // The immediate prop we are setting. + prop = parts.shift(), + // The current value. + current = this.__get(prop); + + // If we have an `object` and remaining parts. + if (canMakeObserve(current) && parts.length) { + // That `object` should set it (this might need to call attr). + current._set(parts, value) + } else if (!parts.length) { + // We're in "real" set territory. + if (this.__convert) { + value = this.__convert(prop, value) + } + this.__set(prop, value, current) + } else { + throw "can.Observe: Object does not exist" + } + }, + __set: function(prop, value, current) { + + // Otherwise, we are setting it on this `object`. + // TODO: Check if value is object and transform + // are we changing the value. + if (value !== current) { + // Check if we are adding this for the first time -- + // if we are, we need to create an `add` event. + var changeType = this.__get().hasOwnProperty(prop) ? "set" : "add"; + + // Set the value on data. + this.___set(prop, + + // If we are getting an object. + canMakeObserve(value) ? + + // Hook it up to send event. + hookupBubble(value, prop, this) : + // Value is normal. + value); + + if (changeType == "add") { + // If there is no current value, let others know that + // the the number of keys have changed + + Observe.triggerBatch(this, "__keys", undefined); + + } + // `batchTrigger` the change event. + this._triggerChange(prop, changeType, value, current); + + //Observe.triggerBatch(this, prop, [value, current]); + // If we can stop listening to our old value, do it. + current && unhookup([current], this._cid); + } + + }, + // Directly sets a property on this `object`. + ___set: function(prop, val) { + this._data[prop] = val; + // Add property directly for easy writing. + // Check if its on the `prototype` so we don't overwrite methods like `attrs`. + if (!(prop in this.constructor.prototype)) { + this[prop] = val + } + }, + + + bind: can.bindAndSetup, + + unbind: can.unbindAndTeardown, + + serialize: function() { + return serialize(this, 'serialize', {}); + }, + + _attrs: function(props, remove) { + + if (props === undefined) { + return serialize(this, 'attr', {}) + } + + props = can.extend({}, props); + var prop, + self = this, + newVal; + Observe.startBatch(); + this.each(function(curVal, prop) { + newVal = props[prop]; + + // If we are merging... + if (newVal === undefined) { + remove && self.removeAttr(prop); + return; + } + + if (self.__convert) { + newVal = self.__convert(prop, newVal) + } + + // if we're dealing with models, want to call _set to let converter run + if (newVal instanceof can.Observe) { + self.__set(prop, newVal, curVal) + // if its an object, let attr merge + } else if (canMakeObserve(curVal) && canMakeObserve(newVal) && curVal.attr) { + curVal.attr(newVal, remove) + // otherwise just set + } else if (curVal != newVal) { + self.__set(prop, newVal, curVal) + } + + delete props[prop]; + }) + // Add remaining props. + for (var prop in props) { + newVal = props[prop]; + this._set(prop, newVal, true) + } + Observe.stopBatch() + return this; + }, + + + compute: function(prop) { + return can.compute(this, prop); + } + }); + // Helpers for `observable` lists. + var splice = [].splice, + + list = Observe( + + { + setup: function(instances, options) { + this.length = 0; + can.cid(this, ".observe") + this._init = 1; + if (can.isDeferred(instances)) { + this.replace(instances) + } else { + this.push.apply(this, can.makeArray(instances || [])); + } + // this change needs to be ignored + this.bind('change' + this._cid, can.proxy(this._changes, this)); + can.extend(this, options); + delete this._init; + }, + _triggerChange: function(attr, how, newVal, oldVal) { + + Observe.prototype._triggerChange.apply(this, arguments) + // `batchTrigger` direct add and remove events... + if (!~attr.indexOf('.')) { + + if (how === 'add') { + Observe.triggerBatch(this, how, [newVal, +attr]); + Observe.triggerBatch(this, 'length', [this.length]); + } else if (how === 'remove') { + Observe.triggerBatch(this, how, [oldVal, +attr]); + Observe.triggerBatch(this, 'length', [this.length]); + } else { + Observe.triggerBatch(this, how, [newVal, +attr]) + } + + } + + }, + __get: function(attr) { + return attr ? this[attr] : this; + }, + ___set: function(attr, val) { + this[attr] = val; + if (+attr >= this.length) { + this.length = (+attr + 1) + } + }, + _each: function(callback) { + var data = this.__get(); + for (var i = 0; i < data.length; i++) { + callback(data[i], i) + } + }, + _bindsetup: makeBindSetup("*"), + // Returns the serialized form of this list. + + serialize: function() { + return serialize(this, 'serialize', []); + }, + + splice: function(index, howMany) { + var args = can.makeArray(arguments), + i; + + for (i = 2; i < args.length; i++) { + var val = args[i]; + if (canMakeObserve(val)) { + args[i] = hookupBubble(val, "*", this, this.constructor.Observe, this.constructor) + } + } + if (howMany === undefined) { + howMany = args[1] = this.length - index; + } + var removed = splice.apply(this, args); + can.Observe.startBatch(); + if (howMany > 0) { + this._triggerChange("" + index, "remove", undefined, removed); + unhookup(removed, this._cid); + } + if (args.length > 2) { + this._triggerChange("" + index, "add", args.slice(2), removed); + } + can.Observe.stopBatch(); + return removed; + }, + + _attrs: function(items, remove) { + if (items === undefined) { + return serialize(this, 'attr', []); + } + + // Create a copy. + items = can.makeArray(items); + + Observe.startBatch(); + this._updateAttrs(items, remove); + Observe.stopBatch() + }, + + _updateAttrs: function(items, remove) { + var len = Math.min(items.length, this.length); + + for (var prop = 0; prop < len; prop++) { + var curVal = this[prop], + newVal = items[prop]; + + if (canMakeObserve(curVal) && canMakeObserve(newVal)) { + curVal.attr(newVal, remove) + } else if (curVal != newVal) { + this._set(prop, newVal) + } else { + + } + } + if (items.length > this.length) { + // Add in the remaining props. + this.push.apply(this, items.slice(this.length)); + } else if (items.length < this.length && remove) { + this.splice(items.length) + } + } + }), + + // Converts to an `array` of arguments. + getArgs = function(args) { + return args[0] && can.isArray(args[0]) ? + args[0] : + can.makeArray(args); + }; + // Create `push`, `pop`, `shift`, and `unshift` + can.each({ + + push: "length", + + unshift: 0 + }, + // Adds a method + // `name` - The method name. + // `where` - Where items in the `array` should be added. + + function(where, name) { + var orig = [][name] + list.prototype[name] = function() { + // Get the items being added. + var args = [], + // Where we are going to add items. + len = where ? this.length : 0, + i = arguments.length, + res, + val, + constructor = this.constructor; + + // Go through and convert anything to an `observe` that needs to be converted. + while (i--) { + val = arguments[i]; + args[i] = canMakeObserve(val) ? + hookupBubble(val, "*", this, this.constructor.Observe, this.constructor) : + val; + } + + // Call the original method. + res = orig.apply(this, args); + + if (!this.comparator || args.length) { + + this._triggerChange("" + len, "add", args, undefined); + } + + return res; + } + }); + + can.each({ + + pop: "length", + + shift: 0 + }, + // Creates a `remove` type method + + function(where, name) { + list.prototype[name] = function() { + + var args = getArgs(arguments), + len = where && this.length ? this.length - 1 : 0; + + var res = [][name].apply(this, args) + + // Create a change where the args are + // `len` - Where these items were removed. + // `remove` - Items removed. + // `undefined` - The new values (there are none). + // `res` - The old, removed values (should these be unbound). + this._triggerChange("" + len, "remove", undefined, [res]) + + if (res && res.unbind) { + res.unbind("change" + this._cid) + } + return res; + } + }); + + can.extend(list.prototype, { + + indexOf: function(item) { + this.attr('length') + return can.inArray(item, this) + }, + + + join: [].join, + + + reverse: [].reverse, + + + slice: function() { + var temp = Array.prototype.slice.apply(this, arguments); + return new this.constructor(temp); + }, + + + concat: function() { + var args = []; + can.each(can.makeArray(arguments), function(arg, i) { + args[i] = arg instanceof can.Observe.List ? arg.serialize() : arg; + }); + return new this.constructor(Array.prototype.concat.apply(this.serialize(), args)); + }, + + + forEach: function(cb, thisarg) { + can.each(this, cb, thisarg || this); + }, + + + replace: function(newList) { + if (can.isDeferred(newList)) { + newList.then(can.proxy(this.replace, this)); + } else { + this.splice.apply(this, [0, this.length].concat(can.makeArray(newList || []))); + } + + return this; + } + }); + + can.List = Observe.List = list; + Observe.setup = function() { + can.Construct.setup.apply(this, arguments); + // I would prefer not to do it this way. It should + // be using the attributes plugin to do this type of conversion. + this.List = Observe.List({ + Observe: this + }, {}); + } + return Observe; + })(__m3, __m8, __m1); + + // ## observe/compute/compute.js + var __m9 = (function(can, bind) { + + // returns the + // - observes and attr methods are called by func + // - the value returned by func + // ex: `{value: 100, observed: [{obs: o, attr: "completed"}]}` + var getValueAndObserved = function(func, self) { + + var oldReading; + if (can.Observe) { + // Set a callback on can.Observe to know + // when an attr is read. + // Keep a reference to the old reader + // if there is one. This is used + // for nested live binding. + oldReading = can.Observe.__reading; + can.Observe.__reading = function(obj, attr) { + // Add the observe and attr that was read + // to `observed` + observed.push({ + obj: obj, + attr: attr + "" + }); + }; + } + + var observed = [], + // Call the "wrapping" function to get the value. `observed` + // will have the observe/attribute pairs that were read. + value = func.call(self); + + // Set back so we are no longer reading. + if (can.Observe) { + can.Observe.__reading = oldReading; + } + return { + value: value, + observed: observed + }; + }, + // Calls `callback(newVal, oldVal)` everytime an observed property + // called within `getterSetter` is changed and creates a new result of `getterSetter`. + // Also returns an object that can teardown all event handlers. + computeBinder = function(getterSetter, context, callback, computeState) { + // track what we are observing + var observing = {}, + // a flag indicating if this observe/attr pair is already bound + matched = true, + // the data to return + data = { + // we will maintain the value while live-binding is taking place + value: undefined, + // a teardown method that stops listening + teardown: function() { + for (var name in observing) { + var ob = observing[name]; + ob.observe.obj.unbind(ob.observe.attr, onchanged); + delete observing[name]; + } + } + }, + batchNum; + + // when a property value is changed + var onchanged = function(ev) { + // If the compute is no longer bound (because the same change event led to an unbind) + // then do not call getValueAndBind, or we will leak bindings. + if (computeState && !computeState.bound) { + return; + } + if (ev.batchNum === undefined || ev.batchNum !== batchNum) { + // store the old value + var oldValue = data.value, + // get the new value + newvalue = getValueAndBind(); + + // update the value reference (in case someone reads) + data.value = newvalue; + // if a change happened + if (newvalue !== oldValue) { + callback(newvalue, oldValue); + } + batchNum = batchNum = ev.batchNum; + } + + + }; + + // gets the value returned by `getterSetter` and also binds to any attributes + // read by the call + var getValueAndBind = function() { + var info = getValueAndObserved(getterSetter, context), + newObserveSet = info.observed; + + var value = info.value; + matched = !matched; + + // go through every attribute read by this observe + can.each(newObserveSet, function(ob) { + // if the observe/attribute pair is being observed + if (observing[ob.obj._cid + "|" + ob.attr]) { + // mark at as observed + observing[ob.obj._cid + "|" + ob.attr].matched = matched; + } else { + // otherwise, set the observe/attribute on oldObserved, marking it as being observed + observing[ob.obj._cid + "|" + ob.attr] = { + matched: matched, + observe: ob + }; + ob.obj.bind(ob.attr, onchanged); + } + }); + + // Iterate through oldObserved, looking for observe/attributes + // that are no longer being bound and unbind them + for (var name in observing) { + var ob = observing[name]; + if (ob.matched !== matched) { + ob.observe.obj.unbind(ob.observe.attr, onchanged); + delete observing[name]; + } + } + return value; + }; + // set the initial value + data.value = getValueAndBind(); + + data.isListening = !can.isEmptyObject(observing); + return data; + } + + // if no one is listening ... we can not calculate every time + + can.compute = function(getterSetter, context, eventName) { + if (getterSetter && getterSetter.isComputed) { + return getterSetter; + } + // stores the result of computeBinder + var computedData, + // how many listeners to this this compute + bindings = 0, + // the computed object + computed, + // an object that keeps track if the computed is bound + // onchanged needs to know this. It's possible a change happens and results in + // something that unbinds the compute, it needs to not to try to recalculate who it + // is listening to + computeState = { + bound: false, + // true if this compute is calculated from other computes and observes + hasDependencies: false + }, + // The following functions are overwritten depending on how compute() is called + // a method to setup listening + on = function() {}, + // a method to teardown listening + off = function() {}, + // the current cached value (only valid if bound = true) + value, + // how to read the value + get = function() { + return value + }, + // sets the value + set = function(newVal) { + value = newVal; + }, + // this compute can be a dependency of other computes + canReadForChangeEvent = true; + + computed = function(newVal) { + // setting ... + if (arguments.length) { + // save a reference to the old value + var old = value; + + // setter may return a value if + // setter is for a value maintained exclusively by this compute + var setVal = set.call(context, newVal, old); + + // if this has dependencies return the current value + if (computed.hasDependencies) { + return get.call(context); + } + + if (setVal === undefined) { + // it's possible, like with the DOM, setting does not + // fire a change event, so we must read + value = get.call(context); + } else { + value = setVal; + } + // fire the change + if (old !== value) { + can.Observe.triggerBatch(computed, "change", [value, old]); + } + return value; + } else { + // Let others know to listen to changes in this compute + if (can.Observe.__reading && canReadForChangeEvent) { + can.Observe.__reading(computed, 'change'); + } + // if we are bound, use the cached value + if (computeState.bound) { + return value; + } else { + return get.call(context); + } + } + } + if (typeof getterSetter === "function") { + set = getterSetter; + get = getterSetter; + canReadForChangeEvent = eventName === false ? false : true; + computed.hasDependencies = false; + on = function(update) { + computedData = computeBinder(getterSetter, context || this, update, computeState); + computed.hasDependencies = computedData.isListening + value = computedData.value; + } + off = function() { + computedData.teardown(); + } + } else if (context) { + + if (typeof context == "string") { + // `can.compute(obj, "propertyName", [eventName])` + + var propertyName = context, + isObserve = getterSetter instanceof can.Observe; + if (isObserve) { + computed.hasDependencies = true; + } + get = function() { + if (isObserve) { + return getterSetter.attr(propertyName); + } else { + return getterSetter[propertyName]; + } + } + set = function(newValue) { + if (isObserve) { + getterSetter.attr(propertyName, newValue) + } else { + getterSetter[propertyName] = newValue; + } + } + var handler; + on = function(update) { + handler = function() { + update(get(), value) + }; + can.bind.call(getterSetter, eventName || propertyName, handler) + + // use getValueAndObserved because + // we should not be indicating that some parent + // reads this property if it happens to be binding on it + value = getValueAndObserved(get).value + } + off = function() { + can.unbind.call(getterSetter, eventName || propertyName, handler) + } + + } else { + // `can.compute(initialValue, setter)` + if (typeof context === "function") { + value = getterSetter; + set = context; + } else { + // `can.compute(initialValue,{get:, set:, on:, off:})` + value = getterSetter; + var options = context; + get = options.get || get; + set = options.set || set; + on = options.on || on; + off = options.off || off; + } + + } + + + + } else { + // `can.compute(5)` + value = getterSetter; + } + + computed.isComputed = true; + + can.cid(computed, "compute") + + var updater = function(newValue, oldValue) { + value = newValue; + // might need a way to look up new and oldVal + can.Observe.triggerBatch(computed, "change", [newValue, oldValue]) + } + + return can.extend(computed, { + _bindsetup: function() { + computeState.bound = true; + // setup live-binding + on.call(this, updater) + }, + _bindteardown: function() { + off.call(this, updater) + computeState.bound = false; + }, + + bind: can.bindAndSetup, + + unbind: can.unbindAndTeardown + }); + }; + can.compute.binder = computeBinder; + return can.compute; + })(__m3, __m8); + + // ## model/model.js + var __m10 = (function(can) { + + // ## model.js + // `can.Model` + // _A `can.Observe` that connects to a RESTful interface._ + // Generic deferred piping function + + var pipe = function(def, model, func) { + var d = new can.Deferred(); + def.then(function() { + var args = can.makeArray(arguments); + args[0] = model[func](args[0]); + d.resolveWith(d, args); + }, function() { + d.rejectWith(this, arguments); + }); + + if (typeof def.abort === 'function') { + d.abort = function() { + return def.abort(); + } + } + + return d; + }, + modelNum = 0, + ignoreHookup = /change.observe\d+/, + getId = function(inst) { + // Instead of using attr, use __get for performance. + // Need to set reading + can.Observe.__reading && can.Observe.__reading(inst, inst.constructor.id) + return inst.__get(inst.constructor.id); + }, + // Ajax `options` generator function + ajax = function(ajaxOb, data, type, dataType, success, error) { + + var params = {}; + + // If we get a string, handle it. + if (typeof ajaxOb == "string") { + // If there's a space, it's probably the type. + var parts = ajaxOb.split(/\s+/); + params.url = parts.pop(); + if (parts.length) { + params.type = parts.pop(); + } + } else { + can.extend(params, ajaxOb); + } + + // If we are a non-array object, copy to a new attrs. + params.data = typeof data == "object" && !can.isArray(data) ? + can.extend(params.data || {}, data) : data; + + // Get the url with any templated values filled out. + params.url = can.sub(params.url, params.data, true); + + return can.ajax(can.extend({ + type: type || "post", + dataType: dataType || "json", + success: success, + error: error + }, params)); + }, + makeRequest = function(self, type, success, error, method) { + var args; + // if we pass an array as `self` it it means we are coming from + // the queued request, and we're passing already serialized data + // self's signature will be: [self, serializedData] + if (can.isArray(self)) { + args = self[1]; + self = self[0]; + } else { + args = self.serialize(); + } + args = [args]; + var deferred, + // The model. + model = self.constructor, + jqXHR; + + // `destroy` does not need data. + if (type == 'destroy') { + args.shift(); + } + // `update` and `destroy` need the `id`. + if (type !== 'create') { + args.unshift(getId(self)); + } + + + jqXHR = model[type].apply(model, args); + + deferred = jqXHR.pipe(function(data) { + self[method || type + "d"](data, jqXHR); + return self; + }); + + // Hook up `abort` + if (jqXHR.abort) { + deferred.abort = function() { + jqXHR.abort(); + }; + } + + deferred.then(success, error); + return deferred; + }, + + // This object describes how to make an ajax request for each ajax method. + // The available properties are: + // `url` - The default url to use as indicated as a property on the model. + // `type` - The default http request type + // `data` - A method that takes the `arguments` and returns `data` used for ajax. + + ajaxMethods = { + + create: { + url: "_shortName", + type: "post" + }, + + update: { + data: function(id, attrs) { + attrs = attrs || {}; + var identity = this.id; + if (attrs[identity] && attrs[identity] !== id) { + attrs["new" + can.capitalize(id)] = attrs[identity]; + delete attrs[identity]; + } + attrs[identity] = id; + return attrs; + }, + type: "put" + }, + + destroy: { + type: "delete", + data: function(id) { + var args = {}; + args.id = args[this.id] = id; + return args; + } + }, + + findAll: { + url: "_shortName" + }, + + findOne: {} + }, + // Makes an ajax request `function` from a string. + // `ajaxMethod` - The `ajaxMethod` object defined above. + // `str` - The string the user provided. Ex: `findAll: "/recipes.json"`. + ajaxMaker = function(ajaxMethod, str) { + // Return a `function` that serves as the ajax method. + return function(data) { + // If the ajax method has it's own way of getting `data`, use that. + data = ajaxMethod.data ? + ajaxMethod.data.apply(this, arguments) : + // Otherwise use the data passed in. + data; + // Return the ajax method with `data` and the `type` provided. + return ajax(str || this[ajaxMethod.url || "_url"], data, ajaxMethod.type || "get") + } + } + + + + can.Model = can.Observe({ + fullName: "can.Model", + _reqs: 0, + setup: function(base) { + // create store here if someone wants to use model without inheriting from it + this.store = {}; + can.Observe.setup.apply(this, arguments); + // Set default list as model list + if (!can.Model) { + return; + } + this.List = ML({ + Observe: this + }, {}); + var self = this, + clean = can.proxy(this._clean, self); + + + // go through ajax methods and set them up + can.each(ajaxMethods, function(method, name) { + // if an ajax method is not a function, it's either + // a string url like findAll: "/recipes" or an + // ajax options object like {url: "/recipes"} + if (!can.isFunction(self[name])) { + // use ajaxMaker to convert that into a function + // that returns a deferred with the data + self[name] = ajaxMaker(method, self[name]); + } + // check if there's a make function like makeFindAll + // these take deferred function and can do special + // behavior with it (like look up data in a store) + if (self["make" + can.capitalize(name)]) { + // pass the deferred method to the make method to get back + // the "findAll" method. + var newMethod = self["make" + can.capitalize(name)](self[name]); + can.Construct._overwrite(self, base, name, function() { + // increment the numer of requests + can.Model._reqs++; + var def = newMethod.apply(this, arguments); + var then = def.then(clean, clean); + then.abort = def.abort; + + // attach abort to our then and return it + return then; + }) + } + }); + + if (self.fullName == "can.Model" || !self.fullName) { + self.fullName = "Model" + (++modelNum); + } + // Add ajax converters. + can.Model._reqs = 0; + this._url = this._shortName + "/{" + this.id + "}" + }, + _ajax: ajaxMaker, + _makeRequest: makeRequest, + _clean: function() { + can.Model._reqs--; + if (!can.Model._reqs) { + for (var id in this.store) { + if (!this.store[id]._bindings) { + delete this.store[id]; + } + } + } + return arguments[0]; + }, + + models: function(instancesRawData, oldList) { + // until "end of turn", increment reqs counter so instances will be added to the store + can.Model._reqs++; + if (!instancesRawData) { + return; + } + + if (instancesRawData instanceof this.List) { + return instancesRawData; + } + + // Get the list type. + var self = this, + tmp = [], + res = oldList instanceof can.Observe.List ? oldList : new(self.List || ML), + // Did we get an `array`? + arr = can.isArray(instancesRawData), + + // Did we get a model list? + ml = (instancesRawData instanceof ML), + + // Get the raw `array` of objects. + raw = arr ? + + // If an `array`, return the `array`. + instancesRawData : + + // Otherwise if a model list. + (ml ? + + // Get the raw objects from the list. + instancesRawData.serialize() : + + // Get the object's data. + instancesRawData.data), + i = 0; + + + + if (res.length) { + res.splice(0); + } + + can.each(raw, function(rawPart) { + tmp.push(self.model(rawPart)); + }); + + // We only want one change event so push everything at once + res.push.apply(res, tmp); + + if (!arr) { // Push other stuff onto `array`. + can.each(instancesRawData, function(val, prop) { + if (prop !== 'data') { + res.attr(prop, val); + } + }) + } + // at "end of turn", clean up the store + setTimeout(can.proxy(this._clean, this), 1); + return res; + }, + + model: function(attributes) { + if (!attributes) { + return; + } + if (attributes instanceof this) { + attributes = attributes.serialize(); + } + var id = attributes[this.id], + model = (id || id === 0) && this.store[id] ? + this.store[id].attr(attributes, this.removeAttr || false) : new this(attributes); + if (can.Model._reqs) { + this.store[attributes[this.id]] = model; + } + return model; + } + }, + + + { + + isNew: function() { + var id = getId(this); + return !(id || id === 0); // If `null` or `undefined` + }, + + save: function(success, error) { + return makeRequest(this, this.isNew() ? 'create' : 'update', success, error); + }, + + destroy: function(success, error) { + if (this.isNew()) { + var self = this; + var def = can.Deferred(); + def.then(success, error); + return def.done(function(data) { + self.destroyed(data) + }).resolve(self); + } + return makeRequest(this, 'destroy', success, error, 'destroyed'); + }, + + _bindsetup: function() { + this.constructor.store[this.__get(this.constructor.id)] = this; + return can.Observe.prototype._bindsetup.apply(this, arguments); + }, + + _bindteardown: function() { + delete this.constructor.store[getId(this)]; + return can.Observe.prototype._bindteardown.apply(this, arguments) + }, + // Change `id`. + ___set: function(prop, val) { + can.Observe.prototype.___set.call(this, prop, val) + // If we add an `id`, move it to the store. + if (prop === this.constructor.id && this._bindings) { + this.constructor.store[getId(this)] = this; + } + } + }); + + can.each({ + makeFindAll: "models", + makeFindOne: "model", + makeCreate: "model", + makeUpdate: "model" + }, function(method, name) { + can.Model[name] = function(oldMethod) { + return function() { + var args = can.makeArray(arguments), + oldArgs = can.isFunction(args[1]) ? args.splice(0, 1) : args.splice(0, 2), + def = pipe(oldMethod.apply(this, oldArgs), this, method); + def.then(args[0], args[1]); + // return the original promise + return def; + }; + }; + }); + + can.each([ + + "created", + + "updated", + + "destroyed" + ], function(funcName) { + can.Model.prototype[funcName] = function(attrs) { + var stub, + constructor = this.constructor; + + // Update attributes if attributes have been passed + stub = attrs && typeof attrs == 'object' && this.attr(attrs.attr ? attrs.attr() : attrs); + + // triggers change event that bubble's like + // handler( 'change','1.destroyed' ). This is used + // to remove items on destroyed from Model Lists. + // but there should be a better way. + can.trigger(this, "change", funcName) + + + // Call event on the instance's Class + can.trigger(constructor, funcName, this); + }; + }); + + // Model lists are just like `Observe.List` except that when their items are + // destroyed, it automatically gets removed from the list. + + var ML = can.Model.List = can.Observe.List({ + setup: function(params) { + if (can.isPlainObject(params) && !can.isArray(params)) { + can.Observe.List.prototype.setup.apply(this); + this.replace(this.constructor.Observe.findAll(params)) + } else { + can.Observe.List.prototype.setup.apply(this, arguments); + } + }, + _changes: function(ev, attr) { + can.Observe.List.prototype._changes.apply(this, arguments); + if (/\w+\.destroyed/.test(attr)) { + var index = this.indexOf(ev.target); + if (index != -1) { + this.splice(index, 1); + } + } + } + }) + + return can.Model; + })(__m3, __m7); + + // ## view/view.js + var __m11 = (function(can) { + // ## view.js + // `can.view` + // _Templating abstraction._ + + var isFunction = can.isFunction, + makeArray = can.makeArray, + // Used for hookup `id`s. + hookupId = 1, + + $view = can.view = can.template = function(view, data, helpers, callback) { + // If helpers is a `function`, it is actually a callback. + if (isFunction(helpers)) { + callback = helpers; + helpers = undefined; + } + + var pipe = function(result) { + return $view.frag(result); + }, + // In case we got a callback, we need to convert the can.view.render + // result to a document fragment + wrapCallback = isFunction(callback) ? function(frag) { + callback(pipe(frag)); + } : null, + // Get the result. + result = $view.render(view, data, helpers, wrapCallback), + deferred = can.Deferred(); + + if (isFunction(result)) { + return result; + } + + if (can.isDeferred(result)) { + result.then(function(result, data) { + deferred.resolve.call(deferred, pipe(result), data); + }, function() { + deferred.fail.apply(deferred, arguments); + }); + return deferred; + } + + // Convert it into a dom frag. + return pipe(result); + }; + + can.extend($view, { + // creates a frag and hooks it up all at once + frag: function(result, parentNode) { + return $view.hookup($view.fragment(result), parentNode); + }, + + // simply creates a frag + // this is used internally to create a frag + // insert it + // then hook it up + fragment: function(result) { + var frag = can.buildFragment(result, document.body); + // If we have an empty frag... + if (!frag.childNodes.length) { + frag.appendChild(document.createTextNode('')); + } + return frag; + }, + + // Convert a path like string into something that's ok for an `element` ID. + toId: function(src) { + return can.map(src.toString().split(/\/|\./g), function(part) { + // Dont include empty strings in toId functions + if (part) { + return part; + } + }).join("_"); + }, + + hookup: function(fragment, parentNode) { + var hookupEls = [], + id, + func; + + // Get all `childNodes`. + can.each(fragment.childNodes ? can.makeArray(fragment.childNodes) : fragment, function(node) { + if (node.nodeType === 1) { + hookupEls.push(node); + hookupEls.push.apply(hookupEls, can.makeArray(node.getElementsByTagName('*'))); + } + }); + + // Filter by `data-view-id` attribute. + can.each(hookupEls, function(el) { + if (el.getAttribute && (id = el.getAttribute('data-view-id')) && (func = $view.hookups[id])) { + func(el, parentNode, id); + delete $view.hookups[id]; + el.removeAttribute('data-view-id'); + } + }); + + return fragment; + }, + + + hookups: {}, + + + hook: function(cb) { + $view.hookups[++hookupId] = cb; + return " data-view-id='" + hookupId + "'"; + }, + + + cached: {}, + + cachedRenderers: {}, + + + cache: true, + + + register: function(info) { + this.types["." + info.suffix] = info; + }, + + types: {}, + + + ext: ".ejs", + + + registerScript: function() {}, + + + preload: function() {}, + + + render: function(view, data, helpers, callback) { + // If helpers is a `function`, it is actually a callback. + if (isFunction(helpers)) { + callback = helpers; + helpers = undefined; + } + + // See if we got passed any deferreds. + var deferreds = getDeferreds(data); + + if (deferreds.length) { // Does data contain any deferreds? + // The deferred that resolves into the rendered content... + var deferred = new can.Deferred(), + dataCopy = can.extend({}, data); + + // Add the view request to the list of deferreds. + deferreds.push(get(view, true)) + + // Wait for the view and all deferreds to finish... + can.when.apply(can, deferreds).then(function(resolved) { + // Get all the resolved deferreds. + var objs = makeArray(arguments), + // Renderer is the last index of the data. + renderer = objs.pop(), + // The result of the template rendering with data. + result; + + // Make data look like the resolved deferreds. + if (can.isDeferred(data)) { + dataCopy = usefulPart(resolved); + } else { + // Go through each prop in data again and + // replace the defferreds with what they resolved to. + for (var prop in data) { + if (can.isDeferred(data[prop])) { + dataCopy[prop] = usefulPart(objs.shift()); + } + } + } + + // Get the rendered result. + result = renderer(dataCopy, helpers); + + // Resolve with the rendered view. + deferred.resolve(result, dataCopy); + + // If there's a `callback`, call it back with the result. + callback && callback(result, dataCopy); + }, function() { + deferred.reject.apply(deferred, arguments) + }); + // Return the deferred... + return deferred; + } else { + // No deferreds! Render this bad boy. + var response, + // If there's a `callback` function + async = isFunction(callback), + // Get the `view` type + deferred = get(view, async); + + // If we are `async`... + if (async) { + // Return the deferred + response = deferred; + // And fire callback with the rendered result. + deferred.then(function(renderer) { + callback(data ? renderer(data, helpers) : renderer); + }) + } else { + // if the deferred is resolved, call the cached renderer instead + // this is because it's possible, with recursive deferreds to + // need to render a view while its deferred is _resolving_. A _resolving_ deferred + // is a deferred that was just resolved and is calling back it's success callbacks. + // If a new success handler is called while resoliving, it does not get fired by + // jQuery's deferred system. So instead of adding a new callback + // we use the cached renderer. + // We also add __view_id on the deferred so we can look up it's cached renderer. + // In the future, we might simply store either a deferred or the cached result. + if (deferred.state() === "resolved" && deferred.__view_id) { + var currentRenderer = $view.cachedRenderers[deferred.__view_id]; + return data ? currentRenderer(data, helpers) : currentRenderer; + } else { + // Otherwise, the deferred is complete, so + // set response to the result of the rendering. + deferred.then(function(renderer) { + response = data ? renderer(data, helpers) : renderer; + }); + } + } + + return response; + } + }, + + + registerView: function(id, text, type, def) { + // Get the renderer function. + var func = (type || $view.types[$view.ext]).renderer(id, text); + def = def || new can.Deferred(); + + // Cache if we are caching. + if ($view.cache) { + $view.cached[id] = def; + def.__view_id = id; + $view.cachedRenderers[id] = func; + } + + // Return the objects for the response's `dataTypes` + // (in this case view). + return def.resolve(func); + } + }); + + // Makes sure there's a template, if not, have `steal` provide a warning. + var checkText = function(text, url) { + if (!text.length) { + + throw "can.view: No template or empty template:" + url; + } + }, + // `Returns a `view` renderer deferred. + // `url` - The url to the template. + // `async` - If the ajax request should be asynchronous. + // Returns a deferred. + get = function(url, async) { + var suffix = url.match(/\.[\w\d]+$/), + type, + // If we are reading a script element for the content of the template, + // `el` will be set to that script element. + el, + // A unique identifier for the view (used for caching). + // This is typically derived from the element id or + // the url for the template. + id, + // The ajax request used to retrieve the template content. + jqXHR; + + //If the url has a #, we assume we want to use an inline template + //from a script element and not current page's HTML + if (url.match(/^#/)) { + url = url.substr(1); + } + // If we have an inline template, derive the suffix from the `text/???` part. + // This only supports `<script>` tags. + if (el = document.getElementById(url)) { + suffix = "." + el.type.match(/\/(x\-)?(.+)/)[2]; + } + + // If there is no suffix, add one. + if (!suffix && !$view.cached[url]) { + url += (suffix = $view.ext); + } + + if (can.isArray(suffix)) { + suffix = suffix[0] + } + + // Convert to a unique and valid id. + id = $view.toId(url); + + // If an absolute path, use `steal` to get it. + // You should only be using `//` if you are using `steal`. + if (url.match(/^\/\//)) { + var sub = url.substr(2); + url = !window.steal ? + sub : + steal.config().root.mapJoin("" + steal.id(sub)); + } + + // Set the template engine type. + type = $view.types[suffix]; + + // If it is cached, + if ($view.cached[id]) { + // Return the cached deferred renderer. + return $view.cached[id]; + + // Otherwise if we are getting this from a `<script>` element. + } else if (el) { + // Resolve immediately with the element's `innerHTML`. + return $view.registerView(id, el.innerHTML, type); + } else { + // Make an ajax request for text. + var d = new can.Deferred(); + can.ajax({ + async: async, + url: url, + dataType: "text", + error: function(jqXHR) { + checkText("", url); + d.reject(jqXHR); + }, + success: function(text) { + // Make sure we got some text back. + checkText(text, url); + $view.registerView(id, text, type, d) + } + }); + return d; + } + }, + // Gets an `array` of deferreds from an `object`. + // This only goes one level deep. + getDeferreds = function(data) { + var deferreds = []; + + // pull out deferreds + if (can.isDeferred(data)) { + return [data] + } else { + for (var prop in data) { + if (can.isDeferred(data[prop])) { + deferreds.push(data[prop]); + } + } + } + return deferreds; + }, + // Gets the useful part of a resolved deferred. + // This is for `model`s and `can.ajax` that resolve to an `array`. + usefulPart = function(resolved) { + return can.isArray(resolved) && resolved[1] === 'success' ? resolved[0] : resolved + }; + + //!steal-pluginify-remove-start + if (window.steal) { + steal.type("view js", function(options, success, error) { + var type = $view.types["." + options.type], + id = $view.toId(options.id); + + options.text = "steal('" + (type.plugin || "can/view/" + options.type) + "',function(can){return " + "can.view.preload('" + id + "'," + options.text + ");\n})"; + success(); + }) + } + //!steal-pluginify-remove-end + + can.extend($view, { + register: function(info) { + this.types["." + info.suffix] = info; + + //!steal-pluginify-remove-start + if (window.steal) { + steal.type(info.suffix + " view js", function(options, success, error) { + var type = $view.types["." + options.type], + id = $view.toId(options.id + ''); + + options.text = type.script(id, options.text) + success(); + }) + }; + //!steal-pluginify-remove-end + + $view[info.suffix] = function(id, text) { + if (!text) { + // Return a nameless renderer + var renderer = function() { + return $view.frag(renderer.render.apply(this, arguments)); + } + renderer.render = function() { + var renderer = info.renderer(null, id); + return renderer.apply(renderer, arguments); + } + return renderer; + } + + $view.preload(id, info.renderer(id, text)); + return can.view(id); + } + }, + registerScript: function(type, id, src) { + return "can.view.preload('" + id + "'," + $view.types["." + type].script(id, src) + ");"; + }, + preload: function(id, renderer) { + $view.cached[id] = new can.Deferred().resolve(function(data, helpers) { + return renderer.call(data, data, helpers); + }); + + function frag() { + return $view.frag(renderer.apply(this, arguments)); + } + // expose the renderer for mustache + frag.render = renderer; + return frag; + } + + }); + + return can; + })(__m3); + + // ## view/elements.js + var __m14 = (function() { + + var elements = { + tagToContentPropMap: { + option: "textContent" in document.createElement("option") ? "textContent" : "innerText", + textarea: "value" + }, + + attrMap: { + "class": "className", + "value": "value", + "innerText": "innerText", + "textContent": "textContent", + "checked": true, + "disabled": true, + "readonly": true, + "required": true + }, + // elements whos default value we should set + defaultValue: ["input", "textarea"], + // a map of parent element to child elements + tagMap: { + "": "span", + table: "tbody", + tr: "td", + ol: "li", + ul: "li", + tbody: "tr", + thead: "tr", + tfoot: "tr", + select: "option", + optgroup: "option" + }, + // a tag's parent element + reverseTagMap: { + tr: "tbody", + option: "select", + td: "tr", + th: "tr", + li: "ul" + }, + + getParentNode: function(el, defaultParentNode) { + return defaultParentNode && el.parentNode.nodeType === 11 ? defaultParentNode : el.parentNode; + }, + // set an attribute on an element + setAttr: function(el, attrName, val) { + var tagName = el.nodeName.toString().toLowerCase(), + prop = elements.attrMap[attrName]; + // if this is a special property + if (prop === true) { + el[attrName] = true; + } else if (prop) { + // set the value as true / false + el[prop] = val; + if (prop === "value" && can.inArray(tagName, elements.defaultValue) >= 0) { + el.defaultValue = val; + } + } else { + el.setAttribute(attrName, val); + } + }, + // gets the value of an attribute + getAttr: function(el, attrName) { + // Default to a blank string for IE7/8 + return (elements.attrMap[attrName] && el[elements.attrMap[attrName]] ? + el[elements.attrMap[attrName]] : + el.getAttribute(attrName)) || ''; + }, + // removes the attribute + removeAttr: function(el, attrName) { + if (elements.attrMap[attrName] === true) { + el[attrName] = false; + } else { + el.removeAttribute(attrName); + } + }, + contentText: function(text) { + if (typeof text == 'string') { + return text; + } + // If has no value, return an empty string. + if (!text && text !== 0) { + return ''; + } + return "" + text; + } + }; + + return elements; + })(); + + // ## view/scanner.js + var __m13 = (function(can, elements) { + + var newLine = /(\r|\n)+/g, + // Escapes characters starting with `\`. + clean = function(content) { + return content + .split('\\').join("\\\\") + .split("\n").join("\\n") + .split('"').join('\\"') + .split("\t").join("\\t"); + }, + // Returns a tagName to use as a temporary placeholder for live content + // looks forward ... could be slow, but we only do it when necessary + getTag = function(tagName, tokens, i) { + // if a tagName is provided, use that + if (tagName) { + return tagName; + } else { + // otherwise go searching for the next two tokens like "<",TAG + while (i < tokens.length) { + if (tokens[i] == "<" && elements.reverseTagMap[tokens[i + 1]]) { + return elements.reverseTagMap[tokens[i + 1]]; + } + i++; + } + } + return ''; + }, + bracketNum = function(content) { + return (--content.split("{").length) - (--content.split("}").length); + }, + myEval = function(script) { + try { + eval(script); + } catch(e) { + console.log(script) + } + }, + attrReg = /([^\s]+)[\s]*=[\s]*$/, + // Commands for caching. + startTxt = 'var ___v1ew = [];', + finishTxt = "return ___v1ew.join('')", + put_cmd = "___v1ew.push(", + insert_cmd = put_cmd, + // Global controls (used by other functions to know where we are). + // Are we inside a tag? + htmlTag = null, + // Are we within a quote within a tag? + quote = null, + // What was the text before the current quote? (used to get the `attr` name) + beforeQuote = null, + // Whether a rescan is in progress + rescan = null, + // Used to mark where the element is. + status = function() { + // `t` - `1`. + // `h` - `0`. + // `q` - String `beforeQuote`. + return quote ? "'" + beforeQuote.match(attrReg)[1] + "'" : (htmlTag ? 1 : 0); + }; + + can.view.Scanner = Scanner = function(options) { + // Set options on self + can.extend(this, { + text: {}, + tokens: [] + }, options); + + // Cache a token lookup + this.tokenReg = []; + this.tokenSimple = { + "<": "<", + ">": ">", + '"': '"', + "'": "'" + }; + this.tokenComplex = []; + this.tokenMap = {}; + for (var i = 0, token; token = this.tokens[i]; i++) { + + + // Save complex mappings (custom regexp) + if (token[2]) { + this.tokenReg.push(token[2]); + this.tokenComplex.push({ + abbr: token[1], + re: new RegExp(token[2]), + rescan: token[3] + }); + } + // Save simple mappings (string only, no regexp) + else { + this.tokenReg.push(token[1]); + this.tokenSimple[token[1]] = token[0]; + } + this.tokenMap[token[0]] = token[1]; + } + + // Cache the token registry. + this.tokenReg = new RegExp("(" + this.tokenReg.slice(0).concat(["<", ">", '"', "'"]).join("|") + ")", "g"); + }; + + Scanner.prototype = { + + helpers: [ + + { + name: /\s*\(([\$\w]+)\)\s*->([^\n]*)/, + fn: function(content) { + var quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/, + parts = content.match(quickFunc); + + return "can.proxy(function(__){var " + parts[1] + "=can.$(__);" + parts[2] + "}, this);"; + } + } + ], + + scan: function(source, name) { + var tokens = [], + last = 0, + simple = this.tokenSimple, + complex = this.tokenComplex; + + source = source.replace(newLine, "\n"); + if (this.transform) { + source = this.transform(source); + } + source.replace(this.tokenReg, function(whole, part) { + // offset is the second to last argument + var offset = arguments[arguments.length - 2]; + + // if the next token starts after the last token ends + // push what's in between + if (offset > last) { + tokens.push(source.substring(last, offset)); + } + + // push the simple token (if there is one) + if (simple[whole]) { + tokens.push(whole); + } + // otherwise lookup complex tokens + else { + for (var i = 0, token; token = complex[i]; i++) { + if (token.re.test(whole)) { + tokens.push(token.abbr); + // Push a rescan function if one exists + if (token.rescan) { + tokens.push(token.rescan(part)); + } + break; + } + } + } + + // update the position of the last part of the last token + last = offset + part.length; + }); + + // if there's something at the end, add it + if (last < source.length) { + tokens.push(source.substr(last)); + } + + var content = '', + buff = [startTxt + (this.text.start || '')], + // Helper `function` for putting stuff in the view concat. + put = function(content, bonus) { + buff.push(put_cmd, '"', clean(content), '"' + (bonus || '') + ');'); + }, + // A stack used to keep track of how we should end a bracket + // `}`. + // Once we have a `<%= %>` with a `leftBracket`, + // we store how the file should end here (either `))` or `;`). + endStack = [], + // The last token, used to remember which tag we are in. + lastToken, + // The corresponding magic tag. + startTag = null, + // Was there a magic tag inside an html tag? + magicInTag = false, + // The current tag name. + tagName = '', + // stack of tagNames + tagNames = [], + // Pop from tagNames? + popTagName = false, + // Declared here. + bracketCount, + i = 0, + token, + tmap = this.tokenMap; + + // Reinitialize the tag state goodness. + htmlTag = quote = beforeQuote = null; + + for (; + (token = tokens[i++]) !== undefined;) { + if (startTag === null) { + switch (token) { + case tmap.left: + case tmap.escapeLeft: + case tmap.returnLeft: + magicInTag = htmlTag && 1; + case tmap.commentLeft: + // A new line -- just add whatever content within a clean. + // Reset everything. + startTag = token; + if (content.length) { + put(content); + } + content = ''; + break; + case tmap.escapeFull: + // This is a full line escape (a line that contains only whitespace and escaped logic) + // Break it up into escape left and right + magicInTag = htmlTag && 1; + rescan = 1; + startTag = tmap.escapeLeft; + if (content.length) { + put(content); + } + rescan = tokens[i++]; + content = rescan.content || rescan; + if (rescan.before) { + put(rescan.before); + } + tokens.splice(i, 0, tmap.right); + break; + case tmap.commentFull: + // Ignore full line comments. + break; + case tmap.templateLeft: + content += tmap.left; + break; + case '<': + // Make sure we are not in a comment. + if (tokens[i].indexOf("!--") !== 0) { + htmlTag = 1; + magicInTag = 0; + } + content += token; + break; + case '>': + htmlTag = 0; + // content.substr(-1) doesn't work in IE7/8 + var emptyElement = content.substr(content.length - 1) == "/" || content.substr(content.length - 2) == "--"; + // if there was a magic tag + // or it's an element that has text content between its tags, + // but content is not other tags add a hookup + // TODO: we should only add `can.EJS.pending()` if there's a magic tag + // within the html tags. + if (magicInTag || !popTagName && elements.tagToContentPropMap[tagNames[tagNames.length - 1]]) { + // make sure / of /> is on the left of pending + if (emptyElement) { + put(content.substr(0, content.length - 1), ",can.view.pending(),\"/>\""); + } else { + put(content, ",can.view.pending(),\">\""); + } + content = ''; + magicInTag = 0; + } else { + content += token; + } + // if it's a tag like <input/> + if (emptyElement || popTagName) { + // remove the current tag in the stack + tagNames.pop(); + // set the current tag to the previous parent + tagName = tagNames[tagNames.length - 1]; + // Don't pop next time + popTagName = false; + } + break; + case "'": + case '"': + // If we are in an html tag, finding matching quotes. + if (htmlTag) { + // We have a quote and it matches. + if (quote && quote === token) { + // We are exiting the quote. + quote = null; + // Otherwise we are creating a quote. + // TODO: does this handle `\`? + } else if (quote === null) { + quote = token; + beforeQuote = lastToken; + } + } + default: + // Track the current tag + if (lastToken === '<') { + tagName = token.split(/\s/)[0]; + if (tagName.indexOf("/") === 0 && tagNames[tagNames.length - 1] === tagName.substr(1)) { + // set tagName to the last tagName + // if there are no more tagNames, we'll rely on getTag. + tagName = tagNames[tagNames.length - 1]; + popTagName = true; + } else { + tagNames.push(tagName); + } + } + content += token; + break; + } + } else { + // We have a start tag. + switch (token) { + case tmap.right: + case tmap.returnRight: + switch (startTag) { + case tmap.left: + // Get the number of `{ minus }` + bracketCount = bracketNum(content); + + // We are ending a block. + if (bracketCount == 1) { + + // We are starting on. + buff.push(insert_cmd, "can.view.txt(0,'" + getTag(tagName, tokens, i) + "'," + status() + ",this,function(){", startTxt, content); + + endStack.push({ + before: "", + after: finishTxt + "}));\n" + }); + } else { + + // How are we ending this statement? + last = // If the stack has value and we are ending a block... + endStack.length && bracketCount == -1 ? // Use the last item in the block stack. + endStack.pop() : // Or use the default ending. + { + after: ";" + }; + + // If we are ending a returning block, + // add the finish text which returns the result of the + // block. + if (last.before) { + buff.push(last.before); + } + // Add the remaining content. + buff.push(content, ";", last.after); + } + break; + case tmap.escapeLeft: + case tmap.returnLeft: + // We have an extra `{` -> `block`. + // Get the number of `{ minus }`. + bracketCount = bracketNum(content); + // If we have more `{`, it means there is a block. + if (bracketCount) { + // When we return to the same # of `{` vs `}` end with a `doubleParent`. + endStack.push({ + before: finishTxt, + after: "}));" + }); + } + + var escaped = startTag === tmap.escapeLeft ? 1 : 0, + commands = { + insert: insert_cmd, + tagName: getTag(tagName, tokens, i), + status: status() + }; + + for (var ii = 0; ii < this.helpers.length; ii++) { + // Match the helper based on helper + // regex name value + var helper = this.helpers[ii]; + if (helper.name.test(content)) { + content = helper.fn(content, commands); + + // dont escape partials + if (helper.name.source == /^>[\s]*\w*/.source) { + escaped = 0; + } + break; + } + } + + // Handle special cases + if (typeof content == 'object') { + if (content.raw) { + buff.push(content.raw); + } + } else { + // If we have `<%== a(function(){ %>` then we want + // `can.EJS.text(0,this, function(){ return a(function(){ var _v1ew = [];`. + buff.push(insert_cmd, "can.view.txt(" + escaped + ",'" + tagName + "'," + status() + ",this,function(){ " + (this.text.escape || '') + "return ", content, + // If we have a block. + bracketCount ? + // Start with startTxt `"var _v1ew = [];"`. + startTxt : + // If not, add `doubleParent` to close push and text. + "}));"); + } + + if (rescan && rescan.after && rescan.after.length) { + put(rescan.after.length); + rescan = null; + } + break; + } + startTag = null; + content = ''; + break; + case tmap.templateLeft: + content += tmap.left; + break; + default: + content += token; + break; + } + } + lastToken = token; + } + + // Put it together... + if (content.length) { + // Should be `content.dump` in Ruby. + put(content); + } + buff.push(";"); + + var template = buff.join(''), + out = { + out: 'with(_VIEW) { with (_CONTEXT) {' + template + " " + finishTxt + "}}" + }; + // Use `eval` instead of creating a function, because it is easier to debug. + myEval.call(out, 'this.fn = (function(_CONTEXT,_VIEW){' + out.out + '});\r\n//@ sourceURL=' + name + ".js"); + + return out; + } + }; + + return Scanner; + })(__m11, __m14); + + // ## view/node_lists.js + var __m17 = (function(can) { + + // text node expando test + var canExpando = true; + try { + document.createTextNode('')._ = 0; + } catch (ex) { + canExpando = false; + } + + // a mapping of element ids to nodeList ids + var nodeMap = {}, + // a mapping of ids to text nodes + textNodeMap = {}, + // a mapping of nodeList ids to nodeList + nodeListMap = {}, + expando = "ejs_" + Math.random(), + _id = 0, + id = function(node) { + if (canExpando || node.nodeType !== 3) { + if (node[expando]) { + return node[expando]; + } else { + return node[expando] = (node.nodeName ? "element_" : "obj_") + (++_id); + } + } else { + for (var textNodeID in textNodeMap) { + if (textNodeMap[textNodeID] === node) { + return textNodeID; + } + } + + textNodeMap["text_" + (++_id)] = node; + return "text_" + _id; + } + }, + // removes a nodeListId from a node's nodeListIds + removeNodeListId = function(node, nodeListId) { + var nodeListIds = nodeMap[id(node)]; + if (nodeListIds) { + var index = can.inArray(nodeListId, nodeListIds); + + if (index >= 0) { + nodeListIds.splice(index, 1); + } + if (!nodeListIds.length) { + delete nodeMap[id(node)]; + } + } + }, + addNodeListId = function(node, nodeListId) { + var nodeListIds = nodeMap[id(node)]; + if (!nodeListIds) { + nodeListIds = nodeMap[id(node)] = []; + } + nodeListIds.push(nodeListId); + }; + + var nodeLists = { + id: id, + // replaces the contents of one node list with the nodes in another list + replace: function(oldNodeList, newNodes) { + // for each node in the node list + oldNodeList = can.makeArray(oldNodeList); + + // try every set + //can.each( oldNodeList, function(node){ + var node = oldNodeList[0] + // for each nodeList the node is in + can.each(can.makeArray(nodeMap[id(node)]), function(nodeListId) { + + // if startNode to endNode is + // within list, replace that list + // I think the problem is not the WHOLE part is being + // matched + var nodeList = nodeListMap[nodeListId], + startIndex = can.inArray(node, nodeList), + endIndex = can.inArray(oldNodeList[oldNodeList.length - 1], nodeList); + + + // remove this nodeListId from each node + if (startIndex >= 0 && endIndex >= 0) { + for (var i = startIndex; i <= endIndex; i++) { + var n = nodeList[i]; + removeNodeListId(n, nodeListId); + } + // swap in new nodes into the nodeLIst + nodeList.splice.apply(nodeList, [startIndex, endIndex - startIndex + 1].concat(newNodes)); + + // tell these new nodes they belong to the nodeList + can.each(newNodes, function(node) { + addNodeListId(node, nodeListId); + }); + } else { + nodeLists.unregister(nodeList); + } + }); + //}); + }, + // registers a list of nodes + register: function(nodeList) { + var nLId = id(nodeList); + nodeListMap[nLId] = nodeList; + + can.each(nodeList, function(node) { + addNodeListId(node, nLId); + }); + + }, + // removes mappings + unregister: function(nodeList) { + var nLId = id(nodeList); + can.each(nodeList, function(node) { + removeNodeListId(node, nLId); + }); + delete nodeListMap[nLId]; + }, + nodeMap: nodeMap, + nodeListMap: nodeListMap + } + var ids = function(nodeList) { + return nodeList.map(function(n) { + return id(n) + ":" + (n.innerHTML || n.nodeValue) + }) + } + return nodeLists; + + })(__m3); + + // ## view/live.js + var __m16 = (function(can, elements, view, nodeLists) { + // ## live.js + // The live module provides live binding for computes + // and can.Observe.List. + // Currently, it's API is designed for `can/view/render`, but + // it could easily be used for other purposes. + + // ### Helper methods + // #### setup + // `setup(HTMLElement, bind(data), unbind(data)) -> data` + // Calls bind right away, but will call unbind + // if the element is "destroyed" (removed from the DOM). + var setup = function(el, bind, unbind) { + var teardown = function() { + unbind(data) + can.unbind.call(el, 'destroyed', teardown); + }, + data = { + teardownCheck: function(parent) { + if (!parent) { + teardown(); + } + } + } + + can.bind.call(el, 'destroyed', teardown); + bind(data) + return data; + }, + // #### listen + // Calls setup, but presets bind and unbind to + // operate on a compute + listen = function(el, compute, change) { + return setup(el, function() { + compute.bind("change", change); + }, function(data) { + compute.unbind("change", change); + if (data.nodeList) { + nodeLists.unregister(data.nodeList); + } + }); + }, + // #### getAttributeParts + // Breaks up a string like foo='bar' into ["foo","'bar'""] + getAttributeParts = function(newVal) { + return (newVal || "").replace(/['"]/g, '').split('=') + } + // #### insertElementsAfter + // Appends elements after the last item in oldElements. + insertElementsAfter = function(oldElements, newFrag) { + var last = oldElements[oldElements.length - 1]; + + // Insert it in the `document` or `documentFragment` + if (last.nextSibling) { + last.parentNode.insertBefore(newFrag, last.nextSibling); + } else { + last.parentNode.appendChild(newFrag); + } + }; + + var live = { + nodeLists: nodeLists, + list: function(el, list, func, context, parentNode) { + // A mapping of the index to an array + // of elements that represent the item. + // Each array is registered so child or parent + // live structures can update the elements + var nodesMap = [], + + add = function(ev, items, index) { + + // Collect new html and mappings + var frag = document.createDocumentFragment(), + newMappings = []; + can.each(items, function(item) { + var itemHTML = func.call(context, item), + itemFrag = can.view.frag(itemHTML, parentNode); + + newMappings.push(can.makeArray(itemFrag.childNodes)); + frag.appendChild(itemFrag); + }) + + // Inserting at the end of the list + if (!nodesMap[index]) { + insertElementsAfter( + index == 0 ? [text] : + nodesMap[index - 1], frag) + } else { + var el = nodesMap[index][0]; + el.parentNode.insertBefore(frag, el) + } + // register each item + can.each(newMappings, function(nodeList) { + nodeLists.register(nodeList) + }); + [].splice.apply(nodesMap, [index, 0].concat(newMappings)); + }, + remove = function(ev, items, index) { + var removedMappings = nodesMap.splice(index, items.length), + itemsToRemove = []; + + can.each(removedMappings, function(nodeList) { + // add items that we will remove all at once + [].push.apply(itemsToRemove, nodeList) + // Update any parent lists to remove these items + nodeLists.replace(nodeList, []); + // unregister the list + nodeLists.unregister(nodeList); + + }); + can.remove(can.$(itemsToRemove)); + }, + parentNode = elements.getParentNode(el, parentNode), + text = document.createTextNode(""); + + // Setup binding and teardown to add and remove events + setup(parentNode, function() { + list.bind("add", add).bind("remove", remove) + }, function() { + list.unbind("add", add).unbind("remove", remove); + can.each(nodesMap, function(nodeList) { + nodeLists.unregister(nodeList); + }) + }) + + insertElementsAfter([el], text); + can.remove(can.$(el)); + add({}, list, 0); + + }, + html: function(el, compute, parentNode) { + var parentNode = elements.getParentNode(el, parentNode), + + data = listen(parentNode, compute, function(ev, newVal, oldVal) { + var attached = nodes[0].parentNode; + // update the nodes in the DOM with the new rendered value + if (attached) { + makeAndPut(newVal); + } + data.teardownCheck(nodes[0].parentNode); + }); + + var nodes, + makeAndPut = function(val) { + // create the fragment, but don't hook it up + // we need to insert it into the document first + var frag = can.view.frag(val, parentNode), + // keep a reference to each node + newNodes = can.makeArray(frag.childNodes); + // Insert it in the `document` or `documentFragment` + insertElementsAfter(nodes || [el], frag) + // nodes hasn't been set yet + if (!nodes) { + can.remove(can.$(el)); + nodes = newNodes; + // set the teardown nodeList + data.nodeList = nodes; + nodeLists.register(nodes); + } else { + // Update node Array's to point to new nodes + // and then remove the old nodes. + // It has to be in this order for Mootools + // and IE because somehow, after an element + // is removed from the DOM, it loses its + // expando values. + var nodesToRemove = can.makeArray(nodes); + nodeLists.replace(nodes, newNodes); + can.remove(can.$(nodesToRemove)); + } + }; + makeAndPut(compute(), [el]); + + }, + text: function(el, compute, parentNode) { + var parent = elements.getParentNode(el, parentNode); + + // setup listening right away so we don't have to re-calculate value + var data = listen(el.parentNode !== parent ? el.parentNode : parent, compute, function(ev, newVal, oldVal) { + // Sometimes this is 'unknown' in IE and will throw an exception if it is + if (typeof node.nodeValue != 'unknown') { + node.nodeValue = "" + newVal; + } + data.teardownCheck(node.parentNode); + }); + + var node = document.createTextNode(compute()); + + if (el.parentNode !== parent) { + parent = el.parentNode; + parent.insertBefore(node, el); + parent.removeChild(el); + } else { + parent.insertBefore(node, el); + parent.removeChild(el); + } + }, + attributes: function(el, compute, currentValue) { + var setAttrs = function(newVal) { + var parts = getAttributeParts(newVal), + newAttrName = parts.shift(); + + // Remove if we have a change and used to have an `attrName`. + if ((newAttrName != attrName) && attrName) { + elements.removeAttr(el, attrName); + } + // Set if we have a new `attrName`. + if (newAttrName) { + elements.setAttr(el, newAttrName, parts.join('=')); + attrName = newAttrName; + } + } + + listen(el, compute, function(ev, newVal) { + setAttrs(newVal) + }) + // current value has been set + if (arguments.length >= 3) { + var attrName = getAttributeParts(currentValue)[0] + } else { + setAttrs(compute()) + } + }, + attributePlaceholder: '__!!__', + attributeReplace: /__!!__/g, + attribute: function(el, attributeName, compute) { + listen(el, compute, function(ev, newVal) { + elements.setAttr(el, attributeName, hook.render()); + }) + + var wrapped = can.$(el), + hooks; + + // Get the list of hookups or create one for this element. + // Hooks is a map of attribute names to hookup `data`s. + // Each hookup data has: + // `render` - A `function` to render the value of the attribute. + // `funcs` - A list of hookup `function`s on that attribute. + // `batchNum` - The last event `batchNum`, used for performance. + hooks = can.data(wrapped, 'hooks'); + if (!hooks) { + can.data(wrapped, 'hooks', hooks = {}); + } + + // Get the attribute value. + var attr = elements.getAttr(el, attributeName), + // Split the attribute value by the template. + // Only split out the first __!!__ so if we have multiple hookups in the same attribute, + // they will be put in the right spot on first render + parts = attr.split(live.attributePlaceholder), + goodParts = [], + hook; + goodParts.push(parts.shift(), + parts.join(live.attributePlaceholder)); + + // If we already had a hookup for this attribute... + if (hooks[attributeName]) { + // Just add to that attribute's list of `function`s. + hooks[attributeName].computes.push(compute); + } else { + // Create the hookup data. + hooks[attributeName] = { + render: function() { + var i = 0, + // attr doesn't have a value in IE + newAttr = attr ? attr.replace(live.attributeReplace, function() { + return elements.contentText(hook.computes[i++]()); + }) : elements.contentText(hook.computes[i++]()); + return newAttr; + }, + computes: [compute], + batchNum: undefined + }; + } + + // Save the hook for slightly faster performance. + hook = hooks[attributeName]; + + // Insert the value in parts. + goodParts.splice(1, 0, compute()); + + // Set the attribute. + elements.setAttr(el, attributeName, goodParts.join("")); + + } + } + return live; + + })(__m3, __m14, __m11, __m17); + + // ## view/render.js + var __m15 = (function(can, elements, live) { + + var pendingHookups = [], + tagChildren = function(tagName) { + var newTag = elements.tagMap[tagName] || "span"; + if (newTag === "span") { + //innerHTML in IE doesn't honor leading whitespace after empty elements + return "@@!!@@"; + } + return "<" + newTag + ">" + tagChildren(newTag) + "</" + newTag + ">"; + }, + contentText = function(input, tag) { + + // If it's a string, return. + if (typeof input == 'string') { + return input; + } + // If has no value, return an empty string. + if (!input && input !== 0) { + return ''; + } + + // If it's an object, and it has a hookup method. + var hook = (input.hookup && + + // Make a function call the hookup method. + + function(el, id) { + input.hookup.call(input, el, id); + }) || + + // Or if it's a `function`, just use the input. + (typeof input == 'function' && input); + + // Finally, if there is a `function` to hookup on some dom, + // add it to pending hookups. + if (hook) { + if (tag) { + return "<" + tag + " " + can.view.hook(hook) + "></" + tag + ">" + } else { + pendingHookups.push(hook); + } + + return ''; + } + + // Finally, if all else is `false`, `toString()` it. + return "" + input; + }, + // Returns escaped/sanatized content for anything other than a live-binding + contentEscape = function(txt) { + return (typeof txt == 'string' || typeof txt == 'number') ? + can.esc(txt) : + contentText(txt); + }; + + var current; + + can.extend(can.view, { + live: live, + setupLists: function() { + + var old = can.view.lists, + data; + + can.view.lists = function(list, renderer) { + data = { + list: list, + renderer: renderer + } + } + return function() { + can.view.lists = old; + return data; + } + }, + pending: function() { + // TODO, make this only run for the right tagName + var hooks = pendingHookups.slice(0); + lastHookups = hooks; + pendingHookups = []; + return can.view.hook(function(el) { + can.each(hooks, function(fn) { + fn(el); + }); + }); + }, + + + txt: function(escape, tagName, status, self, func) { + var listTeardown = can.view.setupLists(), + emptyHandler = function() {}, + unbind = function() { + compute.unbind("change", emptyHandler) + }; + + var compute = can.compute(func, self, false); + // bind to get and temporarily cache the value + compute.bind("change", emptyHandler); + // call the "wrapping" function and get the binding information + var tag = (elements.tagMap[tagName] || "span"), + listData = listTeardown(), + value = compute(); + + + if (listData) { + return "<" + tag + can.view.hook(function(el, parentNode) { + live.list(el, listData.list, listData.renderer, self, parentNode); + }) + "></" + tag + ">"; + } + + // If we had no observes just return the value returned by func. + if (!compute.hasDependencies) { + unbind(); + return (escape || status !== 0 ? contentEscape : contentText)(value, status === 0 && tag); + } + + // the property (instead of innerHTML elements) to adjust. For + // example options should use textContent + var contentProp = elements.tagToContentPropMap[tagName]; + + + // The magic tag is outside or between tags. + if (status === 0 && !contentProp) { + // Return an element tag with a hookup in place of the content + return "<" + tag + can.view.hook( + escape ? + // If we are escaping, replace the parentNode with + // a text node who's value is `func`'s return value. + + function(el, parentNode) { + live.text(el, compute, parentNode); + unbind(); + } : + // If we are not escaping, replace the parentNode with a + // documentFragment created as with `func`'s return value. + + function(el, parentNode) { + live.html(el, compute, parentNode); + unbind(); + //children have to be properly nested HTML for buildFragment to work properly + }) + ">" + tagChildren(tag) + "</" + tag + ">"; + // In a tag, but not in an attribute + } else if (status === 1) { + // remember the old attr name + pendingHookups.push(function(el) { + live.attributes(el, compute, compute()); + unbind(); + }); + return compute(); + } else { // In an attribute... + var attributeName = status === 0 ? contentProp : status; + // if the magic tag is inside the element, like `<option><% TAG %></option>`, + // we add this hookup to the last element (ex: `option`'s) hookups. + // Otherwise, the magic tag is in an attribute, just add to the current element's + // hookups. + (status === 0 ? lastHookups : pendingHookups).push(function(el) { + live.attribute(el, attributeName, compute); + unbind(); + }); + return live.attributePlaceholder; + } + } + }); + + return can; + })(__m11, __m14, __m16, __m2); + + // ## view/ejs/ejs.js + var __m12 = (function(can) { + // ## ejs.js + // `can.EJS` + // _Embedded JavaScript Templates._ + + // Helper methods. + var extend = can.extend, + EJS = function(options) { + // Supports calling EJS without the constructor + // This returns a function that renders the template. + if (this.constructor != EJS) { + var ejs = new EJS(options); + return function(data, helpers) { + return ejs.render(data, helpers); + }; + } + // If we get a `function` directly, it probably is coming from + // a `steal`-packaged view. + if (typeof options == "function") { + this.template = { + fn: options + }; + return; + } + // Set options on self. + extend(this, options); + this.template = this.scanner.scan(this.text, this.name); + }; + + can.EJS = EJS; + + + EJS.prototype. + + render = function(object, extraHelpers) { + object = object || {}; + return this.template.fn.call(object, object, new EJS.Helpers(object, extraHelpers || {})); + }; + + extend(EJS.prototype, { + + scanner: new can.view.Scanner({ + + tokens: [ + ["templateLeft", "<%%"], // Template + ["templateRight", "%>"], // Right Template + ["returnLeft", "<%=="], // Return Unescaped + ["escapeLeft", "<%="], // Return Escaped + ["commentLeft", "<%#"], // Comment + ["left", "<%"], // Run --- this is hack for now + ["right", "%>"], // Right -> All have same FOR Mustache ... + ["returnRight", "%>"] + ], + + + transform: function(source) { + return source.replace(/<%([\s\S]+?)%>/gm, function(whole, part) { + var brackets = [], + foundBracketPair, + i; + + // Look for brackets (for removing self-contained blocks) + part.replace(/[{}]/gm, function(bracket, offset) { + brackets.push([bracket, offset]); + }); + + // Remove bracket pairs from the list of replacements + do { + foundBracketPair = false; + for (i = brackets.length - 2; i >= 0; i--) { + if (brackets[i][0] == '{' && brackets[i + 1][0] == '}') { + brackets.splice(i, 2); + foundBracketPair = true; + break; + } + } + } while (foundBracketPair); + + // Unmatched brackets found, inject EJS tags + if (brackets.length >= 2) { + var result = ['<%'], + bracket, + last = 0; + for (i = 0; bracket = brackets[i]; i++) { + result.push(part.substring(last, last = bracket[1])); + if ((bracket[0] == '{' && i < brackets.length - 1) || (bracket[0] == '}' && i > 0)) { + result.push(bracket[0] == '{' ? '{ %><% ' : ' %><% }'); + } else { + result.push(bracket[0]); + } + ++last; + } + result.push(part.substring(last), '%>'); + return result.join(''); + } + // Otherwise return the original + else { + return '<%' + part + '%>'; + } + }); + } + }) + }); + + EJS.Helpers = function(data, extras) { + this._data = data; + this._extras = extras; + extend(this, extras); + }; + + + EJS.Helpers.prototype = { + // TODO Deprecated!! + list: function(list, cb) { + + can.each(list, function(item, i) { + cb(item, i, list) + }) + }, + each: function(list, cb) { + // Normal arrays don't get live updated + if (can.isArray(list)) { + this.list(list, cb); + } else { + can.view.lists(list, cb); + } + } + }; + + // Options for `steal`'s build. + can.view.register({ + suffix: "ejs", + // returns a `function` that renders the view. + script: function(id, src) { + return "can.EJS(function(_CONTEXT,_VIEW) { " + new EJS({ + text: src, + name: id + }).template.out + " })"; + }, + renderer: function(id, text) { + return EJS({ + text: text, + name: id + }); + } + }); + + return can; + })(__m3, __m11, __m2, __m9, __m13, __m15); + + // ## control/control.js + var __m18 = (function(can) { + // ## control.js + // `can.Control` + // _Controller_ + + // Binds an element, returns a function that unbinds. + var bind = function(el, ev, callback) { + + can.bind.call(el, ev, callback); + + return function() { + can.unbind.call(el, ev, callback); + }; + }, + isFunction = can.isFunction, + extend = can.extend, + each = can.each, + slice = [].slice, + paramReplacer = /\{([^\}]+)\}/g, + special = can.getObject("$.event.special", [can]) || {}, + + // Binds an element, returns a function that unbinds. + delegate = function(el, selector, ev, callback) { + can.delegate.call(el, selector, ev, callback); + return function() { + can.undelegate.call(el, selector, ev, callback); + }; + }, + + // Calls bind or unbind depending if there is a selector. + binder = function(el, ev, callback, selector) { + return selector ? + delegate(el, can.trim(selector), ev, callback) : + bind(el, ev, callback); + }, + + basicProcessor; + + var Control = can.Control = can.Construct( + + { + // Setup pre-processes which methods are event listeners. + + setup: function() { + + // Allow contollers to inherit "defaults" from super-classes as it + // done in `can.Construct` + can.Construct.setup.apply(this, arguments); + + // If you didn't provide a name, or are `control`, don't do anything. + if (can.Control) { + + // Cache the underscored names. + var control = this, + funcName; + + // Calculate and cache actions. + control.actions = {}; + for (funcName in control.prototype) { + if (control._isAction(funcName)) { + control.actions[funcName] = control._action(funcName); + } + } + } + }, + + // Moves `this` to the first argument, wraps it with `jQuery` if it's an element + _shifter: function(context, name) { + + var method = typeof name == "string" ? context[name] : name; + + if (!isFunction(method)) { + method = context[method]; + } + + return function() { + context.called = name; + return method.apply(context, [this.nodeName ? can.$(this) : this].concat(slice.call(arguments, 0))); + }; + }, + + // Return `true` if is an action. + + _isAction: function(methodName) { + + var val = this.prototype[methodName], + type = typeof val; + // if not the constructor + return (methodName !== 'constructor') && + // and is a function or links to a function + (type == "function" || (type == "string" && isFunction(this.prototype[val]))) && + // and is in special, a processor, or has a funny character + !! (special[methodName] || processors[methodName] || /[^\w]/.test(methodName)); + }, + // Takes a method name and the options passed to a control + // and tries to return the data necessary to pass to a processor + // (something that binds things). + + _action: function(methodName, options) { + + // If we don't have options (a `control` instance), we'll run this + // later. + paramReplacer.lastIndex = 0; + if (options || !paramReplacer.test(methodName)) { + // If we have options, run sub to replace templates `{}` with a + // value from the options or the window + var convertedName = options ? can.sub(methodName, [options, window]) : methodName; + if (!convertedName) { + return null; + } + // If a `{}` template resolves to an object, `convertedName` will be + // an array + var arr = can.isArray(convertedName), + + // Get the name + name = arr ? convertedName[1] : convertedName, + + // Grab the event off the end + parts = name.split(/\s+/g), + event = parts.pop(); + + return { + processor: processors[event] || basicProcessor, + parts: [name, parts.join(" "), event], + delegate: arr ? convertedName[0] : undefined + }; + } + }, + // An object of `{eventName : function}` pairs that Control uses to + // hook up events auto-magically. + + processors: {}, + // A object of name-value pairs that act as default values for a + // control instance + defaults: {} + + }, { + + // Sets `this.element`, saves the control in `data, binds event + // handlers. + + setup: function(element, options) { + + var cls = this.constructor, + pluginname = cls.pluginName || cls._fullName, + arr; + + // Want the raw element here. + this.element = can.$(element) + + if (pluginname && pluginname !== 'can_control') { + // Set element and `className` on element. + this.element.addClass(pluginname); + } + + (arr = can.data(this.element, "controls")) || can.data(this.element, "controls", arr = []); + arr.push(this); + + // Option merging. + + this.options = extend({}, cls.defaults, options); + + // Bind all event handlers. + this.on(); + + // Gets passed into `init`. + + return [this.element, this.options]; + }, + + on: function(el, selector, eventName, func) { + if (!el) { + + // Adds bindings. + this.off(); + + // Go through the cached list of actions and use the processor + // to bind + var cls = this.constructor, + bindings = this._bindings, + actions = cls.actions, + element = this.element, + destroyCB = can.Control._shifter(this, "destroy"), + funcName, ready; + + for (funcName in actions) { + // Only push if we have the action and no option is `undefined` + if (actions.hasOwnProperty(funcName) && + (ready = actions[funcName] || cls._action(funcName, this.options))) { + bindings.push(ready.processor(ready.delegate || element, + ready.parts[2], ready.parts[1], funcName, this)); + } + } + + + // Setup to be destroyed... + // don't bind because we don't want to remove it. + can.bind.call(element, "destroyed", destroyCB); + bindings.push(function(el) { + can.unbind.call(el, "destroyed", destroyCB); + }); + return bindings.length; + } + + if (typeof el == 'string') { + func = eventName; + eventName = selector; + selector = el; + el = this.element; + } + + if (func === undefined) { + func = eventName; + eventName = selector; + selector = null; + } + + if (typeof func == 'string') { + func = can.Control._shifter(this, func); + } + + this._bindings.push(binder(el, eventName, func, selector)); + + return this._bindings.length; + }, + // Unbinds all event handlers on the controller. + + off: function() { + var el = this.element[0] + each(this._bindings || [], function(value) { + value(el); + }); + // Adds bindings. + this._bindings = []; + }, + // Prepares a `control` for garbage collection + + destroy: function() { + //Control already destroyed + if (this.element === null) { + + return; + } + var Class = this.constructor, + pluginName = Class.pluginName || Class._fullName, + controls; + + // Unbind bindings. + this.off(); + + if (pluginName && pluginName !== 'can_control') { + // Remove the `className`. + this.element.removeClass(pluginName); + } + + // Remove from `data`. + controls = can.data(this.element, "controls"); + controls.splice(can.inArray(this, controls), 1); + + can.trigger(this, "destroyed"); // In case we want to know if the `control` is removed. + + this.element = null; + } + }); + + var processors = can.Control.processors, + // Processors do the binding. + // They return a function that unbinds when called. + // The basic processor that binds events. + basicProcessor = function(el, event, selector, methodName, control) { + return binder(el, event, can.Control._shifter(control, methodName), selector); + }; + + // Set common events to be processed as a `basicProcessor` + each(["change", "click", "contextmenu", "dblclick", "keydown", "keyup", + "keypress", "mousedown", "mousemove", "mouseout", "mouseover", + "mouseup", "reset", "resize", "scroll", "select", "submit", "focusin", + "focusout", "mouseenter", "mouseleave", + // #104 - Add touch events as default processors + // TOOD feature detect? + "touchstart", "touchmove", "touchcancel", "touchend", "touchleave" + ], function(v) { + processors[v] = basicProcessor; + }); + + return Control; + })(__m3, __m1); + + // ## util/string/deparam/deparam.js + var __m20 = (function(can) { + + // ## deparam.js + // `can.deparam` + // _Takes a string of name value pairs and returns a Object literal that represents those params._ + var digitTest = /^\d+$/, + keyBreaker = /([^\[\]]+)|(\[\])/g, + paramTest = /([^?#]*)(#.*)?$/, + prep = function(str) { + return decodeURIComponent(str.replace(/\+/g, " ")); + }; + + + can.extend(can, { + + deparam: function(params) { + + var data = {}, + pairs, lastPart; + + if (params && paramTest.test(params)) { + + pairs = params.split('&'), + + can.each(pairs, function(pair) { + + var parts = pair.split('='), + key = prep(parts.shift()), + value = prep(parts.join("=")), + current = data; + + if (key) { + parts = key.match(keyBreaker); + + for (var j = 0, l = parts.length - 1; j < l; j++) { + if (!current[parts[j]]) { + // If what we are pointing to looks like an `array` + current[parts[j]] = digitTest.test(parts[j + 1]) || parts[j + 1] == "[]" ? [] : {}; + } + current = current[parts[j]]; + } + lastPart = parts.pop(); + if (lastPart == "[]") { + current.push(value); + } else { + current[lastPart] = value; + } + } + }); + } + return data; + } + }); + return can; + })(__m3, __m2); + + // ## route/route.js + var __m19 = (function(can) { + + // ## route.js + // `can.route` + // _Helps manage browser history (and client state) by synchronizing the + // `window.location.hash` with a `can.Observe`._ + // Helper methods used for matching routes. + var + // `RegExp` used to match route variables of the type ':name'. + // Any word character or a period is matched. + matcher = /\:([\w\.]+)/g, + // Regular expression for identifying &key=value lists. + paramsMatcher = /^(?:&[^=]+=[^&]*)+/, + // Converts a JS Object into a list of parameters that can be + // inserted into an html element tag. + makeProps = function(props) { + var tags = []; + can.each(props, function(val, name) { + tags.push((name === 'className' ? 'class' : name) + '="' + + (name === "href" ? val : can.esc(val)) + '"'); + }); + return tags.join(" "); + }, + // Checks if a route matches the data provided. If any route variable + // is not present in the data, the route does not match. If all route + // variables are present in the data, the number of matches is returned + // to allow discerning between general and more specific routes. + matchesData = function(route, data) { + var count = 0, + i = 0, + defaults = {}; + // look at default values, if they match ... + for (var name in route.defaults) { + if (route.defaults[name] === data[name]) { + // mark as matched + defaults[name] = 1; + count++; + } + } + for (; i < route.names.length; i++) { + if (!data.hasOwnProperty(route.names[i])) { + return -1; + } + if (!defaults[route.names[i]]) { + count++; + } + + } + + return count; + }, + onready = !0, + location = window.location, + wrapQuote = function(str) { + return (str + '').replace(/([.?*+\^$\[\]\\(){}|\-])/g, "\\$1"); + }, + each = can.each, + extend = can.extend; + + can.route = function(url, defaults) { + defaults = defaults || {}; + // Extract the variable names and replace with `RegExp` that will match + // an atual URL with values. + var names = [], + test = url.replace(matcher, function(whole, name, i) { + names.push(name); + var next = "\\" + (url.substr(i + whole.length, 1) || can.route._querySeparator); + // a name without a default value HAS to have a value + // a name that has a default value can be empty + // The `\\` is for string-escaping giving single `\` for `RegExp` escaping. + return "([^" + next + "]" + (defaults[name] ? "*" : "+") + ")"; + }); + + // Add route in a form that can be easily figured out. + can.route.routes[url] = { + // A regular expression that will match the route when variable values + // are present; i.e. for `:page/:type` the `RegExp` is `/([\w\.]*)/([\w\.]*)/` which + // will match for any value of `:page` and `:type` (word chars or period). + test: new RegExp("^" + test + "($|" + wrapQuote(can.route._querySeparator) + ")"), + // The original URL, same as the index for this entry in routes. + route: url, + // An `array` of all the variable names in this route. + names: names, + // Default values provided for the variables. + defaults: defaults, + // The number of parts in the URL separated by `/`. + length: url.split('/').length + }; + return can.route; + }; + + + extend(can.route, { + + _querySeparator: '&', + _paramsMatcher: paramsMatcher, + + + param: function(data, _setRoute) { + // Check if the provided data keys match the names in any routes; + // Get the one with the most matches. + var route, + // Need to have at least 1 match. + matches = 0, + matchCount, + routeName = data.route, + propCount = 0; + + delete data.route; + + each(data, function() { + propCount++; + }); + // Otherwise find route. + each(can.route.routes, function(temp, name) { + // best route is the first with all defaults matching + + + matchCount = matchesData(temp, data); + if (matchCount > matches) { + route = temp; + matches = matchCount; + } + if (matchCount >= propCount) { + return false; + } + }); + // If we have a route name in our `can.route` data, and it's + // just as good as what currently matches, use that + if (can.route.routes[routeName] && matchesData(can.route.routes[routeName], data) === matches) { + route = can.route.routes[routeName]; + } + // If this is match... + if (route) { + var cpy = extend({}, data), + // Create the url by replacing the var names with the provided data. + // If the default value is found an empty string is inserted. + res = route.route.replace(matcher, function(whole, name) { + delete cpy[name]; + return data[name] === route.defaults[name] ? "" : encodeURIComponent(data[name]); + }), + after; + // Remove matching default values + each(route.defaults, function(val, name) { + if (cpy[name] === val) { + delete cpy[name]; + } + }); + + // The remaining elements of data are added as + // `&` separated parameters to the url. + after = can.param(cpy); + // if we are paraming for setting the hash + // we also want to make sure the route value is updated + if (_setRoute) { + can.route.attr('route', route.route); + } + return res + (after ? can.route._querySeparator + after : ""); + } + // If no route was found, there is no hash URL, only paramters. + return can.isEmptyObject(data) ? "" : can.route._querySeparator + can.param(data); + }, + + deparam: function(url) { + // See if the url matches any routes by testing it against the `route.test` `RegExp`. + // By comparing the URL length the most specialized route that matches is used. + var route = { + length: -1 + }; + each(can.route.routes, function(temp, name) { + if (temp.test.test(url) && temp.length > route.length) { + route = temp; + } + }); + // If a route was matched. + if (route.length > -1) { + + var // Since `RegExp` backreferences are used in `route.test` (parens) + // the parts will contain the full matched string and each variable (back-referenced) value. + parts = url.match(route.test), + // Start will contain the full matched string; parts contain the variable values. + start = parts.shift(), + // The remainder will be the `&key=value` list at the end of the URL. + remainder = url.substr(start.length - (parts[parts.length - 1] === can.route._querySeparator ? 1 : 0)), + // If there is a remainder and it contains a `&key=value` list deparam it. + obj = (remainder && can.route._paramsMatcher.test(remainder)) ? can.deparam(remainder.slice(1)) : {}; + + // Add the default values for this route. + obj = extend(true, {}, route.defaults, obj); + // Overwrite each of the default values in `obj` with those in + // parts if that part is not empty. + each(parts, function(part, i) { + if (part && part !== can.route._querySeparator) { + obj[route.names[i]] = decodeURIComponent(part); + } + }); + obj.route = route.route; + return obj; + } + // If no route was matched, it is parsed as a `&key=value` list. + if (url.charAt(0) !== can.route._querySeparator) { + url = can.route._querySeparator + url; + } + return can.route._paramsMatcher.test(url) ? can.deparam(url.slice(1)) : {}; + }, + + data: new can.Observe({}), + + routes: {}, + + ready: function(val) { + if (val === false) { + onready = val; + } + if (val === true || onready === true) { + can.route._setup(); + setState(); + } + return can.route; + }, + + url: function(options, merge) { + if (merge) { + options = extend({}, curParams, options) + } + return "#!" + can.route.param(options); + }, + + link: function(name, options, props, merge) { + return "<a " + makeProps( + extend({ + href: can.route.url(options, merge) + }, props)) + ">" + name + "</a>"; + }, + + current: function(options) { + return location.hash == "#!" + can.route.param(options) + }, + _setup: function() { + // If the hash changes, update the `can.route.data`. + can.bind.call(window, 'hashchange', setState); + }, + _getHash: function() { + return location.href.split(/#!?/)[1] || ""; + }, + _setHash: function(serialized) { + var path = (can.route.param(serialized, true)); + location.hash = "#!" + path; + return path; + } + }); + + + // The functions in the following list applied to `can.route` (e.g. `can.route.attr('...')`) will + // instead act on the `can.route.data` observe. + each(['bind', 'unbind', 'delegate', 'undelegate', 'attr', 'removeAttr'], function(name) { + can.route[name] = function() { + // `delegate` and `undelegate` require + // the `can/observe/delegate` plugin + if (!can.route.data[name]) { + return; + } + + return can.route.data[name].apply(can.route.data, arguments); + } + }) + + var // A ~~throttled~~ debounced function called multiple times will only fire once the + // timer runs down. Each call resets the timer. + timer, + // Intermediate storage for `can.route.data`. + curParams, + // Deparameterizes the portion of the hash of interest and assign the + // values to the `can.route.data` removing existing values no longer in the hash. + // setState is called typically by hashchange which fires asynchronously + // So it's possible that someone started changing the data before the + // hashchange event fired. For this reason, it will not set the route data + // if the data is changing or the hash already matches the hash that was set. + setState = can.route.setState = function() { + var hash = can.route._getHash(); + curParams = can.route.deparam(hash); + + // if the hash data is currently changing, or + // the hash is what we set it to anyway, do NOT change the hash + if (!changingData || hash !== lastHash) { + can.route.attr(curParams, true); + } + }, + // The last hash caused by a data change + lastHash, + // Are data changes pending that haven't yet updated the hash + changingData; + + // If the `can.route.data` changes, update the hash. + // Using `.serialize()` retrieves the raw data contained in the `observable`. + // This function is ~~throttled~~ debounced so it only updates once even if multiple values changed. + // This might be able to use batchNum and avoid this. + can.route.bind("change", function(ev, attr) { + // indicate that data is changing + changingData = 1; + clearTimeout(timer); + timer = setTimeout(function() { + // indicate that the hash is set to look like the data + changingData = 0; + var serialized = can.route.data.serialize(); + + lastHash = can.route._setHash(serialized); + }, 1); + }); + // `onready` event... + can.bind.call(document, "ready", can.route.ready); + + // Libraries other than jQuery don't execute the document `ready` listener + // if we are already DOM ready + if ((document.readyState === 'complete' || document.readyState === "interactive") && onready) { + can.route.ready(); + } + + // extend route to have a similar property + // that is often checked in mustache to determine + // an object's observability + can.route.constructor.canMakeObserve = can.Observe.canMakeObserve; + + return can.route; + })(__m3, __m7, __m20); + + // ## control/route/route.js + var __m21 = (function(can) { + + // ## control/route.js + // _Controller route integration._ + + can.Control.processors.route = function(el, event, selector, funcName, controller) { + selector = selector || ""; + can.route(selector); + var batchNum, + check = function(ev, attr, how) { + if (can.route.attr('route') === (selector) && + (ev.batchNum === undefined || ev.batchNum !== batchNum)) { + + batchNum = ev.batchNum; + + var d = can.route.attr(); + delete d.route; + if (can.isFunction(controller[funcName])) { + controller[funcName](d); + } else { + controller[controller[funcName]](d); + } + + } + }; + can.route.bind('change', check); + return function() { + can.route.unbind('change', check); + }; + }; + + return can; + })(__m3, __m19, __m18); + + window['can'] = __m5; + })(); \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.jquery.min.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.jquery.min.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.jquery.min.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,11 @@ +/*! + * CanJS - 1.1.6 + * http://canjs.us/ + * Copyright (c) 2013 Bitovi + * Wed, 29 May 2013 18:59:22 GMT + * Licensed MIT + * Includes: CanJS default build + * Download from: http://canjs.us/ + */ +!function(undefined){var __m5=function(){var a=window.can||{};("undefined"==typeof GLOBALCAN||GLOBALCAN!==!1)&&(window.can=a),a.isDeferred=function(a){var b=this.isFunction;return a&&b(a.then)&&b(a.pipe)};var b=0;return a.cid=function(a,c){return a._cid?a._cid:a._cid=(c||"")+ ++b},a.VERSION="@EDGE",a}(),__m6=function(a){return a.each=function(a,b,c){var d,e=0;if(a)if("number"==typeof a.length&&a.pop)for(a.attr&&a.attr("length"),d=a.length;d>e&&b.call(c||a[e],a[e],e,a)!==!1;e++);else if(a.hasOwnProperty)for(d in a)if(a.hasOwnProperty(d)&&b.call(c||a[d],a[d],d,a)===!1)break;return a},a}(__m5),__m3=function(a,b){a.extend(b,a,{trigger:function(b,c,d){b.trigger?b.trigger(c,d):a.event.trigger(c,d,b,!0)},addEvent:function(b,c){return a([this]).bind(b,c),this},removeEvent:function(b,c){return a([this]).unbind(b,c),this},buildFragment:function(b,c){var d,e=a.buildFragment;return b=[b],c=c||document,c=!c.nodeType&&c[0]||c,c=c.ownerDocument||c,d=e.call(jQuery,b,c),d.cacheable?a.clone(d.fragment):d.fragment||d},$:a,each:b.each}),a.each(["bind","unbind","undelegate","delegate"],function(c,d){b[d]=function(){var b=this[d]?this:a([this]);return b[d].apply(b,arguments),this}}),a.each(["append","filter","addClass","remove","data","get"],function(a,c){b[c]=function(a){return a[c].apply(a,b.makeArray(arguments).slice(1))}});var c=a.cleanData;return a.cleanData=function(d){a.each(d,function(a,c){c&&b.trigger(c,"destroyed",[],!1)}),c(d)},b}(jQuery,__m5,__m6),__m2=function(a){var b=/_|-/,c=/\=\=/,d=/([A-Z]+)([A-Z][a-z])/g,e=/([a-z\d])([A-Z])/g,f=/([a-z\d])([A-Z])/g,g=/\{([^\}]+)\}/g,h=/"/g,i=/'/g,j=function(a,b,c){var d=a[b];return d===undefined&&c===!0&&(d=a[b]={}),d},k=function(a){return/^f|^o/.test(typeof a)};return a.extend(a,{esc:function(a){var b=null===a||a===undefined||isNaN(a)&&"NaN"==""+a;return(""+(b?"":a)).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(h,""").replace(i,"'")},getObject:function(b,c,d){var e,f,g,h,i=b?b.split("."):[],l=i.length,m=0;if(c=a.isArray(c)?c:[c||window],h=c.length,!l)return c[0];for(m;h>m;m++){for(e=c[m],g=undefined,f=0;l>f&&k(e);f++)g=e,e=j(g,i[f]);if(g!==undefined&&e!==undefined)break}if(d===!1&&e!==undefined&&delete g[i[f-1]],d===!0&&e===undefined)for(e=c[0],f=0;l>f&&k(e);f++)e=j(e,i[f],!0);return e},capitalize:function(a){return a.charAt(0).toUpperCase()+a.slice(1)},underscore:function(a){return a.replace(c,"/").replace(d,"$1_$2").replace(e,"$1_$2").replace(f,"_").toLowerCase()},sub:function(b,c,d){var e=[];return b=b||"",e.push(b.replace(g,function(b,f){var g=a.getObject(f,c,d===!0?!1:undefined);return g===undefined?(e=null,""):k(g)&&e?(e.push(g),""):""+g})),null===e?e:e.length<=1?e[0]:e},replacer:g,undHash:b}),a}(__m3),__m1=function(a){var b=0;return a.Construct=function(){return arguments.length?a.Construct.extend.apply(a.Construct,arguments):void 0},a.extend(a.Construct,{newInstance:function(){var a,b=this.instance();return b.setup&&(a=b.setup.apply(b,arguments)),b.init&&b.init.apply(b,a||arguments),b},_inherit:function(b,c,d){a.extend(d||b,b||{})},_overwrite:function(a,b,c,d){a[c]=d},setup:function(b){this.defaults=a.extend(!0,{},b.defaults,this.defaults)},instance:function(){b=1;var a=new this;return b=0,a},extend:function(c,d,e){function f(){return b?void 0:this.constructor!==f&&arguments.length?arguments.callee.extend.apply(arguments.callee,arguments):this.constructor.newInstance.apply(this.constructor,arguments)}"string"!=typeof c&&(e=d,d=c,c=null),e||(e=d,d=null),e=e||{};var g,h,i,j,k=this,l=this.prototype;j=this.instance(),a.Construct._inherit(e,l,j);for(g in k)k.hasOwnProperty(g)&&(f[g]=k[g]);if(a.Construct._inherit(d,k,f),c){var m=c.split("."),h=m.pop(),n=a.getObject(m.join("."),window,!0),i=n,o=a.underscore(c.replace(/\./g,"_")),p=a.underscore(h);n[h]=f}a.extend(f,{constructor:f,prototype:j,namespace:i,_shortName:p,fullName:c,_fullName:o}),h!==undefined&&(f.shortName=h),f.prototype.constructor=f;var q=[k].concat(a.makeArray(arguments)),r=f.setup.apply(f,q);return f.init&&f.init.apply(f,r||q),f}}),a.Construct}(__m2),__m8=function(a){return a.bindAndSetup=function(){return a.addEvent.apply(this,arguments),this._init||(this._bindings?this._bindings++:(this._bindings=1,this._bindsetup&&this._bindsetup())),this},a.unbindAndTeardown=function(){return a.removeEvent.apply(this,arguments),this._bindings--,this._bindings||this._bindteardown&&this._bindteardown(),this},a}(__m3),__m7=function(a){var b=function(b){return b&&!a.isDeferred(b)&&(a.isArray(b)||a.isPlainObject(b)||b instanceof a.Observe)},c=function(b,c){return a.each(b,function(a){a&&a.unbind&&a.unbind("change"+c)})},d=function(b,d,g,h,i){return h=h||f,i=i||f.List,b instanceof f?g._bindings&&c([b],g._cid):b=a.isArray(b)?new i(b):new h(b),g._bindings&&e(b,d,g),b},e=function(b,c,d){b.bind("change"+d._cid,function(){var e=a.makeArray(arguments),f=e.shift();e[0]=("*"===c?[d.indexOf(b),e[0]]:[c,e[0]]).join("."),f.triggeredNS=f.triggeredNS||{},f.triggeredNS[d._cid]||(f.triggeredNS[d._cid]=!0,a.trigger(d,f,e))})};observeId=0,serialize=function(c,d,e){return c.each(function(c,f){e[f]=b(c)&&a.isFunction(c[d])?c[d]():c}),e},attrParts=function(b,c){return c?[b]:a.isArray(b)?b:(""+b).split(".")},batchNum=1,transactions=0,batchEvents=[],stopCallbacks=[],makeBindSetup=function(a){return function(){var b=this;this._each(function(c,d){c&&c.bind&&e(c,a||d,b)})}};var f=a.Map=a.Observe=a.Construct({bind:a.bindAndSetup,unbind:a.unbindAndTeardown,id:"id",canMakeObserve:b,startBatch:function(a){transactions++,a&&stopCallbacks.push(a)},stopBatch:function(b,c){if(b?transactions=0:transactions--,0==transactions){var d=batchEvents.slice(0),e=stopCallbacks.slice(0);batchEvents=[],stopCallbacks=[],batchNum++,c&&this.startBatch(),a.each(d,function(b){a.trigger.apply(a,b)}),a.each(e,function(a){a()})}},triggerBatch:function(b,c,d){if(!b._init){if(0==transactions)return a.trigger(b,c,d);c="string"==typeof c?{type:c}:c,c.batchNum=batchNum,batchEvents.push([b,c,d])}},keys:function(a){var b=[];f.__reading&&f.__reading(a,"__keys");for(var c in a._data)b.push(c);return b}},{setup:function(b){this._data={},a.cid(this,".observe"),this._init=1,this.attr(b),this.bind("change"+this._cid,a.proxy(this._changes,this)),delete this._init},_bindsetup:makeBindSetup(),_bindteardown:function(){var a=this._cid;this._each(function(b){c([b],a)})},_changes:function(a,b,c,d,e){f.triggerBatch(this,{type:b,batchNum:a.batchNum},[d,e])},_triggerChange:function(){f.triggerBatch(this,"change",a.makeArray(arguments))},_each:function(a){var b=this.__get();for(var c in b)b.hasOwnProperty(c)&&a(b[c],c)},attr:function(a,b){var c=typeof a;return"string"!==c&&"number"!==c?this._attrs(a,b):b===undefined?(f.__reading&&f.__reading(this,a),this._get(a)):(this._set(a,b),this)},each:function(){return f.__reading&&f.__reading(this,"__keys"),a.each.apply(undefined,[this.__get()].concat(a.makeArray(arguments)))},removeAttr:function(b){var c=this instanceof a.Observe.List,d=attrParts(b),e=d.shift(),g=c?this[e]:this._data[e];return d.length?g.removeAttr(d):(c?this.splice(e,1):e in this._data&&(delete this._data[e],e in this.constructor.prototype||delete this[e],f.triggerBatch(this,"__keys"),this._triggerChange(e,"remove",undefined,g)),g)},_get:function(a){var b="string"==typeof a&&!!~a.indexOf(".")&&this.__get(a);if(b)return b;var c=attrParts(a),d=this.__get(c.shift());return c.length?d?d._get(c):undefined:d},__get:function(a){return a?this._data[a]:this._data},_set:function(a,c,d){var e=attrParts(a,d),f=e.shift(),g=this.__get(f);if(b(g)&&e.length)g._set(e,c);else{if(e.length)throw"can.Observe: Object does not exist";this.__convert&&(c=this.__convert(f,c)),this.__set(f,c,g)}},__set:function(a,e,g){if(e!==g){var h=this.__get().hasOwnProperty(a)?"set":"add";this.___set(a,b(e)?d(e,a,this):e),"add"==h&&f.triggerBatch(this,"__keys",undefined),this._triggerChange(a,h,e,g),g&&c([g],this._cid)}},___set:function(a,b){this._data[a]=b,a in this.constructor.prototype||(this[a]=b)},bind:a.bindAndSetup,unbind:a.unbindAndTeardown,serialize:function(){return serialize(this,"serialize",{})},_attrs:function(c,d){if(c===undefined)return serialize(this,"attr",{});c=a.extend({},c);var e,g,h=this;f.startBatch(),this.each(function(e,f){return g=c[f],g===undefined?(d&&h.removeAttr(f),void 0):(h.__convert&&(g=h.__convert(f,g)),g instanceof a.Observe?h.__set(f,g,e):b(e)&&b(g)&&e.attr?e.attr(g,d):e!=g&&h.__set(f,g,e),delete c[f],void 0)});for(var e in c)g=c[e],this._set(e,g,!0);return f.stopBatch(),this},compute:function(b){return a.compute(this,b)}}),g=[].splice,h=f({setup:function(b,c){this.length=0,a.cid(this,".observe"),this._init=1,a.isDeferred(b)?this.replace(b):this.push.apply(this,a.makeArray(b||[])),this.bind("change"+this._cid,a.proxy(this._changes,this)),a.extend(this,c),delete this._init},_triggerChange:function(a,b,c,d){f.prototype._triggerChange.apply(this,arguments),~a.indexOf(".")||("add"===b?(f.triggerBatch(this,b,[c,+a]),f.triggerBatch(this,"length",[this.length])):"remove"===b?(f.triggerBatch(this,b,[d,+a]),f.triggerBatch(this,"length",[this.length])):f.triggerBatch(this,b,[c,+a]))},__get:function(a){return a?this[a]:this},___set:function(a,b){this[a]=b,+a>=this.length&&(this.length=+a+1)},_each:function(a){for(var b=this.__get(),c=0;c<b.length;c++)a(b[c],c)},_bindsetup:makeBindSetup("*"),serialize:function(){return serialize(this,"serialize",[])},splice:function(e,f){var h,i=a.makeArray(arguments);for(h=2;h<i.length;h++){var j=i[h];b(j)&&(i[h]=d(j,"*",this,this.constructor.Observe,this.constructor))}f===undefined&&(f=i[1]=this.length-e);var k=g.apply(this,i);return a.Observe.startBatch(),f>0&&(this._triggerChange(""+e,"remove",undefined,k),c(k,this._cid)),i.length>2&&this._triggerChange(""+e,"add",i.slice(2),k),a.Observe.stopBatch(),k},_attrs:function(b,c){return b===undefined?serialize(this,"attr",[]):(b=a.makeArray(b),f.startBatch(),this._updateAttrs(b,c),f.stopBatch(),void 0)},_updateAttrs:function(a,c){for(var d=Math.min(a.length,this.length),e=0;d>e;e++){var f=this[e],g=a[e];b(f)&&b(g)?f.attr(g,c):f!=g&&this._set(e,g)}a.length>this.length?this.push.apply(this,a.slice(this.length)):a.length<this.length&&c&&this.splice(a.length)}}),i=function(b){return b[0]&&a.isArray(b[0])?b[0]:a.makeArray(b)};return a.each({push:"length",unshift:0},function(a,c){var e=[][c];h.prototype[c]=function(){var c,f,g=[],h=a?this.length:0,i=arguments.length;for(this.constructor;i--;)f=arguments[i],g[i]=b(f)?d(f,"*",this,this.constructor.Observe,this.constructor):f;return c=e.apply(this,g),(!this.comparator||g.length)&&this._triggerChange(""+h,"add",g,undefined),c}}),a.each({pop:"length",shift:0},function(a,b){h.prototype[b]=function(){var c=i(arguments),d=a&&this.length?this.length-1:0,e=[][b].apply(this,c);return this._triggerChange(""+d,"remove",undefined,[e]),e&&e.unbind&&e.unbind("change"+this._cid),e}}),a.extend(h.prototype,{indexOf:function(b){return this.attr("length"),a.inArray(b,this)},join:[].join,reverse:[].reverse,slice:function(){var a=Array.prototype.slice.apply(this,arguments);return new this.constructor(a)},concat:function(){var b=[];return a.each(a.makeArray(arguments),function(c,d){b[d]=c instanceof a.Observe.List?c.serialize():c}),new this.constructor(Array.prototype.concat.apply(this.serialize(),b))},forEach:function(b,c){a.each(this,b,c||this)},replace:function(b){return a.isDeferred(b)?b.then(a.proxy(this.replace,this)):this.splice.apply(this,[0,this.length].concat(a.makeArray(b||[]))),this}}),a.List=f.List=h,f.setup=function(){a.Construct.setup.apply(this,arguments),this.List=f.List({Observe:this},{})},f}(__m3,__m8,__m1),__m9=function(a){var b=function(b,c){var d;a.Observe&&(d=a.Observe.__reading,a.Observe.__reading=function(a,b){e.push({obj:a,attr:b+""})});var e=[],f=b.call(c);return a.Observe&&(a.Observe.__reading=d),{value:f,observed:e}},c=function(c,d,e,f){var g,h={},i=!0,j={value:undefined,teardown:function(){for(var a in h){var b=h[a];b.observe.obj.unbind(b.observe.attr,k),delete h[a]}}},k=function(a){if(!(f&&!f.bound||a.batchNum!==undefined&&a.batchNum===g)){var b=j.value,c=l();j.value=c,c!==b&&e(c,b),g=g=a.batchNum}},l=function(){var e=b(c,d),f=e.observed,g=e.value;i=!i,a.each(f,function(a){h[a.obj._cid+"|"+a.attr]?h[a.obj._cid+"|"+a.attr].matched=i:(h[a.obj._cid+"|"+a.attr]={matched:i,observe:a},a.obj.bind(a.attr,k))});for(var j in h){var l=h[j];l.matched!==i&&(l.observe.obj.unbind(l.observe.attr,k),delete h[j])}return g};return j.value=l(),j.isListening=!a.isEmptyObject(h),j};return a.compute=function(d,e,f){if(d&&d.isComputed)return d;var g,h,i,j={bound:!1,hasDependencies:!1},k=function(){},l=function(){},m=function(){return i},n=function(a){i=a},o=!0;if(h=function(b){if(arguments.length){var c=i,d=n.call(e,b,c);return h.hasDependencies?m.call(e):(i=d===undefined?m.call(e):d,c!==i&&a.Observe.triggerBatch(h,"change",[i,c]),i)}return a.Observe.__reading&&o&&a.Observe.__reading(h,"change"),j.bound?i:m.call(e)},"function"==typeof d)n=d,m=d,o=f===!1?!1:!0,h.hasDependencies=!1,k=function(a){g=c(d,e||this,a,j),h.hasDependencies=g.isListening,i=g.value},l=function(){g.teardown()};else if(e)if("string"==typeof e){var p=e,q=d instanceof a.Observe;q&&(h.hasDependencies=!0),m=function(){return q?d.attr(p):d[p]},n=function(a){q?d.attr(p,a):d[p]=a};var r;k=function(c){r=function(){c(m(),i)},a.bind.call(d,f||p,r),i=b(m).value},l=function(){a.unbind.call(d,f||p,r)}}else if("function"==typeof e)i=d,n=e;else{i=d;var s=e;m=s.get||m,n=s.set||n,k=s.on||k,l=s.off||l}else i=d;h.isComputed=!0,a.cid(h,"compute");var t=function(b,c){i=b,a.Observe.triggerBatch(h,"change",[b,c])};return a.extend(h,{_bindsetup:function(){j.bound=!0,k.call(this,t)},_bindteardown:function(){l.call(this,t),j.bound=!1},bind:a.bindAndSetup,unbind:a.unbindAndTeardown})},a.compute.binder=c,a.compute}(__m3,__m8),__m10=function(a){var b=function(b,c,d){var e=new a.Deferred;return b.then(function(){var b=a.makeArray(arguments);b[0]=c[d](b[0]),e.resolveWith(e,b)},function(){e.rejectWith(this,arguments)}),"function"==typeof b.abort&&(e.abort=function(){return b.abort()}),e},c=0,d=function(b){return a.Observe.__reading&&a.Observe.__reading(b,b.constructor.id),b.__get(b.constructor.id)},e=function(b,c,d,e,f,g){var h={};if("string"==typeof b){var i=b.split(/\s+/);h.url=i.pop(),i.length&&(h.type=i.pop())}else a.extend(h,b);return h.data="object"!=typeof c||a.isArray(c)?c:a.extend(h.data||{},c),h.url=a.sub(h.url,h.data,!0),a.ajax(a.extend({type:d||"post",dataType:e||"json",success:f,error:g},h))},f=function(b,c,e,f,g){var h;a.isArray(b)?(h=b[1],b=b[0]):h=b.serialize(),h=[h];var i,j,k=b.constructor;return"destroy"==c&&h.shift(),"create"!==c&&h.unshift(d(b)),j=k[c].apply(k,h),i=j.pipe(function(a){return b[g||c+"d"](a,j),b}),j.abort&&(i.abort=function(){j.abort()}),i.then(e,f),i},g={create:{url:"_shortName",type:"post"},update:{data:function(b,c){c=c||{};var d=this.id;return c[d]&&c[d]!==b&&(c["new"+a.capitalize(b)]=c[d],delete c[d]),c[d]=b,c},type:"put"},destroy:{type:"delete",data:function(a){var b={};return b.id=b[this.id]=a,b}},findAll:{url:"_shortName"},findOne:{}},h=function(a,b){return function(c){return c=a.data?a.data.apply(this,arguments):c,e(b||this[a.url||"_url"],c,a.type||"get")}};a.Model=a.Observe({fullName:"can.Model",_reqs:0,setup:function(b){if(this.store={},a.Observe.setup.apply(this,arguments),a.Model){this.List=i({Observe:this},{});var d=this,e=a.proxy(this._clean,d);a.each(g,function(c,f){if(a.isFunction(d[f])||(d[f]=h(c,d[f])),d["make"+a.capitalize(f)]){var g=d["make"+a.capitalize(f)](d[f]);a.Construct._overwrite(d,b,f,function(){a.Model._reqs++;var b=g.apply(this,arguments),c=b.then(e,e);return c.abort=b.abort,c})}}),"can.Model"!=d.fullName&&d.fullName||(d.fullName="Model"+ ++c),a.Model._reqs=0,this._url=this._shortName+"/{"+this.id+"}"}},_ajax:h,_makeRequest:f,_clean:function(){if(a.Model._reqs--,!a.Model._reqs)for(var b in this.store)this.store[b]._bindings||delete this.store[b];return arguments[0]},models:function(b,c){if(a.Model._reqs++,b){if(b instanceof this.List)return b;var d=this,e=[],f=c instanceof a.Observe.List?c:new(d.List||i),g=a.isArray(b),h=b instanceof i,j=g?b:h?b.serialize():b.data;return f.length&&f.splice(0),a.each(j,function(a){e.push(d.model(a))}),f.push.apply(f,e),g||a.each(b,function(a,b){"data"!==b&&f.attr(b,a)}),setTimeout(a.proxy(this._clean,this),1),f}},model:function(b){if(b){b instanceof this&&(b=b.serialize());var c=b[this.id],d=(c||0===c)&&this.store[c]?this.store[c].attr(b,this.removeAttr||!1):new this(b);return a.Model._reqs&&(this.store[b[this.id]]=d),d}}},{isNew:function(){var a=d(this);return!(a||0===a)},save:function(a,b){return f(this,this.isNew()?"create":"update",a,b)},destroy:function(b,c){if(this.isNew()){var d=this,e=a.Deferred();return e.then(b,c),e.done(function(a){d.destroyed(a)}).resolve(d)}return f(this,"destroy",b,c,"destroyed")},_bindsetup:function(){return this.constructor.store[this.__get(this.constructor.id)]=this,a.Observe.prototype._bindsetup.apply(this,arguments)},_bindteardown:function(){return delete this.constructor.store[d(this)],a.Observe.prototype._bindteardown.apply(this,arguments)},___set:function(b,c){a.Observe.prototype.___set.call(this,b,c),b===this.constructor.id&&this._bindings&&(this.constructor.store[d(this)]=this)}}),a.each({makeFindAll:"models",makeFindOne:"model",makeCreate:"model",makeUpdate:"model"},function(c,d){a.Model[d]=function(d){return function(){var e=a.makeArray(arguments),f=a.isFunction(e[1])?e.splice(0,1):e.splice(0,2),g=b(d.apply(this,f),this,c);return g.then(e[0],e[1]),g}}}),a.each(["created","updated","destroyed"],function(b){a.Model.prototype[b]=function(c){var d,e=this.constructor;d=c&&"object"==typeof c&&this.attr(c.attr?c.attr():c),a.trigger(this,"change",b),a.trigger(e,b,this)}});var i=a.Model.List=a.Observe.List({setup:function(b){a.isPlainObject(b)&&!a.isArray(b)?(a.Observe.List.prototype.setup.apply(this),this.replace(this.constructor.Observe.findAll(b))):a.Observe.List.prototype.setup.apply(this,arguments)},_changes:function(b,c){if(a.Observe.List.prototype._changes.apply(this,arguments),/\w+\.destroyed/.test(c)){var d=this.indexOf(b.target);-1!=d&&this.splice(d,1)}}});return a.Model}(__m3,__m7),__m11=function(a){var b=a.isFunction,c=a.makeArray,d=1,e=a.view=a.template=function(c,d,f,g){b(f)&&(g=f,f=undefined);var h=function(a){return e.frag(a)},i=b(g)?function(a){g(h(a))}:null,j=e.render(c,d,f,i),k=a.Deferred();return b(j)?j:a.isDeferred(j)?(j.then(function(a,b){k.resolve.call(k,h(a),b)},function(){k.fail.apply(k,arguments)}),k):h(j)};a.extend(e,{frag:function(a,b){return e.hookup(e.fragment(a),b)},fragment:function(b){var c=a.buildFragment(b,document.body);return c.childNodes.length||c.appendChild(document.createTextNode("")),c},toId:function(b){return a.map(b.toString().split(/\/|\./g),function(a){return a?a:void 0}).join("_")},hookup:function(b,c){var d,f,g=[];return a.each(b.childNodes?a.makeArray(b.childNodes):b,function(b){1===b.nodeType&&(g.push(b),g.push.apply(g,a.makeArray(b.getElementsByTagName("*"))))}),a.each(g,function(a){a.getAttribute&&(d=a.getAttribute("data-view-id"))&&(f=e.hookups[d])&&(f(a,c,d),delete e.hookups[d],a.removeAttribute("data-view-id"))}),b},hookups:{},hook:function(a){return e.hookups[++d]=a," data-view-id='"+d+"'"},cached:{},cachedRenderers:{},cache:!0,register:function(a){this.types["."+a.suffix]=a},types:{},ext:".ejs",registerScript:function(){},preload:function(){},render:function(d,f,j,k){b(j)&&(k=j,j=undefined);var l=h(f);if(l.length){var m=new a.Deferred,n=a.extend({},f);return l.push(g(d,!0)),a.when.apply(a,l).then(function(b){var d,e=c(arguments),g=e.pop();if(a.isDeferred(f))n=i(b);else for(var h in f)a.isDeferred(f[h])&&(n[h]=i(e.shift()));d=g(n,j),m.resolve(d,n),k&&k(d,n)},function(){m.reject.apply(m,arguments)}),m}var o,p=b(k),m=g(d,p);if(p)o=m,m.then(function(a){k(f?a(f,j):a)});else{if("resolved"===m.state()&&m.__view_id){var q=e.cachedRenderers[m.__view_id];return f?q(f,j):q}m.then(function(a){o=f?a(f,j):a})}return o},registerView:function(b,c,d,f){var g=(d||e.types[e.ext]).renderer(b,c);return f=f||new a.Deferred,e.cache&&(e.cached[b]=f,f.__view_id=b,e.cachedRenderers[b]=g),f.resolve(g)}});var f=function(a,b){if(!a.length)throw"can.view: No template or empty template:"+b},g=function(b,c){var d,g,h,i=b.match(/\.[\w\d]+$/);if(b.match(/^#/)&&(b=b.substr(1)),(g=document.getElementById(b))&&(i="."+g.type.match(/\/(x\-)?(.+)/)[2]),i||e.cached[b]||(b+=i=e.ext),a.isArray(i)&&(i=i[0]),h=e.toId(b),b.match(/^\/\//)){var j=b.substr(2);b=window.steal?steal.config().root.mapJoin(""+steal.id(j)):j}if(d=e.types[i],e.cached[h])return e.cached[h];if(g)return e.registerView(h,g.innerHTML,d);var k=new a.Deferred;return a.ajax({async:c,url:b,dataType:"text",error:function(a){f("",b),k.reject(a)},success:function(a){f(a,b),e.registerView(h,a,d,k)}}),k},h=function(b){var c=[];if(a.isDeferred(b))return[b];for(var d in b)a.isDeferred(b[d])&&c.push(b[d]);return c},i=function(b){return a.isArray(b)&&"success"===b[1]?b[0]:b};return window.steal&&steal.type("view js",function(a,b){var c=e.types["."+a.type],d=e.toId(a.id);a.text="steal('"+(c.plugin||"can/view/"+a.type)+"',function(can){return "+"can.view.preload('"+d+"',"+a.text+");\n})",b()}),a.extend(e,{register:function(b){this.types["."+b.suffix]=b,window.steal&&steal.type(b.suffix+" view js",function(a,b){var c=e.types["."+a.type],d=e.toId(a.id+"");a.text=c.script(d,a.text),b()}),e[b.suffix]=function(c,d){if(!d){var f=function(){return e.frag(f.render.apply(this,arguments))};return f.render=function(){var a=b.renderer(null,c);return a.apply(a,arguments)},f}return e.preload(c,b.renderer(c,d)),a.view(c)}},registerScript:function(a,b,c){return"can.view.preload('"+b+"',"+e.types["."+a].script(b,c)+");"},preload:function(b,c){function d(){return e.frag(c.apply(this,arguments))}return e.cached[b]=(new a.Deferred).resolve(function(a,b){return c.call(a,a,b)}),d.render=c,d}}),a}(__m3),__m14=function(){var a={tagToContentPropMap:{option:"textContent"in document.createElement("option")?"textContent":"innerText",textarea:"value"},attrMap:{"class":"className",value:"value",innerText:"innerText",textContent:"textContent",checked:!0,disabled:!0,readonly:!0,required:!0},defaultValue:["input","textarea"],tagMap:{"":"span",table:"tbody",tr:"td",ol:"li",ul:"li",tbody:"tr",thead:"tr",tfoot:"tr",select:"option",optgroup:"option"},reverseTagMap:{tr:"tbody",option:"select",td:"tr",th:"tr",li:"ul"},getParentNode:function(a,b){return b&&11===a.parentNode.nodeType?b:a.parentNode},setAttr:function(b,c,d){var e=b.nodeName.toString().toLowerCase(),f=a.attrMap[c];f===!0?b[c]=!0:f?(b[f]=d,"value"===f&&can.inArray(e,a.defaultValue)>=0&&(b.defaultValue=d)):b.setAttribute(c,d)},getAttr:function(b,c){return(a.attrMap[c]&&b[a.attrMap[c]]?b[a.attrMap[c]]:b.getAttribute(c))||""},removeAttr:function(b,c){a.attrMap[c]===!0?b[c]=!1:b.removeAttribute(c)},contentText:function(a){return"string"==typeof a?a:a||0===a?""+a:""}};return a}(),__m13=function(can,elements){var newLine=/(\r|\n)+/g,clean=function(a){return a.split("\\").join("\\\\").split("\n").join("\\n").split('"').join('\\"').split(" ").join("\\t")},getTag=function(a,b,c){if(a)return a;for(;c<b.length;){if("<"==b[c]&&elements.reverseTagMap[b[c+1]])return elements.reverseTagMap[b[c+1]];c++}return""},bracketNum=function(a){return--a.split("{").length- --a.split("}").length},myEval=function(script){eval(script)},attrReg=/([^\s]+)[\s]*=[\s]*$/,startTxt="var ___v1ew = [];",finishTxt="return ___v1ew.join('')",put_cmd="___v1ew.push(",insert_cmd=put_cmd,htmlTag=null,quote=null,beforeQuote=null,rescan=null,status=function(){return quote?"'"+beforeQuote.match(attrReg)[1]+"'":htmlTag?1:0};return can.view.Scanner=Scanner=function(a){can.extend(this,{text:{},tokens:[]},a),this.tokenReg=[],this.tokenSimple={"<":"<",">":">",'"':'"',"'":"'"},this.tokenComplex=[],this.tokenMap={};for(var b,c=0;b=this.tokens[c];c++)b[2]?(this.tokenReg.push(b[2]),this.tokenComplex.push({abbr:b[1],re:new RegExp(b[2]),rescan:b[3]})):(this.tokenReg.push(b[1]),this.tokenSimple[b[1]]=b[0]),this.tokenMap[b[0]]=b[1];this.tokenReg=new RegExp("("+this.tokenReg.slice(0).concat(["<",">",'"',"'"]).join("|")+")","g")},Scanner.prototype={helpers:[{name:/\s*\(([\$\w]+)\)\s*->([^\n]*)/,fn:function(a){var b=/\s*\(([\$\w]+)\)\s*->([^\n]*)/,c=a.match(b);return"can.proxy(function(__){var "+c[1]+"=can.$(__);"+c[2]+"}, this);"}}],scan:function(a,b){var c=[],d=0,e=this.tokenSimple,f=this.tokenComplex;a=a.replace(newLine,"\n"),this.transform&&(a=this.transform(a)),a.replace(this.tokenReg,function(b,g){var h=arguments[arguments.length-2];if(h>d&&c.push(a.substring(d,h)),e[b])c.push(b);else for(var i,j=0;i=f[j];j++)if(i.re.test(b)){c.push(i.abbr),i.rescan&&c.push(i.rescan(g));break}d=h+g.length}),d<a.length&&c.push(a.substr(d));var g,h,i,j="",k=[startTxt+(this.text.start||"")],l=function(a,b){k.push(put_cmd,'"',clean(a),'"'+(b||"")+");")},m=[],n=null,o=!1,p="",q=[],r=!1,s=0,t=this.tokenMap;for(htmlTag=quote=beforeQuote=null;(i=c[s++])!==undefined;){if(null===n)switch(i){case t.left:case t.escapeLeft:case t.returnLeft:o=htmlTag&&1;case t.commentLeft:n=i,j.length&&l(j),j="";break;case t.escapeFull:o=htmlTag&&1,rescan=1,n=t.escapeLeft,j.length&&l(j),rescan=c[s++],j=rescan.content||rescan,rescan.before&&l(rescan.before),c.splice(s,0,t.right);break;case t.commentFull:break;case t.templateLeft:j+=t.left;break;case"<":0!==c[s].indexOf("!--")&&(htmlTag=1,o=0),j+=i;break;case">":htmlTag=0;var u="/"==j.substr(j.length-1)||"--"==j.substr(j.length-2);o||!r&&elements.tagToContentPropMap[q[q.length-1]]?(u?l(j.substr(0,j.length-1),',can.view.pending(),"/>"'):l(j,',can.view.pending(),">"'),j="",o=0):j+=i,(u||r)&&(q.pop(),p=q[q.length-1],r=!1);break;case"'":case'"':htmlTag&&(quote&"e===i?quote=null:null===quote&&(quote=i,beforeQuote=g));default:"<"===g&&(p=i.split(/\s/)[0],0===p.indexOf("/")&&q[q.length-1]===p.substr(1)?(p=q[q.length-1],r=!0):q.push(p)),j+=i}else switch(i){case t.right:case t.returnRight:switch(n){case t.left:h=bracketNum(j),1==h?(k.push(insert_cmd,"can.view.txt(0,'"+getTag(p,c,s)+"',"+status()+",this,function(){",startTxt,j),m.push({before:"",after:finishTxt+"}));\n"})):(d=m.length&&-1==h?m.pop():{after:";"},d.before&&k.push(d.before),k.push(j,";",d.after));break;case t.escapeLeft:case t.returnLeft:h=bracketNum(j),h&&m.push({before:finishTxt,after:"}));"});for(var v=n===t.escapeLeft?1:0,w={insert:insert_cmd,tagName:getTag(p,c,s),status:status()},x=0;x<this.helpers.length;x++){var y=this.helpers[x];if(y.name.test(j)){j=y.fn(j,w),y.name.source==/^>[\s]*\w*/.source&&(v=0);break}}"object"==typeof j?j.raw&&k.push(j.raw):k.push(insert_cmd,"can.view.txt("+v+",'"+p+"',"+status()+",this,function(){ "+(this.text.escape||"")+"return ",j,h?startTxt:"}));"),rescan&&rescan.after&&rescan.after.length&&(l(rescan.after.length),rescan=null)}n=null,j="";break;case t.templateLeft:j+=t.left;break;default:j+=i}g=i}j.length&&l(j),k.push(";");var z=k.join(""),A={out:"with(_VIEW) { with (_CONTEXT) {"+z+" "+finishTxt+"}}"};return myEval.call(A,"this.fn = (function(_CONTEXT,_VIEW){"+A.out+"});\r\n//@ sourceURL="+b+".js"),A}},Scanner}(__m11,__m14),__m17=function(a){var b=!0;try{document.createTextNode("")._=0}catch(c){b=!1}var d={},e={},f={},g="ejs_"+Math.random(),h=0,i=function(a){if(b||3!==a.nodeType)return a[g]?a[g]:a[g]=(a.nodeName?"element_":"obj_")+ ++h;for(var c in e)if(e[c]===a)return c;return e["text_"+ ++h]=a,"text_"+h},j=function(b,c){var e=d[i(b)];if(e){var f=a.inArray(c,e);f>=0&&e.splice(f,1),e.length||delete d[i(b)]}},k=function(a,b){var c=d[i(a)];c||(c=d[i(a)]=[]),c.push(b)},l={id:i,replace:function(b,c){b=a.makeArray(b);var e=b[0];a.each(a.makeArray(d[i(e)]),function(d){var g=f[d],h=a.inArray(e,g),i=a.inArray(b[b.length-1],g);if(h>=0&&i>=0){for(var m=h;i>=m;m++){var n=g[m];j(n,d)}g.splice.apply(g,[h,i-h+1].concat(c)),a.each(c,function(a){k(a,d)})}else l.unregister(g)})},register:function(b){var c=i(b);f[c]=b,a.each(b,function(a){k(a,c)})},unregister:function(b){var c=i(b);a.each(b,function(a){j(a,c)}),delete f[c]},nodeMap:d,nodeListMap:f};return l}(__m3),__m16=function(a,b,c,d){var e=function(b,c,d){var e=function(){d(f),a.unbind.call(b,"destroyed",e)},f={teardownCheck:function(a){a||e()}};return a.bind.call(b,"destroyed",e),c(f),f},f=function(a,b,c){return e(a,function(){b.bind("change",c)},function(a){b.unbind("change",c),a.nodeList&&d.unregister(a.nodeList)})},g=function(a){return(a||"").replace(/['"]/g,"").split("=")};insertElementsAfter=function(a,b){var c=a[a.length-1];c.nextSibling?c.parentNode.insertBefore(b,c.nextSibling):c.parentNode.appendChild(b)};var h={nodeLists:d,list:function(c,f,g,h,i){var j=[],k=function(b,c,e){var f=document.createDocumentFragment(),k=[];if(a.each(c,function(b){var c=g.call(h,b),d=a.view.frag(c,i);k.push(a.makeArray(d.childNodes)),f.appendChild(d)}),j[e]){var l=j[e][0];l.parentNode.insertBefore(f,l)}else insertElementsAfter(0==e?[m]:j[e-1],f);a.each(k,function(a){d.register(a)}),[].splice.apply(j,[e,0].concat(k))},l=function(b,c,e){var f=j.splice(e,c.length),g=[];a.each(f,function(a){[].push.apply(g,a),d.replace(a,[]),d.unregister(a)}),a.remove(a.$(g))},i=b.getParentNode(c,i),m=document.createTextNode("");e(i,function(){f.bind("add",k).bind("remove",l)},function(){f.unbind("add",k).unbind("remove",l),a.each(j,function(a){d.unregister(a)})}),insertElementsAfter([c],m),a.remove(a.$(c)),k({},f,0)},html:function(c,e,g){var h,g=b.getParentNode(c,g),i=f(g,e,function(a,b){var c=h[0].parentNode;c&&j(b),i.teardownCheck(h[0].parentNode)}),j=function(b){var e=a.view.frag(b,g),f=a.makeArray(e.childNodes);if(insertElementsAfter(h||[c],e),h){var j=a.makeArray(h);d.replace(h,f),a.remove(a.$(j))}else a.remove(a.$(c)),h=f,i.nodeList=h,d.register(h)};j(e(),[c])},text:function(a,c,d){var e=b.getParentNode(a,d),g=f(a.parentNode!==e?a.parentNode:e,c,function(a,b){"unknown"!=typeof h.nodeValue&&(h.nodeValue=""+b),g.teardownCheck(h.parentNode)}),h=document.createTextNode(c());a.parentNode!==e?(e=a.parentNode,e.insertBefore(h,a),e.removeChild(a)):(e.insertBefore(h,a),e.removeChild(a))},attributes:function(a,c,d){var e=function(c){var d=g(c),e=d.shift();e!=h&&h&&b.removeAttr(a,h),e&&(b.setAttr(a,e,d.join("=")),h=e)};if(f(a,c,function(a,b){e(b)}),arguments.length>=3)var h=g(d)[0];else e(c())},attributePlaceholder:"__!!__",attributeReplace:/__!!__/g,attribute:function(c,d,e){f(c,e,function(){b.setAttr(c,d,j.render())});var g,i=a.$(c);g=a.data(i,"hooks"),g||a.data(i,"hooks",g={});var j,k=b.getAttr(c,d),l=k.split(h.attributePlaceholder),m=[];m.push(l.shift(),l.join(h.attributePlaceholder)),g[d]?g[d].computes.push(e):g[d]={render:function(){var a=0,c=k?k.replace(h.attributeReplace,function(){return b.contentText(j.computes[a++]())}):b.contentText(j.computes[a++]());return c},computes:[e],batchNum:undefined},j=g[d],m.splice(1,0,e()),b.setAttr(c,d,m.join(""))}};return h}(__m3,__m14,__m11,__m17),__m15=function(a,b,c){var d=[],e=function(a){var c=b.tagMap[a]||"span";return"span"===c?"@@!!@@":"<"+c+">"+e(c)+"</"+c+">"},f=function(b,c){if("string"==typeof b)return b;if(!b&&0!==b)return"";var e=b.hookup&&function(a,c){b.hookup.call(b,a,c)}||"function"==typeof b&&b;return e?c?"<"+c+" "+a.view.hook(e)+"></"+c+">":(d.push(e),""):""+b},g=function(b){return"string"==typeof b||"number"==typeof b?a.esc(b):f(b)};return a.extend(a.view,{live:c,setupLists:function(){var b,c=a.view.lists;return a.view.lists=function(a,c){b={list:a,renderer:c}},function(){return a.view.lists=c,b}},pending:function(){var b=d.slice(0);return lastHookups=b,d=[],a.view.hook(function(c){a.each(b,function(a){a(c)})})},txt:function(h,i,j,k,l){var m=a.view.setupLists(),n=function(){},o=function(){p.unbind("change",n)},p=a.compute(l,k,!1);p.bind("change",n);var q=b.tagMap[i]||"span",r=m(),s=p();if(r)return"<"+q+a.view.hook(function(a,b){c.list(a,r.list,r.renderer,k,b)})+"></"+q+">";if(!p.hasDependencies)return o(),(h||0!==j?g:f)(s,0===j&&q);var t=b.tagToContentPropMap[i];if(0!==j||t){if(1===j)return d.push(function(a){c.attributes(a,p,p()),o()}),p();var u=0===j?t:j;return(0===j?lastHookups:d).push(function(a){c.attribute(a,u,p),o() +}),c.attributePlaceholder}return"<"+q+a.view.hook(h?function(a,b){c.text(a,p,b),o()}:function(a,b){c.html(a,p,b),o()})+">"+e(q)+"</"+q+">"}}),a}(__m11,__m14,__m16,__m2),__m12=function(a){var b=a.extend,c=function(a){if(this.constructor!=c){var d=new c(a);return function(a,b){return d.render(a,b)}}return"function"==typeof a?(this.template={fn:a},void 0):(b(this,a),this.template=this.scanner.scan(this.text,this.name),void 0)};return a.EJS=c,c.prototype.render=function(a,b){return a=a||{},this.template.fn.call(a,a,new c.Helpers(a,b||{}))},b(c.prototype,{scanner:new a.view.Scanner({tokens:[["templateLeft","<%%"],["templateRight","%>"],["returnLeft","<%=="],["escapeLeft","<%="],["commentLeft","<%#"],["left","<%"],["right","%>"],["returnRight","%>"]],transform:function(a){return a.replace(/<%([\s\S]+?)%>/gm,function(a,b){var c,d,e=[];b.replace(/[{}]/gm,function(a,b){e.push([a,b])});do for(c=!1,d=e.length-2;d>=0;d--)if("{"==e[d][0]&&"}"==e[d+1][0]){e.splice(d,2),c=!0;break}while(c);if(e.length>=2){var f,g=["<%"],h=0;for(d=0;f=e[d];d++)g.push(b.substring(h,h=f[1])),"{"==f[0]&&d<e.length-1||"}"==f[0]&&d>0?g.push("{"==f[0]?"{ %><% ":" %><% }"):g.push(f[0]),++h;return g.push(b.substring(h),"%>"),g.join("")}return"<%"+b+"%>"})}})}),c.Helpers=function(a,c){this._data=a,this._extras=c,b(this,c)},c.Helpers.prototype={list:function(b,c){a.each(b,function(a,d){c(a,d,b)})},each:function(b,c){a.isArray(b)?this.list(b,c):a.view.lists(b,c)}},a.view.register({suffix:"ejs",script:function(a,b){return"can.EJS(function(_CONTEXT,_VIEW) { "+new c({text:b,name:a}).template.out+" })"},renderer:function(a,b){return c({text:b,name:a})}}),a}(__m3,__m11,__m2,__m9,__m13,__m15),__m18=function(a){var b,c=function(b,c,d){return a.bind.call(b,c,d),function(){a.unbind.call(b,c,d)}},d=a.isFunction,e=a.extend,f=a.each,g=[].slice,h=/\{([^\}]+)\}/g,i=a.getObject("$.event.special",[a])||{},j=function(b,c,d,e){return a.delegate.call(b,c,d,e),function(){a.undelegate.call(b,c,d,e)}},k=function(b,d,e,f){return f?j(b,a.trim(f),d,e):c(b,d,e)},l=a.Control=a.Construct({setup:function(){if(a.Construct.setup.apply(this,arguments),a.Control){var b,c=this;c.actions={};for(b in c.prototype)c._isAction(b)&&(c.actions[b]=c._action(b))}},_shifter:function(b,c){var e="string"==typeof c?b[c]:c;return d(e)||(e=b[e]),function(){return b.called=c,e.apply(b,[this.nodeName?a.$(this):this].concat(g.call(arguments,0)))}},_isAction:function(a){var b=this.prototype[a],c=typeof b;return"constructor"!==a&&("function"==c||"string"==c&&d(this.prototype[b]))&&!!(i[a]||m[a]||/[^\w]/.test(a))},_action:function(c,d){if(h.lastIndex=0,d||!h.test(c)){var e=d?a.sub(c,[d,window]):c;if(!e)return null;var f=a.isArray(e),g=f?e[1]:e,i=g.split(/\s+/g),j=i.pop();return{processor:m[j]||b,parts:[g,i.join(" "),j],delegate:f?e[0]:undefined}}},processors:{},defaults:{}},{setup:function(b,c){var d,f=this.constructor,g=f.pluginName||f._fullName;return this.element=a.$(b),g&&"can_control"!==g&&this.element.addClass(g),(d=a.data(this.element,"controls"))||a.data(this.element,"controls",d=[]),d.push(this),this.options=e({},f.defaults,c),this.on(),[this.element,this.options]},on:function(b,c,d,e){if(!b){this.off();var f,g,h=this.constructor,i=this._bindings,j=h.actions,l=this.element,m=a.Control._shifter(this,"destroy");for(f in j)j.hasOwnProperty(f)&&(g=j[f]||h._action(f,this.options))&&i.push(g.processor(g.delegate||l,g.parts[2],g.parts[1],f,this));return a.bind.call(l,"destroyed",m),i.push(function(b){a.unbind.call(b,"destroyed",m)}),i.length}return"string"==typeof b&&(e=d,d=c,c=b,b=this.element),e===undefined&&(e=d,d=c,c=null),"string"==typeof e&&(e=a.Control._shifter(this,e)),this._bindings.push(k(b,d,e,c)),this._bindings.length},off:function(){var a=this.element[0];f(this._bindings||[],function(b){b(a)}),this._bindings=[]},destroy:function(){if(null!==this.element){var b,c=this.constructor,d=c.pluginName||c._fullName;this.off(),d&&"can_control"!==d&&this.element.removeClass(d),b=a.data(this.element,"controls"),b.splice(a.inArray(this,b),1),a.trigger(this,"destroyed"),this.element=null}}}),m=a.Control.processors,b=function(b,c,d,e,f){return k(b,c,a.Control._shifter(f,e),d)};return f(["change","click","contextmenu","dblclick","keydown","keyup","keypress","mousedown","mousemove","mouseout","mouseover","mouseup","reset","resize","scroll","select","submit","focusin","focusout","mouseenter","mouseleave","touchstart","touchmove","touchcancel","touchend","touchleave"],function(a){m[a]=b}),l}(__m3,__m1),__m20=function(a){var b=/^\d+$/,c=/([^\[\]]+)|(\[\])/g,d=/([^?#]*)(#.*)?$/,e=function(a){return decodeURIComponent(a.replace(/\+/g," "))};return a.extend(a,{deparam:function(f){var g,h,i={};return f&&d.test(f)&&(g=f.split("&"),a.each(g,function(a){var d=a.split("="),f=e(d.shift()),g=e(d.join("=")),j=i;if(f){d=f.match(c);for(var k=0,l=d.length-1;l>k;k++)j[d[k]]||(j[d[k]]=b.test(d[k+1])||"[]"==d[k+1]?[]:{}),j=j[d[k]];h=d.pop(),"[]"==h?j.push(g):j[h]=g}})),i}}),a}(__m3,__m2),__m19=function(a){var b=/\:([\w\.]+)/g,c=/^(?:&[^=]+=[^&]*)+/,d=function(b){var c=[];return a.each(b,function(b,d){c.push(("className"===d?"class":d)+'="'+("href"===d?b:a.esc(b))+'"')}),c.join(" ")},e=function(a,b){var c=0,d=0,e={};for(var f in a.defaults)a.defaults[f]===b[f]&&(e[f]=1,c++);for(;d<a.names.length;d++){if(!b.hasOwnProperty(a.names[d]))return-1;e[a.names[d]]||c++}return c},f=!0,g=window.location,h=function(a){return(a+"").replace(/([.?*+\^$\[\]\\(){}|\-])/g,"\\$1")},i=a.each,j=a.extend;a.route=function(c,d){d=d||{};var e=[],f=c.replace(b,function(b,f,g){e.push(f);var h="\\"+(c.substr(g+b.length,1)||a.route._querySeparator);return"([^"+h+"]"+(d[f]?"*":"+")+")"});return a.route.routes[c]={test:new RegExp("^"+f+"($|"+h(a.route._querySeparator)+")"),route:c,names:e,defaults:d,length:c.split("/").length},a.route},j(a.route,{_querySeparator:"&",_paramsMatcher:c,param:function(c,d){var f,g,h=0,k=c.route,l=0;if(delete c.route,i(c,function(){l++}),i(a.route.routes,function(a){return g=e(a,c),g>h&&(f=a,h=g),g>=l?!1:void 0}),a.route.routes[k]&&e(a.route.routes[k],c)===h&&(f=a.route.routes[k]),f){var m,n=j({},c),o=f.route.replace(b,function(a,b){return delete n[b],c[b]===f.defaults[b]?"":encodeURIComponent(c[b])});return i(f.defaults,function(a,b){n[b]===a&&delete n[b]}),m=a.param(n),d&&a.route.attr("route",f.route),o+(m?a.route._querySeparator+m:"")}return a.isEmptyObject(c)?"":a.route._querySeparator+a.param(c)},deparam:function(b){var c={length:-1};if(i(a.route.routes,function(a){a.test.test(b)&&a.length>c.length&&(c=a)}),c.length>-1){var d=b.match(c.test),e=d.shift(),f=b.substr(e.length-(d[d.length-1]===a.route._querySeparator?1:0)),g=f&&a.route._paramsMatcher.test(f)?a.deparam(f.slice(1)):{};return g=j(!0,{},c.defaults,g),i(d,function(b,d){b&&b!==a.route._querySeparator&&(g[c.names[d]]=decodeURIComponent(b))}),g.route=c.route,g}return b.charAt(0)!==a.route._querySeparator&&(b=a.route._querySeparator+b),a.route._paramsMatcher.test(b)?a.deparam(b.slice(1)):{}},data:new a.Observe({}),routes:{},ready:function(b){return b===!1&&(f=b),(b===!0||f===!0)&&(a.route._setup(),o()),a.route},url:function(b,c){return c&&(b=j({},l,b)),"#!"+a.route.param(b)},link:function(b,c,e,f){return"<a "+d(j({href:a.route.url(c,f)},e))+">"+b+"</a>"},current:function(b){return g.hash=="#!"+a.route.param(b)},_setup:function(){a.bind.call(window,"hashchange",o)},_getHash:function(){return g.href.split(/#!?/)[1]||""},_setHash:function(b){var c=a.route.param(b,!0);return g.hash="#!"+c,c}}),i(["bind","unbind","delegate","undelegate","attr","removeAttr"],function(b){a.route[b]=function(){return a.route.data[b]?a.route.data[b].apply(a.route.data,arguments):void 0}});var k,l,m,n,o=a.route.setState=function(){var b=a.route._getHash();l=a.route.deparam(b),n&&b===m||a.route.attr(l,!0)};return a.route.bind("change",function(){n=1,clearTimeout(k),k=setTimeout(function(){n=0;var b=a.route.data.serialize();m=a.route._setHash(b)},1)}),a.bind.call(document,"ready",a.route.ready),"complete"!==document.readyState&&"interactive"!==document.readyState||!f||a.route.ready(),a.route.constructor.canMakeObserve=a.Observe.canMakeObserve,a.route}(__m3,__m7,__m20),__m21=function(a){return a.Control.processors.route=function(b,c,d,e,f){d=d||"",a.route(d);var g,h=function(b){if(a.route.attr("route")===d&&(b.batchNum===undefined||b.batchNum!==g)){g=b.batchNum;var c=a.route.attr();delete c.route,a.isFunction(f[e])?f[e](c):f[f[e]](c)}};return a.route.bind("change",h),function(){a.route.unbind("change",h)}},a}(__m3,__m19,__m18);window.can=__m5}(); \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.object.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.object.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.object.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,137 @@ +/*! + * CanJS - 1.1.6 + * http://canjs.us/ + * Copyright (c) 2013 Bitovi + * Wed, 29 May 2013 18:59:29 GMT + * Licensed MIT + * Includes: can/util/object + * Download from: http://canjs.com + */ +(function(can) { + + var isArray = can.isArray, + // essentially returns an object that has all the must have comparisons ... + // must haves, do not return true when provided undefined + cleanSet = function(obj, compares) { + var copy = can.extend({}, obj); + for (var prop in copy) { + var compare = compares[prop] === undefined ? compares["*"] : compares[prop]; + if (same(copy[prop], undefined, compare)) { + delete copy[prop] + } + } + return copy; + }, + propCount = function(obj) { + var count = 0; + for (var prop in obj) count++; + return count; + }; + + can.Object = {}; + + var same = can.Object.same = function(a, b, compares, aParent, bParent, deep) { + var aType = typeof a, + aArray = isArray(a), + comparesType = typeof compares, + compare; + + if (comparesType == 'string' || compares === null) { + compares = compareMethods[compares]; + comparesType = 'function' + } + if (comparesType == 'function') { + return compares(a, b, aParent, bParent) + } + compares = compares || {}; + + if (a instanceof Date) { + return a === b; + } + if (deep === -1) { + return aType === 'object' || a === b; + } + if (aType !== typeof b || aArray !== isArray(b)) { + return false; + } + if (a === b) { + return true; + } + if (aArray) { + if (a.length !== b.length) { + return false; + } + for (var i = 0; i < a.length; i++) { + compare = compares[i] === undefined ? compares["*"] : compares[i] + if (!same(a[i], b[i], a, b, compare)) { + return false; + } + }; + return true; + } else if (aType === "object" || aType === 'function') { + var bCopy = can.extend({}, b); + for (var prop in a) { + compare = compares[prop] === undefined ? compares["*"] : compares[prop]; + if (!same(a[prop], b[prop], compare, a, b, deep === false ? -1 : undefined)) { + return false; + } + delete bCopy[prop]; + } + // go through bCopy props ... if there is no compare .. return false + for (prop in bCopy) { + if (compares[prop] === undefined || !same(undefined, b[prop], compares[prop], a, b, deep === false ? -1 : undefined)) { + return false; + } + } + return true; + } + return false; + }; + + can.Object.subsets = function(checkSet, sets, compares) { + var len = sets.length, + subsets = [], + checkPropCount = propCount(checkSet), + setLength; + + for (var i = 0; i < len; i++) { + //check this subset + var set = sets[i]; + if (can.Object.subset(checkSet, set, compares)) { + subsets.push(set) + } + } + return subsets; + }; + + can.Object.subset = function(subset, set, compares) { + // go through set {type: 'folder'} and make sure every property + // is in subset {type: 'folder', parentId :5} + // then make sure that set has fewer properties + // make sure we are only checking 'important' properties + // in subset (ones that have to have a value) + + var setPropCount = 0, + compares = compares || {}; + + for (var prop in set) { + + if (!same(subset[prop], set[prop], compares[prop], subset, set)) { + return false; + } + } + return true; + } + + var compareMethods = { + "null": function() { + return true; + }, + i: function(a, b) { + return ("" + a).toLowerCase() == ("" + b).toLowerCase() + } + } + + return can.Object; + + })(can); \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.observe.attributes.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.observe.attributes.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.observe.attributes.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,174 @@ +/*! + * CanJS - 1.1.6 + * http://canjs.us/ + * Copyright (c) 2013 Bitovi + * Wed, 29 May 2013 18:59:29 GMT + * Licensed MIT + * Includes: can/observe/attributes + * Download from: http://canjs.com + */ +(function(can, Observe) { + + can.each([can.Observe, can.Model], function(clss) { + // in some cases model might not be defined quite yet. + if (clss === undefined) { + return; + } + var isObject = function(obj) { + return typeof obj === 'object' && obj !== null && obj; + }; + + can.extend(clss, { + + attributes: {}, + + + convert: { + "date": function(str) { + var type = typeof str; + if (type === "string") { + return isNaN(Date.parse(str)) ? null : Date.parse(str) + } else if (type === 'number') { + return new Date(str) + } else { + return str + } + }, + "number": function(val) { + return parseFloat(val); + }, + "boolean": function(val) { + if (val === 'false' || val === '0' || !val) { + return false; + } + return true; + }, + "default": function(val, oldVal, error, type) { + var construct = can.getObject(type), + context = window, + realType; + // if type has a . we need to look it up + if (type.indexOf(".") >= 0) { + // get everything before the last . + realType = type.substring(0, type.lastIndexOf(".")); + // get the object before the last . + context = can.getObject(realType); + } + return typeof construct == "function" ? construct.call(context, val, oldVal) : val; + } + }, + + serialize: { + "default": function(val, type) { + return isObject(val) && val.serialize ? val.serialize() : val; + }, + "date": function(val) { + return val && val.getTime() + } + } + }); + + // overwrite setup to do this stuff + var oldSetup = clss.setup; + + + clss.setup = function(superClass, stat, proto) { + var self = this; + oldSetup.call(self, superClass, stat, proto); + + can.each(["attributes"], function(name) { + if (!self[name] || superClass[name] === self[name]) { + self[name] = {}; + } + }); + + can.each(["convert", "serialize"], function(name) { + if (superClass[name] != self[name]) { + self[name] = can.extend({}, superClass[name], self[name]); + } + }); + }; + }); + + var oldSetup = can.Observe.prototype.setup; + + can.Observe.prototype.setup = function(obj) { + + var diff = {}; + + oldSetup.call(this, obj); + + can.each(this.constructor.defaults, function(value, key) { + if (!this.hasOwnProperty(key)) { + diff[key] = value; + } + }, this); + + this._init = 1; + this.attr(diff); + delete this._init; + }; + + can.Observe.prototype.__convert = function(prop, value) { + // check if there is a + + var Class = this.constructor, + oldVal = this.attr(prop), + type, converter; + + if (Class.attributes) { + // the type of the attribute + type = Class.attributes[prop]; + converter = Class.convert[type] || Class.convert['default']; + } + + return value === null || !type ? + // just use the value + value : + // otherwise, pass to the converter + converter.call(Class, value, oldVal, function() {}, type); + }; + + can.Observe.prototype.serialize = function(attrName, stack) { + var where = {}, + Class = this.constructor, + attrs = {}; + + stack = can.isArray(stack) ? stack : []; + stack.push(this._cid); + + if (attrName !== undefined) { + attrs[attrName] = this[attrName]; + } else { + attrs = this.__get(); + } + + can.each(attrs, function(val, name) { + var type, converter; + + // If this is an observe, check that it wasn't serialized earlier in the stack. + if (val instanceof can.Observe && can.inArray(val._cid, stack) > -1) { + // Since this object has already been serialized once, + // just reference the id (or undefined if it doesn't exist). + where[name] = val.attr('id'); + } else { + type = Class.attributes ? Class.attributes[name] : 0; + converter = Class.serialize ? Class.serialize[type] : 0; + + // if the value is an object, and has a attrs or serialize function + where[name] = val && typeof val.serialize == 'function' ? + // call attrs or serialize to get the original data back + val.serialize(undefined, stack) : + // otherwise if we have a converter + converter ? + // use the converter + converter(val, type) : + // or return the val + val; + } + }); + + return attrName != undefined ? where[attrName] : where; + }; + return can.Observe; + })(can); \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.observe.delegate.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.observe.delegate.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/libs/can.observe.delegate.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,215 @@ +/*! + * CanJS - 1.1.6 + * http://canjs.us/ + * Copyright (c) 2013 Bitovi + * Wed, 29 May 2013 18:59:29 GMT + * Licensed MIT + * Includes: can/observe/delegate + * Download from: http://canjs.com + */ +(function(can) { + + + + // ** - 'this' will be the deepest item changed + // * - 'this' will be any changes within *, but * will be the + // this returned + + // tells if the parts part of a delegate matches the broken up props of the event + // gives the prop to use as 'this' + // - parts - the attribute name of the delegate split in parts ['foo','*'] + // - props - the split props of the event that happened ['foo','bar','0'] + // - returns - the attribute to delegate too ('foo.bar'), or null if not a match + var delegateMatches = function(parts, props) { + //check props parts are the same or + var len = parts.length, + i = 0, + // keeps the matched props we will use + matchedProps = [], + prop; + + // if the event matches + for (i; i < len; i++) { + prop = props[i] + // if no more props (but we should be matching them) + // return null + if (typeof prop !== 'string') { + return null; + } else + // if we have a "**", match everything + if (parts[i] == "**") { + return props.join("."); + } else + // a match, but we want to delegate to "*" + if (parts[i] == "*") { + // only do this if there is nothing after ... + matchedProps.push(prop); + } else if (prop === parts[i]) { + matchedProps.push(prop); + } else { + return null; + } + } + return matchedProps.join("."); + }, + // gets a change event and tries to figure out which + // delegates to call + delegateHandler = function(event, prop, how, newVal, oldVal) { + // pre-split properties to save some regexp time + var props = prop.split("."), + delegates = (this._observe_delegates || []).slice(0), + delegate, + attr, + matchedAttr, + hasMatch, + valuesEqual; + event.attr = prop; + event.lastAttr = props[props.length - 1]; + + // for each delegate + for (var i = 0; delegate = delegates[i++];) { + + // if there is a batchNum, this means that this + // event is part of a series of events caused by a single + // attrs call. We don't want to issue the same event + // multiple times + // setting the batchNum happens later + if ((event.batchNum && delegate.batchNum === event.batchNum) || delegate.undelegated) { + continue; + } + + // reset match and values tests + hasMatch = undefined; + valuesEqual = true; + + // yeah, all this under here has to be redone v + + // for each attr in a delegate + for (var a = 0; a < delegate.attrs.length; a++) { + + attr = delegate.attrs[a]; + + // check if it is a match + if (matchedAttr = delegateMatches(attr.parts, props)) { + hasMatch = matchedAttr; + } + // if it has a value, make sure it's the right value + // if it's set, we should probably check that it has a + // value no matter what + if (attr.value && valuesEqual) { + valuesEqual = attr.value === "" + this.attr(attr.attr) + } else if (valuesEqual && delegate.attrs.length > 1) { + // if there are multiple attributes, each has to at + // least have some value + valuesEqual = this.attr(attr.attr) !== undefined + } + } + + + // if there is a match and valuesEqual ... call back + if (hasMatch && valuesEqual) { + // how to get to the changed property from the delegate + var from = prop.replace(hasMatch + ".", ""); + + // if this event is part of a batch, set it on the delegate + // to only send one event + if (event.batchNum) { + delegate.batchNum = event.batchNum + } + + // if we listen to change, fire those with the same attrs + // TODO: the attrs should probably be using from + if (delegate.event === 'change') { + arguments[1] = from; + event.curAttr = hasMatch; + delegate.callback.apply(this.attr(hasMatch), can.makeArray(arguments)); + } else if (delegate.event === how) { + + // if it's a match, callback with the location of the match + delegate.callback.apply(this.attr(hasMatch), [event, newVal, oldVal, from]); + } else if (delegate.event === 'set' && + how == 'add') { + // if we are listening to set, we should also listen to add + delegate.callback.apply(this.attr(hasMatch), [event, newVal, oldVal, from]); + } + } + + } + }; + + can.extend(can.Observe.prototype, { + + delegate: function(selector, event, handler) { + selector = can.trim(selector); + var delegates = this._observe_delegates || (this._observe_delegates = []), + attrs = [], + selectorRegex = /([^\s=,]+)(?:=("[^",]*"|'[^',]*'|[^\s"',]*))?(,?)\s*/g, + matches; + + // parse each property in the selector + while (matches = selectorRegex.exec(selector)) { + // we need to do a little doctoring to make up for the quotes. + if (matches[2] && can.inArray(matches[2].substr(0, 1), ['"', "'"]) >= 0) { + matches[2] = matches[2].substr(1, -1); + } + + attrs.push({ + // the attribute name + attr: matches[1], + // the attribute name, pre-split for speed + parts: matches[1].split('.'), + // the value associated with this property (if there was one given) + value: matches[2], + // whether this selector combines with the one after it with AND or OR + or: matches[3] === ',' + }); + } + + // delegates has pre-processed info about the event + delegates.push({ + // the attrs name for unbinding + selector: selector, + // an object of attribute names and values {type: 'recipe',id: undefined} + // undefined means a value was not defined + attrs: attrs, + callback: handler, + event: event + }); + if (delegates.length === 1) { + this.bind("change", delegateHandler) + } + return this; + }, + + undelegate: function(selector, event, handler) { + selector = selector && can.trim(selector); + + var i = 0, + delegates = this._observe_delegates || [], + delegateOb; + if (selector) { + while (i < delegates.length) { + delegateOb = delegates[i]; + if (delegateOb.callback === handler || + (!handler && delegateOb.selector === selector)) { + delegateOb.undelegated = true; + delegates.splice(i, 1) + } else { + i++; + } + } + } else { + // remove all delegates + delegates = []; + } + if (!delegates.length) { + //can.removeData(this, "_observe_delegates"); + this.unbind("change", delegateHandler) + } + return this; + } + }); + // add helpers for testing .. + can.Observe.prototype.delegate.matches = delegateMatches; + return can.Observe; + })(can); \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/models/comments.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/models/comments.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/models/comments.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,48 @@ +var Comment = can.Model({ + findAll: 'GET /poll/{pollId}/comments', +// findOne: 'GET /comments/{commentId}', + create : "POST /polls/{pollId}/comments", + update : "PUT /comments", + destroy : "DELETE /comments/{commentId}" +},{}); + +var COMMENTS = [ + [{ + text: "This is the end of the world", + author: "Maya", + postDate: "21/12/12" + },{ + text: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.", + author: "Sacha", + postDate: "03/12/11" + }], + [{ + text: "azztyuiopfghj", + author: "yyy", + postDate: "" + },{ + text: "aqwxszedv", + author: "sdsqdqsdqsdqdqsd", + postDate: "" + }] +]; + +can.fixture('GET /poll/{pollId}/comments', function(orig) { + return COMMENTS[orig.data.pollId - 1]; +}); + +var idToInc= 3; +can.fixture("POST /polls/{pollId}/comments", function() { + console.log("create comment " + idToInc); + return {id: (idToInc++)} +}); + +can.fixture("PUT /comments", function(orig) { + console.log("update comment " + orig.data.id); + return {}; +}); + +can.fixture("DELETE /comments/{commentId}", function(orig){ + console.log("delete comment " + orig.data.commentId); + return {}; +}); \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/models/polls.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/models/polls.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/models/polls.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,70 @@ +var Poll = can.Model({ + findAll: 'GET /polls', + findOne: "GET /polls/{id}", + create : "POST /polls", + update : "PUT /polls/{id}", + destroy : "DELETE /polls/{id}" +},{}); + +var POLLS = [ + { + id: 1, + title: 'Titre 1', + description: "Desc 1", + creatorName: "Kevin", + choices: [ + { + id: 1, + name: "Choix 1", + description: "desc du choix 1" + }, + { + id: 2, + name: "Choix 2", + description: "desc du choix 2" + } + ] + }, + { + id: 2, + title: 'Lorem Ipsum', + description: "description très longue, mais vraiment très longue pour qu'elle passe en dessous. Enfin j'espère que ce sera assez long", + creatorName: "moi", + choices: [ + { + id: 3, + name: "Choix 1", + description: "desc du choix 1" + }, + { + id: 4, + name: "Choix 2", + description: "desc du choix 2" + } + ] + }, +]; + +can.fixture('GET /polls', function(){ + return POLLS; +}); + +can.fixture('GET /polls/{id}', function(orig) { + return POLLS[orig.data.id - 1]; +}); + +var idToInc= 3; +can.fixture("POST /polls", function() { + console.log("create poll " + idToInc); + return {id: (idToInc++)} +}); + +can.fixture("PUT /polls/{id}", function(orig) { + console.log("update poll " + orig.data.id); + return {}; +}); + +can.fixture("DELETE /polls/{id}", function(orig){ + console.log("delete poll " + orig.data.id); + return {}; +}); Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/models/votes.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/models/votes.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/models/votes.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,138 @@ +var Vote = can.Model({ + findAll: 'GET /poll/{pollId}/votes', +// findOne: 'GET /comments/{commentId}', + create : "POST /poll/{pollId}/votes", + update : "PUT /votes", + destroy : "DELETE /votes/{voteId}" +},{}); + +var VOTES = [ + [{ + id: 1, + voterListMember: { + name: "Joe", + email: "joe@dalt.on" + }, + voteToChoices: [{ + voteValue: 0, + choice: 1 + },{ + voteValue: 0, + choice: 2 + }] + + }, { + id: 2, + voterListMember: { + name: "Jack", + email: "jack@dalt.on" + }, + voteToChoices: [{ + voteValue: 1, + choice: 1 + },{ + voteValue: 1, + choice: 2 + }] + },{ + id: 3, + voterListMember: { + name: "William", + email: "william@dalt.on" + }, + voteToChoices: [{ + voteValue: 1, + choice: 1 + },{ + voteValue: 0, + choice: 2 + }] + }, { + id: 4, + voterListMember: { + name: "Avrel", + email: "avrel@dalt.on" + }, + voteToChoices: [{ + voteValue: 0, + choice: 1 + },{ + voteValue: 1, + choice: 2 + }] + }], + [{ + id: 5, + voterListMember: { + name: "Joe", + email: "joe@dalt.on" + }, + voteToChoices: [{ + voteValue: 0, + choice: 3 + },{ + voteValue: 0, + choice: 4 + }] + + }, { + id: 6, + voterListMember: { + name: "Jack", + email: "jack@dalt.on" + }, + voteToChoices: [{ + voteValue: 1, + choice: 3 + },{ + voteValue: 1, + choice: 4 + }] + },{ + id: 7, + voterListMember: { + name: "William", + email: "william@dalt.on" + }, + voteToChoices: [{ + voteValue: 1, + choice: 3 + },{ + voteValue: 0, + choice: 4 + }] + }, { + id: 8, + voterListMember: { + name: "Avrel", + email: "avrel@dalt.on" + }, + voteToChoices: [{ + voteValue: 0, + choice: 3 + },{ + voteValue: 1, + choice: 4 + }] + }] +]; + +can.fixture('GET /poll/{pollId}/votes', function(orig) { + return VOTES[orig.data.pollId - 1]; +}); + +var idToInc= 3; +can.fixture("POST /polls/{pollId}/votes", function() { + console.log("create vote " + idToInc); + return {id: (idToInc++)} +}); + +can.fixture("PUT /votes", function(orig) { + console.log("update vote " + orig.data.id); + return {}; +}); + +can.fixture("DELETE /votes/{voteId}", function(orig){ + console.log("delete vote " + orig.data.vote); + return {}; +}); \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/route.js =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/route.js (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/js/route.js 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,14 @@ +var Routing = can.Control({ + 'route': function() { + // Matches every routing change, but gets passed no data. + console.log("route") + }, + + ':type route': function(data) { + console.log(":type route " + data) + }, + + ':type/:id route': function(data) { + console.log(":type/:id route" + data) + } +}) \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/menu.ejs =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/menu.ejs (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/menu.ejs 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,4 @@ +<ul> + <li><%== can.route.link('Sondage', { type: 'poll', action: 'edit' }) %></li> + <li><%== can.route.link('Autre', { type: 'other', action: 'donothing' }) %></li> +</ul> \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/poll_form.ejs =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/poll_form.ejs (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/poll_form.ejs 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,30 @@ +<h1>Création de sondage</h1> + +<form id='pollCreationForm' class="form-horizontal"> + + <div class="control-group"> + <label class="control-label" for="pollCreationFormTitle">Title</label> + <div class="controls"> + <input type='text' name='title' id='pollCreationFormTitle' placeholder='title' + <%= poll.attr("title") ? "value='" + poll.attr("title") + "'" : "class='empty'" %>/> + </div> + </div> + + <div class="control-group"> + <label class="control-label" for='pollCreationFormDescription'>Description</label> + <div class="controls"> + <textarea id='pollCreationFormDescription' name='description' + <% if (!poll.attr("description")) {%> + class='empty' + <% } %>><%= poll.attr('description') %></textarea> + </div> + </div> + + <div class="form-actions"> + <a href="javascript://" class="btn btn-primary save">Save</a> + <a href="javascript://" class="btn btn-primary create">New poll</a> + <a href="javascript://" data-id="1" class="btn btn">Edit poll 1</a> + <a href="javascript://" data-id="2" class="btn btn">Edit poll 2</a> + </div> + +</form> \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/summary.ejs =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/summary.ejs (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/summary.ejs 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,35 @@ +<h1>Sondage <%= poll.title %></h1> + +<div class="control-group"> + <label class="control-label" for='pollSummaryLink'>Lien pour participer</label> + <input type='url' id='pollSummaryLink' value="<%== window.location.href.split('#')[0] + can.route.url({ type: 'poll', action: 'vote', id: poll.id }) %>"/> +</div> + +<div class='row-fluid'> + <div class='span4'> + <h2>Sondage</h2> + <ul class='noListStyle'> + <li><%== can.route.link('<i class="icon-ok"></i>Clore', {}) %></li> + <li><%== can.route.link('<i class="icon-pencil"></i>Modifer', {type: 'poll', action: 'edit', id: poll.id}) %></li> + <li><%== can.route.link('<i class="icon-envelope"></i>Notification', {}) %></li> + <li><%== can.route.link('<i class="icon-time"></i>Historique', {}) %></li> + <li><%== can.route.link('<i class="icon-clone"></i>Créer un duplicata', {}) %></li> + <li><%== can.route.link('<i class="icon-trash"></i>Effacer', {}) %></li> + </ul> + </div> + + <div class='span4'> + <h2>Participants</h2> + <ul class='noListStyle'> + <li><%== can.route.link('<i class="icon-user"></i>Inviter des participants', {}) %></li> + <li><%== can.route.link('<i class="icon-envelope"></i>Contacter les invités', {}) %></li> + </ul> + </div> + + <div class='span4'> + <h2>Export</h2> + <ul class='noListStyle'> + <li><i class="icon-upload"></i>clore</li> + </ul> + </div> +</div> \ No newline at end of file Added: branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/vote.ejs =================================================================== --- branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/vote.ejs (rev 0) +++ branches/pollen-2.0/pollen-ui-js/src/main/webapp/views/vote.ejs 2013-06-08 12:12:35 UTC (rev 3819) @@ -0,0 +1,53 @@ +<div class="page-header"> + <h1><%= poll.title %></h1> + <p>Sondage lancé par <%= poll.creatorName %> + | <i class='icon-user'></i> 0 + | <i class='icon-comment'></i> 0 + | <i class='icon-time'></i> il y a 2 minutes</p> + <p><%= poll.description %></p> +</div> + +<form> + + <table class="table table-bordered table-striped"> + <caption>Votes</caption> + <thead> + <tr> + <th>Votant</th> + <% poll.choices.each(function(choice) { %> + <th><%= choice.attr('name') %></th> + <% }); %> + </tr> + </thead> + <tbody> + <% votes.each(function(vote) { %> + <tr> + <td><%= vote.attr('voterListMember.name') %></td> + <% vote.voteToChoices.each(function(choice) { %> + <td><%= choice.attr('voteValue') %></td> + <% }); %> + </tr> + <% }); %> + <tr> + <td><input type='text'/></td> + <% poll.choices.each(function(choice) { %> + <td><input type='checkbox'/></td> + <% }); %> + </tr> + </tbody> + </table> + + <div class="form-actions"> + <button class="btn btn-primary">Voter</button> + </div> + +</form> + +<div> +<h2>Commentaires</h2> +<% comments.each(function(comment) { %> + +<p><strong><%= comment.attr('author') %></strong> : <%= comment.attr('text') %></p> + +<% }); %> +</div> \ No newline at end of file
participants (1)
-
kmorin@users.chorem.org