rmails 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/Gemfile +24 -0
- data/Gemfile.lock +167 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +9 -0
- data/Rakefile +31 -0
- data/Rakefile1 +9 -0
- data/Vagrantfile +51 -0
- data/app/assets/images/arrow_bottom.gif +0 -0
- data/app/assets/images/arrow_right.gif +0 -0
- data/app/assets/images/at-sign.png +0 -0
- data/app/assets/images/checkerboard.gif +0 -0
- data/app/assets/images/glyphicons-halflings-white.png +0 -0
- data/app/assets/images/glyphicons-halflings.png +0 -0
- data/app/assets/images/icon_draft.gif +0 -0
- data/app/assets/images/icon_layout.gif +0 -0
- data/app/assets/images/icon_move.gif +0 -0
- data/app/assets/images/icon_regular.gif +0 -0
- data/app/assets/images/icon_snippet.gif +0 -0
- data/app/assets/images/nav_arrow.png +0 -0
- data/app/assets/images/x.png +0 -0
- data/app/assets/javascripts/application.coffee.erb +27 -0
- data/app/assets/javascripts/canjs/can.construct.proxy.js +60 -0
- data/app/assets/javascripts/canjs/can.construct.super.js +44 -0
- data/app/assets/javascripts/canjs/can.control.plugin.js +245 -0
- data/app/assets/javascripts/canjs/can.control.view.js +88 -0
- data/app/assets/javascripts/canjs/can.dojo.js +3669 -0
- data/app/assets/javascripts/canjs/can.dojo.min.js +66 -0
- data/app/assets/javascripts/canjs/can.fixture.js +1020 -0
- data/app/assets/javascripts/canjs/can.jquery.js +2995 -0
- data/app/assets/javascripts/canjs/can.jquery.min.js +52 -0
- data/app/assets/javascripts/canjs/can.mootools.js +3462 -0
- data/app/assets/javascripts/canjs/can.mootools.min.js +63 -0
- data/app/assets/javascripts/canjs/can.observe.attributes.js +293 -0
- data/app/assets/javascripts/canjs/can.observe.backup.js +368 -0
- data/app/assets/javascripts/canjs/can.observe.delegate.js +359 -0
- data/app/assets/javascripts/canjs/can.observe.setter.js +58 -0
- data/app/assets/javascripts/canjs/can.observe.validations.js +374 -0
- data/app/assets/javascripts/canjs/can.view.modifiers.js +292 -0
- data/app/assets/javascripts/canjs/can.yui.js +3530 -0
- data/app/assets/javascripts/canjs/can.yui.min.js +65 -0
- data/app/assets/javascripts/canjs/can.zepto.js +3426 -0
- data/app/assets/javascripts/canjs/can.zepto.min.js +62 -0
- data/app/assets/javascripts/controls/admins.coffee.erb +105 -0
- data/app/assets/javascripts/controls/aliases.coffee.erb +91 -0
- data/app/assets/javascripts/controls/domains.coffee.erb +115 -0
- data/app/assets/javascripts/controls/settings.coffee.erb +47 -0
- data/app/assets/javascripts/controls/users.coffee.erb +94 -0
- data/app/assets/javascripts/lib/facebox.js +311 -0
- data/app/assets/javascripts/lib/html5.js +2 -0
- data/app/assets/javascripts/lib/jquery.js +9301 -0
- data/app/assets/javascripts/lib/jquery_formparams.js +108 -0
- data/app/assets/javascripts/lib/jquery_input_hint.js +20 -0
- data/app/assets/javascripts/lib/jquery_paginate.js +120 -0
- data/app/assets/javascripts/lib/jquery_ui_custom.js +6 -0
- data/app/assets/javascripts/lib/json2.js +487 -0
- data/app/assets/javascripts/lib/utils.coffee.erb +48 -0
- data/app/assets/javascripts/models/admin.coffee.erb +42 -0
- data/app/assets/javascripts/models/alias.coffee.erb +28 -0
- data/app/assets/javascripts/models/domain.coffee.erb +39 -0
- data/app/assets/javascripts/models/property.coffee.erb +18 -0
- data/app/assets/javascripts/models/user.coffee.erb +29 -0
- data/app/assets/stylesheets/application.sass +156 -0
- data/app/assets/stylesheets/base.css.sass +243 -0
- data/app/assets/stylesheets/facebox.css +80 -0
- data/app/assets/stylesheets/lib/bootstrap.css +9 -0
- data/app/assets/stylesheets/twitter/bootstrap-responsive.scss +1 -0
- data/app/assets/stylesheets/twitter/bootstrap.scss +63 -0
- data/app/assets/stylesheets/twitter/bootstrap/_accordion.scss +34 -0
- data/app/assets/stylesheets/twitter/bootstrap/_alerts.scss +79 -0
- data/app/assets/stylesheets/twitter/bootstrap/_breadcrumbs.scss +24 -0
- data/app/assets/stylesheets/twitter/bootstrap/_button-groups.scss +229 -0
- data/app/assets/stylesheets/twitter/bootstrap/_buttons.scss +228 -0
- data/app/assets/stylesheets/twitter/bootstrap/_carousel.scss +158 -0
- data/app/assets/stylesheets/twitter/bootstrap/_close.scss +32 -0
- data/app/assets/stylesheets/twitter/bootstrap/_code.scss +61 -0
- data/app/assets/stylesheets/twitter/bootstrap/_component-animations.scss +22 -0
- data/app/assets/stylesheets/twitter/bootstrap/_dropdowns.scss +237 -0
- data/app/assets/stylesheets/twitter/bootstrap/_forms.scss +689 -0
- data/app/assets/stylesheets/twitter/bootstrap/_grid.scss +21 -0
- data/app/assets/stylesheets/twitter/bootstrap/_hero-unit.scss +25 -0
- data/app/assets/stylesheets/twitter/bootstrap/_labels-badges.scss +83 -0
- data/app/assets/stylesheets/twitter/bootstrap/_layouts.scss +16 -0
- data/app/assets/stylesheets/twitter/bootstrap/_media.scss +55 -0
- data/app/assets/stylesheets/twitter/bootstrap/_mixins.scss +690 -0
- data/app/assets/stylesheets/twitter/bootstrap/_modals.scss +95 -0
- data/app/assets/stylesheets/twitter/bootstrap/_navbar.scss +497 -0
- data/app/assets/stylesheets/twitter/bootstrap/_navs.scss +409 -0
- data/app/assets/stylesheets/twitter/bootstrap/_pager.scss +43 -0
- data/app/assets/stylesheets/twitter/bootstrap/_pagination.scss +123 -0
- data/app/assets/stylesheets/twitter/bootstrap/_popovers.scss +133 -0
- data/app/assets/stylesheets/twitter/bootstrap/_progress-bars.scss +122 -0
- data/app/assets/stylesheets/twitter/bootstrap/_reset.scss +216 -0
- data/app/assets/stylesheets/twitter/bootstrap/_responsive-1200px-min.scss +28 -0
- data/app/assets/stylesheets/twitter/bootstrap/_responsive-767px-max.scss +193 -0
- data/app/assets/stylesheets/twitter/bootstrap/_responsive-768px-979px.scss +19 -0
- data/app/assets/stylesheets/twitter/bootstrap/_responsive-navbar.scss +189 -0
- data/app/assets/stylesheets/twitter/bootstrap/_responsive-utilities.scss +74 -0
- data/app/assets/stylesheets/twitter/bootstrap/_scaffolding.scss +53 -0
- data/app/assets/stylesheets/twitter/bootstrap/_sprites.scss +197 -0
- data/app/assets/stylesheets/twitter/bootstrap/_tables.scss +235 -0
- data/app/assets/stylesheets/twitter/bootstrap/_thumbnails.scss +53 -0
- data/app/assets/stylesheets/twitter/bootstrap/_tooltip.scss +70 -0
- data/app/assets/stylesheets/twitter/bootstrap/_type.scss +247 -0
- data/app/assets/stylesheets/twitter/bootstrap/_utilities.scss +45 -0
- data/app/assets/stylesheets/twitter/bootstrap/_variables.scss +301 -0
- data/app/assets/stylesheets/twitter/bootstrap/_wells.scss +29 -0
- data/app/assets/stylesheets/twitter/bootstrap/responsive.scss +48 -0
- data/app/controllers/admin_users_controller.rb +62 -0
- data/app/controllers/aliases_controller.rb +35 -0
- data/app/controllers/application_controller.rb +20 -0
- data/app/controllers/domains_controller.rb +48 -0
- data/app/controllers/server_controller.rb +28 -0
- data/app/controllers/users_controller.rb +35 -0
- data/app/helpers/admin_users_helper.rb +11 -0
- data/app/helpers/application_helper.rb +2 -0
- data/app/helpers/domains_helper.rb +25 -0
- data/app/helpers/server_helper.rb +30 -0
- data/app/mailers/.gitkeep +0 -0
- data/app/models/.gitkeep +0 -0
- data/app/models/admin_user.rb +54 -0
- data/app/models/certificate_manager.rb +46 -0
- data/app/models/property.rb +103 -0
- data/app/models/property/awstats.rb +15 -0
- data/app/models/property/dovecot.rb +61 -0
- data/app/models/property/dspam.rb +45 -0
- data/app/models/property/nginx.rb +47 -0
- data/app/models/property/postfix.rb +64 -0
- data/app/models/property_value_validator.rb +41 -0
- data/app/models/system_manager.rb +88 -0
- data/app/models/virtual_alias.rb +13 -0
- data/app/models/virtual_domain.rb +15 -0
- data/app/models/virtual_user.rb +51 -0
- data/app/views/admin_users/_form.html.haml +21 -0
- data/app/views/admin_users/first.html.haml +12 -0
- data/app/views/admin_users/index.html.haml +61 -0
- data/app/views/aliases/_form.html.haml +11 -0
- data/app/views/aliases/_list.html.haml +0 -0
- data/app/views/devise/confirmations/new.html.haml +9 -0
- data/app/views/devise/mailer/confirmation_instructions.html.haml +4 -0
- data/app/views/devise/mailer/reset_password_instructions.html.haml +6 -0
- data/app/views/devise/mailer/unlock_instructions.html.haml +5 -0
- data/app/views/devise/passwords/edit.html.haml +14 -0
- data/app/views/devise/passwords/new.html.haml +12 -0
- data/app/views/devise/registrations/edit.html.haml +18 -0
- data/app/views/devise/registrations/new.html.haml +10 -0
- data/app/views/devise/sessions/new.html.haml +16 -0
- data/app/views/devise/shared/_links.haml +17 -0
- data/app/views/devise/unlocks/new.html.haml +11 -0
- data/app/views/domains/_fields.html.haml +0 -0
- data/app/views/domains/_form.html.haml +7 -0
- data/app/views/domains/index.html.haml +133 -0
- data/app/views/domains/show.html.haml +1 -0
- data/app/views/layouts/_nav.html.haml +3 -0
- data/app/views/layouts/application.html.haml +29 -0
- data/app/views/server/_certificates.html.haml +10 -0
- data/app/views/server/_dovecot.html.haml +27 -0
- data/app/views/server/_dspam.html.haml +9 -0
- data/app/views/server/_postfix_info.html.haml +5 -0
- data/app/views/server/_postfix_test.html.haml +5 -0
- data/app/views/server/_status.html.haml +11 -0
- data/app/views/server/index.html.haml +36 -0
- data/app/views/users/_form.html.haml +11 -0
- data/app/views/users/_list.html.haml +0 -0
- data/bin/rmails +43 -0
- data/config.ru +4 -0
- data/config/application.rb +62 -0
- data/config/boot.rb +6 -0
- data/config/environment.rb +5 -0
- data/config/environments/development.rb +37 -0
- data/config/environments/production.rb +67 -0
- data/config/environments/test.rb +37 -0
- data/config/initializers/backtrace_silencers.rb +7 -0
- data/config/initializers/devise.rb +240 -0
- data/config/initializers/devise_encryptable.rb +37 -0
- data/config/initializers/devise_models.rb +86 -0
- data/config/initializers/inflections.rb +15 -0
- data/config/initializers/mime_types.rb +5 -0
- data/config/initializers/secret_token.rb +7 -0
- data/config/initializers/session_store.rb +8 -0
- data/config/initializers/simple_form.rb +142 -0
- data/config/initializers/simple_form_bootstrap.rb +45 -0
- data/config/initializers/wrap_parameters.rb +14 -0
- data/config/locales/cz.yml +45 -0
- data/config/locales/devise.cz.yml +47 -0
- data/config/locales/devise.en.yml +60 -0
- data/config/locales/en.yml +5 -0
- data/config/locales/server/cz.yml +14 -0
- data/config/locales/server/en.yml +7 -0
- data/config/locales/server/help/dovecot/cz.yml +21 -0
- data/config/locales/server/help/dovecot/en.yml +18 -0
- data/config/locales/server/help/dspam/cz.yml +10 -0
- data/config/locales/server/help/dspam/en.yml +4 -0
- data/config/locales/server/key/dovecot/cz.yml +23 -0
- data/config/locales/server/key/dovecot/en.yml +20 -0
- data/config/locales/server/key/dspam/cz.yml +10 -0
- data/config/locales/server/key/dspam/en.yml +4 -0
- data/config/locales/simple_form.en.yml +26 -0
- data/config/locales/views/admin_users/cz.yml +25 -0
- data/config/locales/views/admin_users/en.yml +5 -0
- data/config/locales/views/aliases/cz.yml +17 -0
- data/config/locales/views/aliases/en.yml +1 -0
- data/config/locales/views/devise/cz.yml +26 -0
- data/config/locales/views/devise/en.yml +13 -0
- data/config/locales/views/domains/cz.yml +15 -0
- data/config/locales/views/domains/en.yml +15 -0
- data/config/locales/views/server/cz.yml +19 -0
- data/config/locales/views/server/en.yml +3 -0
- data/config/locales/views/users/cz.yml +18 -0
- data/config/locales/views/users/en.yml +1 -0
- data/config/routes.rb +80 -0
- data/db/migrate/20121112201233_virtual_domain.rb +11 -0
- data/db/migrate/20121112201247_virtual_alias.rb +10 -0
- data/db/migrate/20121112201341_virtual_user.rb +34 -0
- data/db/migrate/20121112201359_create_admin_user.rb +30 -0
- data/db/migrate/20121112201411_server_configuration.rb +13 -0
- data/db/migrate/20130304083938_join_domains_users.rb +12 -0
- data/db/migrate/20130311214040_create_versions.rb +18 -0
- data/db/seeds.rb +45 -0
- data/lib/devise-encryptable.rb +1 -0
- data/lib/rmails.rb +5 -0
- data/lib/rmails/installer.rb +37 -0
- data/lib/rmails/version.rb +3 -0
- data/lib/tasks/.gitkeep +0 -0
- data/lib/templates/haml/scaffold/_form.html.haml +10 -0
- data/locals +0 -0
- data/log/.gitkeep +0 -0
- data/public/404.html +26 -0
- data/public/422.html +26 -0
- data/public/500.html +25 -0
- data/public/favicon.ico +0 -0
- data/public/fonts/London.eot +0 -0
- data/public/fonts/London.otf +0 -0
- data/public/fonts/London.woff +0 -0
- data/public/fonts/LondonBold.otf +0 -0
- data/public/robots.txt +5 -0
- data/rmails.gemspec +35 -0
- data/script/prepflog.pl +576 -0
- data/script/rails +6 -0
- data/system/config/automateit_env.rb +16 -0
- data/system/config/fields.yml +48 -0
- data/system/config/tags.yml +39 -0
- data/system/dist/README_AutomateIt_dist.txt +20 -0
- data/system/dist/amavis/spamassassin.cf.erb +85 -0
- data/system/dist/amavis/user.erb +33 -0
- data/system/dist/awstats/awstats.mail.conf.erb +41 -0
- data/system/dist/awstats/prepflog.pl +576 -0
- data/system/dist/dovecot/dovecot-sql.conf.ext.erb +14 -0
- data/system/dist/dovecot/dovecot.conf.erb +297 -0
- data/system/dist/dovecot/old_dovecot.conf.erb +63 -0
- data/system/dist/dspam/conf.erb +96 -0
- data/system/dist/dspam/pgsql.conf +33 -0
- data/system/dist/nginx/awstats.erb +17 -0
- data/system/dist/nginx/rmails.erb +40 -0
- data/system/dist/postfix/email2email.cf.erb +5 -0
- data/system/dist/postfix/main.cf.erb +118 -0
- data/system/dist/postfix/master.cf.erb +74 -0
- data/system/dist/postfix/sasl.conf.erb +3 -0
- data/system/dist/postfix/sender_login_maps.cf.erb +5 -0
- data/system/dist/postfix/virtual_alias_maps.cf.erb +5 -0
- data/system/dist/postfix/virtual_mailbox_domains.cf.erb +5 -0
- data/system/dist/postfix/virtual_mailbox_maps.cf.erb +5 -0
- data/system/dist/postgresql/postgresql.conf.erb +54 -0
- data/system/dist/rmails/Gemfile.1 +8 -0
- data/system/dist/rmails/Gemfile.2 +24 -0
- data/system/dist/rmails/database.yml.erb +15 -0
- data/system/dist/sudoers +37 -0
- data/system/lib/README_AutomateIt_lib.txt +22 -0
- data/system/lib/dkim_key.rb +9 -0
- data/system/lib/smtpd_key.rb +39 -0
- data/system/lib/ssl.rb +23 -0
- data/system/recipes/01_prepare_server.rb +84 -0
- data/system/recipes/02_setup_database.rb +65 -0
- data/system/recipes/03_setup_postfix.rb +124 -0
- data/system/recipes/04_setup_dovecot.rb +78 -0
- data/system/recipes/05_setup_nginx.rb +29 -0
- data/system/recipes/06_setup_dspam.rb +24 -0
- data/system/recipes/07_setup_amavis.rb +16 -0
- data/system/recipes/08_setup_spamassassin.rb +22 -0
- data/system/recipes/09_setup_awstats.rb +49 -0
- data/system/recipes/XX_start_services.rb +5 -0
- data/test/fixtures/.gitkeep +0 -0
- data/test/functional/.gitkeep +0 -0
- data/test/functional/aliases_controller_test.rb +7 -0
- data/test/functional/domains_controller_test.rb +7 -0
- data/test/functional/users_controller_test.rb +7 -0
- data/test/integration/.gitkeep +0 -0
- data/test/performance/browsing_test.rb +12 -0
- data/test/test_helper.rb +13 -0
- data/test/unit/.gitkeep +0 -0
- data/test/unit/helpers/aliases_helper_test.rb +4 -0
- data/test/unit/helpers/domains_helper_test.rb +4 -0
- data/test/unit/helpers/users_helper_test.rb +4 -0
- data/vendor/assets/javascripts/.gitkeep +0 -0
- data/vendor/assets/javascripts/canjs/can.construct.proxy.js +60 -0
- data/vendor/assets/javascripts/canjs/can.construct.super.js +44 -0
- data/vendor/assets/javascripts/canjs/can.control.plugin.js +245 -0
- data/vendor/assets/javascripts/canjs/can.control.view.js +88 -0
- data/vendor/assets/javascripts/canjs/can.dojo.js +3669 -0
- data/vendor/assets/javascripts/canjs/can.dojo.min.js +66 -0
- data/vendor/assets/javascripts/canjs/can.fixture.js +1020 -0
- data/vendor/assets/javascripts/canjs/can.jquery.js +2995 -0
- data/vendor/assets/javascripts/canjs/can.jquery.min.js +52 -0
- data/vendor/assets/javascripts/canjs/can.mootools.js +3462 -0
- data/vendor/assets/javascripts/canjs/can.mootools.min.js +63 -0
- data/vendor/assets/javascripts/canjs/can.observe.attributes.js +293 -0
- data/vendor/assets/javascripts/canjs/can.observe.backup.js +368 -0
- data/vendor/assets/javascripts/canjs/can.observe.delegate.js +359 -0
- data/vendor/assets/javascripts/canjs/can.observe.setter.js +58 -0
- data/vendor/assets/javascripts/canjs/can.observe.validations.js +374 -0
- data/vendor/assets/javascripts/canjs/can.view.modifiers.js +292 -0
- data/vendor/assets/javascripts/canjs/can.yui.js +3530 -0
- data/vendor/assets/javascripts/canjs/can.yui.min.js +65 -0
- data/vendor/assets/javascripts/canjs/can.zepto.js +3426 -0
- data/vendor/assets/javascripts/canjs/can.zepto.min.js +62 -0
- data/vendor/assets/javascripts/twitter/bootstrap-affix.js +104 -0
- data/vendor/assets/javascripts/twitter/bootstrap-alert.js +90 -0
- data/vendor/assets/javascripts/twitter/bootstrap-button.js +96 -0
- data/vendor/assets/javascripts/twitter/bootstrap-carousel.js +176 -0
- data/vendor/assets/javascripts/twitter/bootstrap-collapse.js +158 -0
- data/vendor/assets/javascripts/twitter/bootstrap-dropdown.js +150 -0
- data/vendor/assets/javascripts/twitter/bootstrap-modal.js +239 -0
- data/vendor/assets/javascripts/twitter/bootstrap-popover.js +103 -0
- data/vendor/assets/javascripts/twitter/bootstrap-scrollspy.js +151 -0
- data/vendor/assets/javascripts/twitter/bootstrap-tab.js +135 -0
- data/vendor/assets/javascripts/twitter/bootstrap-tooltip.js +275 -0
- data/vendor/assets/javascripts/twitter/bootstrap-transition.js +60 -0
- data/vendor/assets/javascripts/twitter/bootstrap-typeahead.js +300 -0
- data/vendor/assets/javascripts/twitter/my/bootstrap-typeahead.js +311 -0
- data/vendor/assets/stylesheets/.gitkeep +0 -0
- metadata +470 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
(function(can, window, undefined){
|
|
2
|
+
|
|
3
|
+
// tests if we can get super in .toString()
|
|
4
|
+
var isFunction = can.isFunction,
|
|
5
|
+
|
|
6
|
+
fnTest = /xyz/.test(function() {
|
|
7
|
+
xyz;
|
|
8
|
+
}) ? /\b_super\b/ : /.*/;
|
|
9
|
+
|
|
10
|
+
// overwrites a single property so it can still call super
|
|
11
|
+
can.Construct._overwrite = function(addTo, base, name, val){
|
|
12
|
+
// Check if we're overwriting an existing function
|
|
13
|
+
addTo[name] = isFunction(val) &&
|
|
14
|
+
isFunction(base[name]) &&
|
|
15
|
+
fnTest.test(val) ? (function( name, fn ) {
|
|
16
|
+
return function() {
|
|
17
|
+
var tmp = this._super,
|
|
18
|
+
ret;
|
|
19
|
+
|
|
20
|
+
// Add a new ._super() method that is the same method
|
|
21
|
+
// but on the super-class
|
|
22
|
+
this._super = base[name];
|
|
23
|
+
|
|
24
|
+
// The method only need to be bound temporarily, so we
|
|
25
|
+
// remove it when we're done executing
|
|
26
|
+
ret = fn.apply(this, arguments);
|
|
27
|
+
this._super = tmp;
|
|
28
|
+
return ret;
|
|
29
|
+
};
|
|
30
|
+
})(name, val) : val;
|
|
31
|
+
}
|
|
32
|
+
// overwrites an object with methods, sets up _super
|
|
33
|
+
// newProps - new properties
|
|
34
|
+
// oldProps - where the old properties might be
|
|
35
|
+
// addTo - what we are adding to
|
|
36
|
+
can.Construct._inherit = function( newProps, oldProps, addTo ) {
|
|
37
|
+
addTo = addTo || newProps
|
|
38
|
+
for ( var name in newProps ) {
|
|
39
|
+
can.Construct._overwrite(addTo, oldProps, name, newProps[name]);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
})(this.can, this )
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
(function(can, window, undefined){
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
//used to determine if a control instance is one of controllers
|
|
6
|
+
//controllers can be strings or classes
|
|
7
|
+
var i,
|
|
8
|
+
isAControllerOf = function( instance, controllers ) {
|
|
9
|
+
for ( i = 0; i < controllers.length; i++ ) {
|
|
10
|
+
if ( typeof controllers[i] == 'string' ? instance.constructor._shortName == controllers[i] : instance instanceof controllers[i] ) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
},
|
|
16
|
+
data = function(el, data){
|
|
17
|
+
return $el.data('controls');
|
|
18
|
+
},
|
|
19
|
+
makeArray = can.makeArray,
|
|
20
|
+
old = can.Control.setup;
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
* static
|
|
24
|
+
*/
|
|
25
|
+
can.Control.setup = function() {
|
|
26
|
+
// if you didn't provide a name, or are control, don't do anything
|
|
27
|
+
if ( this !== can.Control ) {
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @attribute can.Control.plugin.static.pluginName
|
|
31
|
+
* @parent can.Control.plugin
|
|
32
|
+
*
|
|
33
|
+
* Setting the static `pluginName` property allows you to override the default name
|
|
34
|
+
* with your own.
|
|
35
|
+
*
|
|
36
|
+
* var Filler = can.Control({
|
|
37
|
+
* pluginName: 'fillWith'
|
|
38
|
+
* },{});
|
|
39
|
+
*
|
|
40
|
+
* $("#foo").fillWith();
|
|
41
|
+
*
|
|
42
|
+
* If you don't provide a `pluginName`, the control falls back to the
|
|
43
|
+
* [can.Construct.fullName fullName] attribute:
|
|
44
|
+
*
|
|
45
|
+
* can.Control('Ui.Layout.FillWith', {}, {});
|
|
46
|
+
* $("#foo").ui_layout_fill_with();
|
|
47
|
+
*
|
|
48
|
+
*/
|
|
49
|
+
var pluginName = this.pluginName || this._fullName;
|
|
50
|
+
|
|
51
|
+
// create jQuery plugin
|
|
52
|
+
if(pluginName !== 'can_control'){
|
|
53
|
+
this.plugin(pluginName);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
old.apply(this, arguments);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
* prototype
|
|
62
|
+
*/
|
|
63
|
+
$.fn.extend({
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @function jQuery.fn.controls
|
|
67
|
+
* @parent can.Control.plugin
|
|
68
|
+
*
|
|
69
|
+
* When the widget is initialized, the plugin control creates an array
|
|
70
|
+
* of control instance(s) with the DOM element it was initialized on using
|
|
71
|
+
* [can.data] method.
|
|
72
|
+
*
|
|
73
|
+
* The `controls` method allows you to get the control instance(s) for any element.
|
|
74
|
+
*
|
|
75
|
+
* //- Inits the widgets
|
|
76
|
+
* $('.widgets:eq(0)').my_box();
|
|
77
|
+
* $('.widgets:eq(1)').my_clock();
|
|
78
|
+
*
|
|
79
|
+
* <div class="widgets my_box" />
|
|
80
|
+
* <div class="widgets my_clock" />
|
|
81
|
+
*
|
|
82
|
+
* $('.widgets').controls() //-> [ MyBox, MyClock ]
|
|
83
|
+
*
|
|
84
|
+
* Additionally, you can invoke it passing the name of a control
|
|
85
|
+
* to fetch a specific instance(s).
|
|
86
|
+
*
|
|
87
|
+
* //- Inits the widgets
|
|
88
|
+
* $('.widgets:eq(0)').my_box();
|
|
89
|
+
* $('.widgets:eq(1)').my_clock();
|
|
90
|
+
*
|
|
91
|
+
* <div class="widgets my_box" />
|
|
92
|
+
* <div class="widgets my_clock" />
|
|
93
|
+
*
|
|
94
|
+
* $('.widgets').controls('MyBox') //-> [ MyBox ]
|
|
95
|
+
*
|
|
96
|
+
* @param {Object} control (optional) if exists the control instance(s) with that constructor function or type will be returned.
|
|
97
|
+
* @return {Array} an array of control instance(s).
|
|
98
|
+
*/
|
|
99
|
+
controls: function() {
|
|
100
|
+
var controllerNames = makeArray(arguments),
|
|
101
|
+
instances = [],
|
|
102
|
+
controls, c, cname;
|
|
103
|
+
//check if arguments
|
|
104
|
+
this.each(function() {
|
|
105
|
+
|
|
106
|
+
controls = can.$(this).data("controls");
|
|
107
|
+
if(!controls){
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
for(var i=0; i<controls.length; i++){
|
|
111
|
+
c = controls[i];
|
|
112
|
+
if (!controllerNames.length || isAControllerOf(c, controllerNames) ) {
|
|
113
|
+
instances.push(c);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
return instances;
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @function jQuery.fn.control
|
|
122
|
+
* @parent can.Control.plugin
|
|
123
|
+
*
|
|
124
|
+
* The `control` does the same as [jQuery.fn.controls controls] execept it only
|
|
125
|
+
* returns the first instance found.
|
|
126
|
+
*
|
|
127
|
+
* //- Init MyBox widget
|
|
128
|
+
* $('.widgets').my_box();
|
|
129
|
+
*
|
|
130
|
+
* <div class="widgets my_box" />
|
|
131
|
+
*
|
|
132
|
+
* $('.widgets').controls() //-> MyBox
|
|
133
|
+
*
|
|
134
|
+
* @param {Object} control (optional) if exists the first control instance with that constructor function or type will be returned.
|
|
135
|
+
* @return {can.Control} the first control.
|
|
136
|
+
*/
|
|
137
|
+
control: function( control ) {
|
|
138
|
+
return this.controls.apply(this, arguments)[0];
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
can.Control.plugin = function(pluginname){
|
|
143
|
+
var control = this;
|
|
144
|
+
|
|
145
|
+
if (!$.fn[pluginname]) {
|
|
146
|
+
$.fn[pluginname] = function(options){
|
|
147
|
+
|
|
148
|
+
var args = makeArray(arguments), //if the arg is a method on this control
|
|
149
|
+
isMethod = typeof options == "string" && $.isFunction(control.prototype[options]), meth = args[0];
|
|
150
|
+
return this.each(function(){
|
|
151
|
+
//check if created
|
|
152
|
+
var plugin = can.$(this).control(control);
|
|
153
|
+
|
|
154
|
+
if (plugin) {
|
|
155
|
+
if (isMethod) {
|
|
156
|
+
// call a method on the control with the remaining args
|
|
157
|
+
plugin[meth].apply(plugin, args.slice(1));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// call the plugin's update method
|
|
161
|
+
plugin.update.apply(plugin, args);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
//create a new control instance
|
|
166
|
+
control.newInstance.apply(control, [this].concat(args));
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @function can.Control.prototype.update
|
|
175
|
+
* @parent can.Control.plugin
|
|
176
|
+
*
|
|
177
|
+
* Update extends [can.Control.prototype.options options]
|
|
178
|
+
* with the `options` argument and rebinds all events. It
|
|
179
|
+
* re-configures the control.
|
|
180
|
+
*
|
|
181
|
+
* For example, the following control wraps a recipe form. When the form
|
|
182
|
+
* is submitted, it creates the recipe on the server. When the recipe
|
|
183
|
+
* is `created`, it resets the form with a new instance.
|
|
184
|
+
*
|
|
185
|
+
* var Creator = can.Control({
|
|
186
|
+
* "{recipe} created" : function(){
|
|
187
|
+
* this.update({recipe : new Recipe()});
|
|
188
|
+
* this.element[0].reset();
|
|
189
|
+
* this.element.find("[type=submit]").val("Create Recipe")
|
|
190
|
+
* },
|
|
191
|
+
* "submit" : function(el, ev){
|
|
192
|
+
* ev.preventDefault();
|
|
193
|
+
* var recipe = this.options.recipe;
|
|
194
|
+
* recipe.attrs( this.element.formParams() );
|
|
195
|
+
* this.element.find("[type=submit]").val("Saving...")
|
|
196
|
+
* recipe.save();
|
|
197
|
+
* }
|
|
198
|
+
* });
|
|
199
|
+
*
|
|
200
|
+
* $('#createRecipes').creator({ recipe : new Recipe() })
|
|
201
|
+
*
|
|
202
|
+
* *Update* is called if a control's plugin helper is called with the plugin options on an element
|
|
203
|
+
* that already has a control instance of the same type. If you want to implement your
|
|
204
|
+
* own update method make sure to call the old one either using the [can.Construct.super super] plugin or
|
|
205
|
+
* by calling `can.Control.prototype.update.apply(this, arguments);`.
|
|
206
|
+
* For example, you can change the content of the control element every time the options change:
|
|
207
|
+
*
|
|
208
|
+
* var Plugin = can.Control({
|
|
209
|
+
* pluginName: 'myPlugin'
|
|
210
|
+
* }, {
|
|
211
|
+
* init : function(el, options) {
|
|
212
|
+
* this.updateCount = 0;
|
|
213
|
+
* this.update({
|
|
214
|
+
* text : 'Initialized'
|
|
215
|
+
* });
|
|
216
|
+
* },
|
|
217
|
+
* update : function(options) {
|
|
218
|
+
* // Call the can.Control update first.
|
|
219
|
+
* // Use this._super when using can/construct/super
|
|
220
|
+
* can.Control.prototype.update.call(this, options);
|
|
221
|
+
* this.element.html(this.options.text + ' ' +
|
|
222
|
+
* (++this.updateCount) + ' times');
|
|
223
|
+
* }
|
|
224
|
+
* });
|
|
225
|
+
*
|
|
226
|
+
* $('#control').myPlugin();
|
|
227
|
+
* $('#control').html();
|
|
228
|
+
* // Initialized. Updated 1 times
|
|
229
|
+
*
|
|
230
|
+
* $('#control').myPlugin({ text : 'Calling update. Updated' });
|
|
231
|
+
* $('#control').html();
|
|
232
|
+
* // Calling update. Updated 2 times
|
|
233
|
+
*
|
|
234
|
+
* @demo can/control/plugin/demo-update.html
|
|
235
|
+
*
|
|
236
|
+
* @param {Object} options A list of options to merge with
|
|
237
|
+
* [can.Control.prototype.options this.options]. Often this method
|
|
238
|
+
* is called by the [can.Control.plugin jQuery helper function].
|
|
239
|
+
*/
|
|
240
|
+
can.Control.prototype.update = function( options ) {
|
|
241
|
+
can.extend(this.options, options);
|
|
242
|
+
this.on();
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
})(this.can, this )
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
(function(can, window, undefined){
|
|
2
|
+
var URI = steal.URI || steal.File;
|
|
3
|
+
|
|
4
|
+
can.Control.getFolder = function() {
|
|
5
|
+
return can.underscore(this.fullName.replace(/\./g, "/")).replace("/Controllers", "");
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
can.Control._calculatePosition = function( Class, view ) {
|
|
9
|
+
var classParts = Class.fullName.split('.'),
|
|
10
|
+
classPartsWithoutPrefix = classParts.slice(0);
|
|
11
|
+
classPartsWithoutPrefix.splice(0, 2),
|
|
12
|
+
action_name = "init"; // Remove prefix (usually 2 elements)
|
|
13
|
+
|
|
14
|
+
var hasControllers = (classParts.length > 2) && classParts[1] == 'Controllers',
|
|
15
|
+
path = hasControllers? can.underscore(classParts[0]): can.underscore(classParts.join("/")),
|
|
16
|
+
controller_name = can.underscore(classPartsWithoutPrefix.join('/')).toLowerCase(),
|
|
17
|
+
suffix = (typeof view == "string" && /\.[\w\d]+$/.test(view)) ? "" : can.view.ext;
|
|
18
|
+
|
|
19
|
+
//calculate view
|
|
20
|
+
if ( typeof view == "string" ) {
|
|
21
|
+
if ( view.substr(0, 2) == "//" ) { //leave where it is
|
|
22
|
+
} else {
|
|
23
|
+
view = "//" + URI(path).join( 'views/' + (view.indexOf('/') !== -1 ? view : (hasControllers ? controller_name + '/' : "") + view)) + suffix;
|
|
24
|
+
}
|
|
25
|
+
} else if (!view ) {
|
|
26
|
+
view = "//" + URI(path).join('views/' + (hasControllers ? controller_name + '/' : "") + action_name.replace(/\.|#/g, '').replace(/ /g, '_'))+ suffix;
|
|
27
|
+
}
|
|
28
|
+
return view;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
var calculateHelpers = function( myhelpers ) {
|
|
32
|
+
var helpers = {};
|
|
33
|
+
if ( myhelpers ) {
|
|
34
|
+
if ( can.isArray(myhelpers) ) {
|
|
35
|
+
for ( var h = 0; h < myhelpers.length; h++ ) {
|
|
36
|
+
can.extend(helpers, myhelpers[h]);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
can.extend(helpers, myhelpers);
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
if ( this._default_helpers ) {
|
|
44
|
+
helpers = this._default_helpers;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//load from name
|
|
48
|
+
var current = window;
|
|
49
|
+
var parts = this.constructor.fullName.split(/\./);
|
|
50
|
+
for ( var i = 0; i < parts.length; i++ ) {
|
|
51
|
+
if(current){
|
|
52
|
+
if ( typeof current.Helpers == 'object' ) {
|
|
53
|
+
can.extend(helpers, current.Helpers);
|
|
54
|
+
}
|
|
55
|
+
current = current[parts[i]];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (current && typeof current.Helpers == 'object' ) {
|
|
60
|
+
can.extend(helpers, current.Helpers);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this._default_helpers = helpers;
|
|
64
|
+
}
|
|
65
|
+
return helpers;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
can.Control.prototype.view = function( view, data, myhelpers ) {
|
|
69
|
+
//shift args if no view is provided
|
|
70
|
+
if ( typeof view != "string" && !myhelpers ) {
|
|
71
|
+
myhelpers = data;
|
|
72
|
+
data = view;
|
|
73
|
+
view = null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
//guess from controller name
|
|
77
|
+
view = can.Control._calculatePosition(this.constructor, view, this.called);
|
|
78
|
+
|
|
79
|
+
//calculate data
|
|
80
|
+
data = data || this;
|
|
81
|
+
|
|
82
|
+
//calculate helpers
|
|
83
|
+
var helpers = calculateHelpers.call(this, myhelpers);
|
|
84
|
+
|
|
85
|
+
return can.view(view, data, helpers); //what about controllers in other folders?
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
})(this.can, this )
|
|
@@ -0,0 +1,3669 @@
|
|
|
1
|
+
(function(can, window, undefined){
|
|
2
|
+
define("can/dojo", ["dojo/query", "dojo/NodeList-dom", "dojo/NodeList-traverse"], function(){
|
|
3
|
+
|
|
4
|
+
// event.js
|
|
5
|
+
// ---------
|
|
6
|
+
// _Basic event wrapper._
|
|
7
|
+
can.addEvent = function(event, fn){
|
|
8
|
+
if(!this.__bindEvents){
|
|
9
|
+
this.__bindEvents = {};
|
|
10
|
+
}
|
|
11
|
+
var eventName = event.split(".")[0];
|
|
12
|
+
|
|
13
|
+
if(!this.__bindEvents[eventName]){
|
|
14
|
+
this.__bindEvents[eventName] = [];
|
|
15
|
+
}
|
|
16
|
+
this.__bindEvents[eventName].push({
|
|
17
|
+
handler: fn,
|
|
18
|
+
name: event
|
|
19
|
+
});
|
|
20
|
+
return this;
|
|
21
|
+
};
|
|
22
|
+
can.removeEvent = function(event, fn){
|
|
23
|
+
if(!this.__bindEvents){
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
var i =0,
|
|
27
|
+
events = this.__bindEvents[event.split(".")[0]],
|
|
28
|
+
ev;
|
|
29
|
+
while(i < events.length){
|
|
30
|
+
ev = events[i]
|
|
31
|
+
if((fn && ev.handler === fn) || (!fn && ev.name === event)){
|
|
32
|
+
events.splice(i, 1);
|
|
33
|
+
} else {
|
|
34
|
+
i++;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return this;
|
|
38
|
+
};
|
|
39
|
+
can.dispatch = function(event){
|
|
40
|
+
if(!this.__bindEvents){
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
var eventName = event.type.split(".")[0],
|
|
45
|
+
handlers = (this.__bindEvents[eventName] || []).slice(0),
|
|
46
|
+
self= this,
|
|
47
|
+
args = [event].concat(event.data || []);
|
|
48
|
+
|
|
49
|
+
can.each(handlers, function(ev){
|
|
50
|
+
event.data = args.slice(1);
|
|
51
|
+
ev.handler.apply(self, args);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
;
|
|
56
|
+
|
|
57
|
+
define("plugd/trigger",["dojo"], function(dojo){
|
|
58
|
+
|
|
59
|
+
var d = dojo, isfn = d.isFunction,
|
|
60
|
+
leaveRe = /mouse(enter|leave)/,
|
|
61
|
+
_fix = function(_, p){
|
|
62
|
+
return "mouse" + (p == "enter" ? "over" : "out");
|
|
63
|
+
},
|
|
64
|
+
mix = d._mixin,
|
|
65
|
+
|
|
66
|
+
// the guts of the node triggering logic:
|
|
67
|
+
// the function accepts node (not string|node), "on"-less event name,
|
|
68
|
+
// and an object of args to mix into the event.
|
|
69
|
+
realTrigger = d.doc.createEvent ?
|
|
70
|
+
function(n, e, a){
|
|
71
|
+
// the sane branch
|
|
72
|
+
var ev = d.doc.createEvent("HTMLEvents");
|
|
73
|
+
e = e.replace(leaveRe, _fix);
|
|
74
|
+
// destroyed events should not bubble
|
|
75
|
+
ev.initEvent(e, e === "destroyed" ? false : true, true);
|
|
76
|
+
a && mix(ev, a);
|
|
77
|
+
n.dispatchEvent(ev);
|
|
78
|
+
} :
|
|
79
|
+
function(n, e, a){
|
|
80
|
+
// the janktastic branch
|
|
81
|
+
var ev = "on" + e, stop = false, lc = e.toLowerCase(), node = n;
|
|
82
|
+
try{
|
|
83
|
+
// FIXME: is this worth it? for mixed-case native event support:? Opera ends up in the
|
|
84
|
+
// createEvent path above, and also fails on _some_ native-named events.
|
|
85
|
+
// if(lc !== e && d.indexOf(d.NodeList.events, lc) >= 0){
|
|
86
|
+
// // if the event is one of those listed in our NodeList list
|
|
87
|
+
// // in lowercase form but is mixed case, throw to avoid
|
|
88
|
+
// // fireEvent. /me sighs. http://gist.github.com/315318
|
|
89
|
+
// throw("janktastic");
|
|
90
|
+
// }
|
|
91
|
+
n.fireEvent(ev);
|
|
92
|
+
}catch(er){
|
|
93
|
+
// a lame duck to work with. we're probably a 'custom event'
|
|
94
|
+
var evdata = mix({
|
|
95
|
+
type: e, target: n, faux: true,
|
|
96
|
+
// HACK: [needs] added support for customStopper to _base/event.js
|
|
97
|
+
// some tests will fail until del._stopPropagation has support.
|
|
98
|
+
_stopper: function(){ stop = this.cancelBubble; }
|
|
99
|
+
}, a);
|
|
100
|
+
|
|
101
|
+
isfn(n[ev]) && n[ev](evdata);
|
|
102
|
+
|
|
103
|
+
// handle bubbling of custom events, unless the event was stopped.
|
|
104
|
+
while(!stop && n !== d.doc && n.parentNode){
|
|
105
|
+
n = n.parentNode;
|
|
106
|
+
isfn(n[ev]) && n[ev](evdata);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
;
|
|
111
|
+
|
|
112
|
+
d._trigger = function(node, event, extraArgs){
|
|
113
|
+
// summary:
|
|
114
|
+
// Helper for `dojo.trigger`, which handles the DOM cases. We should never
|
|
115
|
+
// be here without a domNode reference and a string eventname.
|
|
116
|
+
var n = d.byId(node), ev = event && event.slice(0, 2) == "on" ? event.slice(2) : event;
|
|
117
|
+
realTrigger(n, ev, extraArgs);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
d.trigger = function(obj, event, extraArgs){
|
|
121
|
+
// summary:
|
|
122
|
+
// Trigger some event. It can be either a Dom Event, Custom Event,
|
|
123
|
+
// or direct function call.
|
|
124
|
+
//
|
|
125
|
+
// description:
|
|
126
|
+
// Trigger some event. It can be either a Dom Event, Custom Event,
|
|
127
|
+
// or direct function call. NOTE: This function does not trigger
|
|
128
|
+
// default behavior, only triggers bound event listeneres. eg:
|
|
129
|
+
// one cannot trigger("anchorNode", "onclick") and expect the browser
|
|
130
|
+
// to follow the href="" attribute naturally.
|
|
131
|
+
//
|
|
132
|
+
// obj: String|DomNode|Object|Function
|
|
133
|
+
// An ID, or DomNode reference, from which to trigger the event.
|
|
134
|
+
// If an Object, fire the `event` in the scope of this object,
|
|
135
|
+
// similar to calling dojo.hitch(obj, event)(). The return value
|
|
136
|
+
// in this case is returned from `dojo.trigger`
|
|
137
|
+
//
|
|
138
|
+
// event: String|Function
|
|
139
|
+
// The name of the event to trigger. can be any DOM level 2 event
|
|
140
|
+
// and can be in either form: "onclick" or "click" for instance.
|
|
141
|
+
// In the object-firing case, this method can be a function or
|
|
142
|
+
// a string version of a member function, just like `dojo.hitch`.
|
|
143
|
+
//
|
|
144
|
+
// extraArgs: Object?
|
|
145
|
+
// An object to mix into the `event` object passed to any bound
|
|
146
|
+
// listeners. Be careful not to override important members, like
|
|
147
|
+
// `type`, or `preventDefault`. It will likely error.
|
|
148
|
+
//
|
|
149
|
+
// Additionally, extraArgs is moot in the object-triggering case,
|
|
150
|
+
// as all arguments beyond the `event` are curried onto the triggered
|
|
151
|
+
// function.
|
|
152
|
+
//
|
|
153
|
+
// example:
|
|
154
|
+
// | dojo.connect(node, "onclick", function(e){ // | // later:
|
|
155
|
+
// | dojo.trigger(node, "onclick");
|
|
156
|
+
//
|
|
157
|
+
// example:
|
|
158
|
+
// | // or from within dojo.query: (requires dojo.NodeList)
|
|
159
|
+
// | dojo.query("a").onclick(function(){}).trigger("onclick");
|
|
160
|
+
//
|
|
161
|
+
// example:
|
|
162
|
+
// | // fire obj.method() in scope of obj
|
|
163
|
+
// | dojo.trigger(obj, "method");
|
|
164
|
+
//
|
|
165
|
+
// example:
|
|
166
|
+
// | // fire an anonymous function:
|
|
167
|
+
// | dojo.trigger(d.global, function(){ //
|
|
168
|
+
// example:
|
|
169
|
+
// | // fire and anonymous function in the scope of obj
|
|
170
|
+
// | dojo.trigger(obj, function(){ this == obj; });
|
|
171
|
+
//
|
|
172
|
+
// example:
|
|
173
|
+
// | // with a connected function like:
|
|
174
|
+
// | dojo.connect(dojo.doc, "onclick", function(e){
|
|
175
|
+
// | if(e && e.manuallydone){
|
|
176
|
+
// | console.log("this was a triggered onclick, not natural");
|
|
177
|
+
// | }
|
|
178
|
+
// | });
|
|
179
|
+
// | // fire onclick, passing in a custom bit of info
|
|
180
|
+
// | dojo.trigger("someId", "onclick", { manuallydone:true });
|
|
181
|
+
//
|
|
182
|
+
// returns: Anything
|
|
183
|
+
// Will not return anything in the Dom event case, but will return whatever
|
|
184
|
+
// return value is received from the triggered event.
|
|
185
|
+
return (isfn(obj) || isfn(event) || isfn(obj[event])) ?
|
|
186
|
+
d.hitch.apply(d, arguments)() : d._trigger.apply(d, arguments);
|
|
187
|
+
};
|
|
188
|
+
d.NodeList.prototype.trigger = d.NodeList._adaptAsForEach(d._trigger);
|
|
189
|
+
|
|
190
|
+
// if the node.js module is available, extend trigger into that.
|
|
191
|
+
if(d._Node && !d._Node.prototype.trigger){
|
|
192
|
+
d.extend(d._Node, {
|
|
193
|
+
trigger: function(ev, data){
|
|
194
|
+
// summary:
|
|
195
|
+
// Fire some some event originating from this node.
|
|
196
|
+
// Only available if both the `dojo.trigger` and `dojo.node` plugin
|
|
197
|
+
// are enabled. Allows chaining as all `dojo._Node` methods do.
|
|
198
|
+
//
|
|
199
|
+
// ev: String
|
|
200
|
+
// Some string event name to fire. eg: "onclick", "submit"
|
|
201
|
+
//
|
|
202
|
+
// data: Object
|
|
203
|
+
// Just like `extraArgs` for `dojo.trigger`, additional data
|
|
204
|
+
// to mix into the event object.
|
|
205
|
+
//
|
|
206
|
+
// example:
|
|
207
|
+
// | // fire onlick orginiating from a node with id="someAnchorId"
|
|
208
|
+
// | dojo.node("someAnchorId").trigger("click");
|
|
209
|
+
|
|
210
|
+
d._trigger(this, ev, data);
|
|
211
|
+
return this; // dojo._Node
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return d.trigger;
|
|
217
|
+
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
can.each = function (elements, callback, context) {
|
|
221
|
+
var i = 0,
|
|
222
|
+
key;
|
|
223
|
+
if (elements) {
|
|
224
|
+
if (typeof elements.length == 'number' && elements.pop) {
|
|
225
|
+
elements.attr && elements.attr('length');
|
|
226
|
+
for (var len = elements.length; i < len; i++) {
|
|
227
|
+
if (callback.call(context || elements[i], elements[i], i, elements) === false) {
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
for (key in elements) {
|
|
233
|
+
if (callback.call(context || elements[i], elements[key], key, elements) === false) {
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return elements;
|
|
240
|
+
}
|
|
241
|
+
;
|
|
242
|
+
|
|
243
|
+
// dojo.js
|
|
244
|
+
// ---------
|
|
245
|
+
// _dojo node list._
|
|
246
|
+
//
|
|
247
|
+
// These are pre-loaded by `steal` -> no callback.
|
|
248
|
+
require(["dojo", "dojo/query", "plugd/trigger", "dojo/NodeList-dom"]);
|
|
249
|
+
|
|
250
|
+
// Map string helpers.
|
|
251
|
+
can.trim = function(s){
|
|
252
|
+
return s && dojo.trim(s);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Map array helpers.
|
|
256
|
+
can.makeArray = function(arr){
|
|
257
|
+
array = [];
|
|
258
|
+
dojo.forEach(arr, function(item){ array.push(item)});
|
|
259
|
+
return array;
|
|
260
|
+
};
|
|
261
|
+
can.isArray = dojo.isArray;
|
|
262
|
+
can.inArray = function(item,arr){
|
|
263
|
+
return dojo.indexOf(arr, item);
|
|
264
|
+
};
|
|
265
|
+
can.map = function(arr, fn){
|
|
266
|
+
return dojo.map(can.makeArray(arr||[]), fn);
|
|
267
|
+
};
|
|
268
|
+
// Map object helpers.
|
|
269
|
+
can.extend = function(first){
|
|
270
|
+
if(first === true){
|
|
271
|
+
var args = can.makeArray(arguments);
|
|
272
|
+
args.shift();
|
|
273
|
+
return dojo.mixin.apply(dojo, args)
|
|
274
|
+
}
|
|
275
|
+
return dojo.mixin.apply(dojo, arguments)
|
|
276
|
+
}
|
|
277
|
+
can.isEmptyObject = function(object){
|
|
278
|
+
var prop;
|
|
279
|
+
for(prop in object){
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
return prop === undefined;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Use a version of param similar to jQuery's param that
|
|
286
|
+
// handles nested data instead of dojo.objectToQuery which doesn't
|
|
287
|
+
can.param = function(object){
|
|
288
|
+
var pairs = [],
|
|
289
|
+
add = function( key, value ){
|
|
290
|
+
pairs.push(encodeURIComponent(key) + "=" + encodeURIComponent(value))
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
for(var name in object){
|
|
294
|
+
can.buildParam(name, object[name], add);
|
|
295
|
+
}
|
|
296
|
+
return pairs.join("&").replace(/%20/g, "+");
|
|
297
|
+
}
|
|
298
|
+
can.buildParam = function(prefix, obj, add){
|
|
299
|
+
if(can.isArray(obj)){
|
|
300
|
+
for(var i = 0, l = obj.length; i < l; ++i){
|
|
301
|
+
add(prefix + "[]", obj[i])
|
|
302
|
+
}
|
|
303
|
+
} else if( dojo.isObject(obj) ){
|
|
304
|
+
for (var name in obj){
|
|
305
|
+
can.buildParam(prefix + "[" + name + "]", obj[name], add);
|
|
306
|
+
}
|
|
307
|
+
} else {
|
|
308
|
+
add(prefix, obj);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Map function helpers.
|
|
313
|
+
can.proxy = function(func, context){
|
|
314
|
+
return dojo.hitch(context, func)
|
|
315
|
+
}
|
|
316
|
+
can.isFunction = function(f){
|
|
317
|
+
return dojo.isFunction(f);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// The id of the `function` to be bound, used as an expando on the `function`
|
|
321
|
+
// so we can lookup it's `remove` object.
|
|
322
|
+
var id = 0,
|
|
323
|
+
// Takes a node list, goes through each node
|
|
324
|
+
// and adds events data that has a map of events to
|
|
325
|
+
// callbackId to `remove` object. It looks like
|
|
326
|
+
// `{click: {5: {remove: fn}}}`.
|
|
327
|
+
addBinding = function( nodelist, ev, cb ) {
|
|
328
|
+
nodelist.forEach(function(node){
|
|
329
|
+
var node = new dojo.NodeList(node)
|
|
330
|
+
var events = can.data(node,"events");
|
|
331
|
+
if(!events){
|
|
332
|
+
can.data(node,"events", events = {})
|
|
333
|
+
}
|
|
334
|
+
if(!events[ev]){
|
|
335
|
+
events[ev] = {};
|
|
336
|
+
}
|
|
337
|
+
if(cb.__bindingsIds === undefined) {
|
|
338
|
+
cb.__bindingsIds=id++;
|
|
339
|
+
}
|
|
340
|
+
events[ev][cb.__bindingsIds] = node.on(ev, cb)[0]
|
|
341
|
+
});
|
|
342
|
+
},
|
|
343
|
+
// Removes a binding on a `nodelist` by finding
|
|
344
|
+
// the remove object within the object's data.
|
|
345
|
+
removeBinding = function(nodelist,ev,cb){
|
|
346
|
+
nodelist.forEach(function(node){
|
|
347
|
+
var node = new dojo.NodeList(node),
|
|
348
|
+
events = can.data(node,"events"),
|
|
349
|
+
handlers = events[ev],
|
|
350
|
+
handler = handlers[cb.__bindingsIds];
|
|
351
|
+
|
|
352
|
+
dojo.disconnect(handler);
|
|
353
|
+
delete handlers[cb.__bindingsIds];
|
|
354
|
+
|
|
355
|
+
if(can.isEmptyObject(handlers)){
|
|
356
|
+
delete events[ev]
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
can.bind = function( ev, cb){
|
|
362
|
+
// If we can bind to it...
|
|
363
|
+
if(this.bind && this.bind !== can.bind){
|
|
364
|
+
this.bind(ev, cb)
|
|
365
|
+
|
|
366
|
+
// Otherwise it's an element or `nodeList`.
|
|
367
|
+
} else if(this.on || this.nodeType){
|
|
368
|
+
addBinding( new dojo.NodeList(this), ev, cb)
|
|
369
|
+
} else if(this.addEvent) {
|
|
370
|
+
this.addEvent(ev, cb)
|
|
371
|
+
} else {
|
|
372
|
+
// Make it bind-able...
|
|
373
|
+
can.addEvent.call(this, ev, cb)
|
|
374
|
+
}
|
|
375
|
+
return this;
|
|
376
|
+
}
|
|
377
|
+
can.unbind = function(ev, cb){
|
|
378
|
+
// If we can bind to it...
|
|
379
|
+
if(this.unbind && this.unbind !== can.unbind){
|
|
380
|
+
this.unbind(ev, cb)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
else if(this.on || this.nodeType) {
|
|
384
|
+
removeBinding(new dojo.NodeList(this), ev, cb);
|
|
385
|
+
} else {
|
|
386
|
+
// Make it bind-able...
|
|
387
|
+
can.removeEvent.call(this, ev, cb)
|
|
388
|
+
}
|
|
389
|
+
return this;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
can.trigger = function(item, event, args, bubble){
|
|
393
|
+
if(item.trigger){
|
|
394
|
+
if(bubble === false){
|
|
395
|
+
if(!item[0] || item[0].nodeType === 3){
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
// Force stop propagation by
|
|
399
|
+
// listening to `on` and then immediately disconnecting.
|
|
400
|
+
var connect = item.on(event, function(ev){
|
|
401
|
+
|
|
402
|
+
ev.stopPropagation && ev.stopPropagation();
|
|
403
|
+
ev.cancelBubble = true;
|
|
404
|
+
ev._stopper && ev._stopper();
|
|
405
|
+
|
|
406
|
+
dojo.disconnect(connect);
|
|
407
|
+
})
|
|
408
|
+
item.trigger(event,args)
|
|
409
|
+
} else {
|
|
410
|
+
item.trigger(event,args)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
} else {
|
|
414
|
+
if(typeof event === 'string'){
|
|
415
|
+
event = {type: event}
|
|
416
|
+
}
|
|
417
|
+
event.data = args
|
|
418
|
+
event.target = event.target || item;
|
|
419
|
+
can.dispatch.call(item, event)
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
can.delegate = function(selector, ev , cb){
|
|
424
|
+
if(this.on || this.nodeType){
|
|
425
|
+
addBinding( new dojo.NodeList(this), selector+":"+ev, cb)
|
|
426
|
+
} else if(this.delegate) {
|
|
427
|
+
this.delegate(selector, ev , cb)
|
|
428
|
+
}
|
|
429
|
+
return this;
|
|
430
|
+
}
|
|
431
|
+
can.undelegate = function(selector, ev , cb){
|
|
432
|
+
if(this.on || this.nodeType){
|
|
433
|
+
removeBinding(new dojo.NodeList(this), selector+":"+ev, cb);
|
|
434
|
+
} else if(this.undelegate) {
|
|
435
|
+
this.undelegate(selector, ev , cb)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return this;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
var optionsMap = {
|
|
442
|
+
type:"method",
|
|
443
|
+
success : undefined,
|
|
444
|
+
error: undefined
|
|
445
|
+
}
|
|
446
|
+
var updateDeferred = function(xhr, d){
|
|
447
|
+
for(var prop in xhr){
|
|
448
|
+
if(typeof d[prop] == 'function'){
|
|
449
|
+
d[prop] = function(){
|
|
450
|
+
xhr[prop].apply(xhr, arguments)
|
|
451
|
+
}
|
|
452
|
+
} else {
|
|
453
|
+
d[prop] = prop[xhr]
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
can.ajax = function(options){
|
|
460
|
+
var type = can.capitalize( (options.type || "get").toLowerCase() ),
|
|
461
|
+
method = dojo["xhr"+type];
|
|
462
|
+
var success = options.success,
|
|
463
|
+
error = options.error,
|
|
464
|
+
d = new can.Deferred();
|
|
465
|
+
|
|
466
|
+
var def = method({
|
|
467
|
+
url : options.url,
|
|
468
|
+
handleAs : options.dataType,
|
|
469
|
+
sync : !options.async,
|
|
470
|
+
headers : options.headers,
|
|
471
|
+
content: options.data
|
|
472
|
+
})
|
|
473
|
+
def.then(function(data, ioargs){
|
|
474
|
+
updateDeferred(xhr, d);
|
|
475
|
+
d.resolve(data,"success",xhr);
|
|
476
|
+
success && success(data,"success",xhr);
|
|
477
|
+
},function(data,ioargs){
|
|
478
|
+
updateDeferred(xhr, d);
|
|
479
|
+
d.reject(xhr,"error");
|
|
480
|
+
error(xhr,"error");
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
var xhr = def.ioArgs.xhr;
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
updateDeferred(xhr, d);
|
|
487
|
+
return d;
|
|
488
|
+
|
|
489
|
+
}
|
|
490
|
+
// Element - get the wrapped helper.
|
|
491
|
+
can.$ = function(selector){
|
|
492
|
+
if(selector === window){
|
|
493
|
+
return window;
|
|
494
|
+
}
|
|
495
|
+
if(typeof selector === "string"){
|
|
496
|
+
return dojo.query(selector)
|
|
497
|
+
} else {
|
|
498
|
+
return new dojo.NodeList(selector);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
}
|
|
503
|
+
can.buildFragment = function(frag, node){
|
|
504
|
+
var owner = node && node.ownerDocument,
|
|
505
|
+
frag = dojo.toDom(frag, owner );
|
|
506
|
+
if(frag.nodeType !== 11){
|
|
507
|
+
var tmp = document.createDocumentFragment();
|
|
508
|
+
tmp.appendChild(frag)
|
|
509
|
+
frag = tmp;
|
|
510
|
+
}
|
|
511
|
+
return frag
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
can.append = function(wrapped, html){
|
|
515
|
+
return wrapped.forEach(function(node){
|
|
516
|
+
dojo.place( html, node)
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
var data = {},
|
|
521
|
+
uuid = can.uuid = +new Date(),
|
|
522
|
+
exp = can.expando = 'can' + uuid;
|
|
523
|
+
|
|
524
|
+
function getData(node, name) {
|
|
525
|
+
var id = node[exp], store = id && data[id];
|
|
526
|
+
return name === undefined ? store || setData(node) :
|
|
527
|
+
(store && store[name]);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function setData(node, name, value) {
|
|
531
|
+
var id = node[exp] || (node[exp] = ++uuid),
|
|
532
|
+
store = data[id] || (data[id] = {});
|
|
533
|
+
if (name !== undefined) store[name] = value;
|
|
534
|
+
return store;
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
var cleanData = function(elems){
|
|
538
|
+
can.trigger(new dojo.NodeList(elems),"destroyed",[],false)
|
|
539
|
+
for ( var i = 0, elem;
|
|
540
|
+
(elem = elems[i]) !== undefined; i++ ) {
|
|
541
|
+
var id = elem[exp]
|
|
542
|
+
delete data[id];
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
can.data = function(wrapped, name, value){
|
|
547
|
+
return value === undefined ?
|
|
548
|
+
wrapped.length == 0 ? undefined : getData(wrapped[0], name) :
|
|
549
|
+
wrapped.forEach(function(node){
|
|
550
|
+
setData(node, name, value);
|
|
551
|
+
});
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
// Overwrite `dojo.destroy`, `dojo.empty` and `dojo.place`.
|
|
555
|
+
var empty = dojo.empty;
|
|
556
|
+
dojo.empty = function(){
|
|
557
|
+
for(var c; c = node.lastChild;){ // Intentional assignment.
|
|
558
|
+
dojo.destroy(c);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
var destroy = dojo.destroy;
|
|
563
|
+
dojo.destroy = function(node){
|
|
564
|
+
node = dojo.byId(node);
|
|
565
|
+
cleanData([node]);
|
|
566
|
+
node.getElementsByTagName && cleanData(node.getElementsByTagName('*'))
|
|
567
|
+
|
|
568
|
+
return destroy.apply(dojo, arguments);
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
can.addClass = function(wrapped, className){
|
|
572
|
+
return wrapped.addClass(className);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
can.remove = function(wrapped){
|
|
576
|
+
// We need to remove text nodes ourselves.
|
|
577
|
+
wrapped.forEach(dojo.destroy);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
can.get = function(wrapped, index){
|
|
581
|
+
return wrapped[index];
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Add pipe to `dojo.Deferred`.
|
|
585
|
+
can.extend(dojo.Deferred.prototype, {
|
|
586
|
+
pipe : function(done, fail){
|
|
587
|
+
var d = new dojo.Deferred();
|
|
588
|
+
this.addCallback(function(){
|
|
589
|
+
d.resolve( done.apply(this, arguments) );
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
this.addErrback(function(){
|
|
593
|
+
if(fail){
|
|
594
|
+
d.reject( fail.apply(this, arguments) );
|
|
595
|
+
} else {
|
|
596
|
+
d.reject.apply(d, arguments);
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
return d;
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
// deferred.js
|
|
604
|
+
// ---------
|
|
605
|
+
// _Lightweight, jQuery style deferreds._
|
|
606
|
+
|
|
607
|
+
var Deferred = function( func ) {
|
|
608
|
+
if ( ! ( this instanceof Deferred ))
|
|
609
|
+
return new Deferred();
|
|
610
|
+
|
|
611
|
+
this._doneFuncs = [];
|
|
612
|
+
this._failFuncs = [];
|
|
613
|
+
this._resultArgs = null;
|
|
614
|
+
this._status = "";
|
|
615
|
+
|
|
616
|
+
// Check for option `function` -- call it with this as context and as first
|
|
617
|
+
// parameter, as specified in jQuery API.
|
|
618
|
+
func && func.call(this, this);
|
|
619
|
+
};
|
|
620
|
+
can.Deferred = Deferred;
|
|
621
|
+
can.when = Deferred.when = function() {
|
|
622
|
+
var args = can.makeArray( arguments );
|
|
623
|
+
if (args.length < 2) {
|
|
624
|
+
var obj = args[0];
|
|
625
|
+
if (obj && ( can.isFunction( obj.isResolved ) && can.isFunction( obj.isRejected ))) {
|
|
626
|
+
return obj;
|
|
627
|
+
} else {
|
|
628
|
+
return Deferred().resolve(obj);
|
|
629
|
+
}
|
|
630
|
+
} else {
|
|
631
|
+
|
|
632
|
+
var df = Deferred(),
|
|
633
|
+
done = 0,
|
|
634
|
+
// Resolve params -- params of each resolve, we need to track them down
|
|
635
|
+
// to be able to pass them in the correct order if the master
|
|
636
|
+
// needs to be resolved.
|
|
637
|
+
rp = [];
|
|
638
|
+
|
|
639
|
+
can.each(args, function(arg, j){
|
|
640
|
+
arg.done(function() {
|
|
641
|
+
rp[j] = (arguments.length < 2) ? arguments[0] : arguments;
|
|
642
|
+
if (++done == args.length) {
|
|
643
|
+
df.resolve.apply(df, rp);
|
|
644
|
+
}
|
|
645
|
+
}).fail(function() {
|
|
646
|
+
df.reject(arguments);
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
return df;
|
|
651
|
+
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
var resolveFunc = function(type, _status){
|
|
656
|
+
return function(context){
|
|
657
|
+
var args = this._resultArgs = (arguments.length > 1) ? arguments[1] : [];
|
|
658
|
+
return this.exec(context, this[type], args, _status);
|
|
659
|
+
}
|
|
660
|
+
},
|
|
661
|
+
doneFunc = function(type, _status){
|
|
662
|
+
return function(){
|
|
663
|
+
var self = this;
|
|
664
|
+
// In Safari, the properties of the `arguments` object are not enumerable,
|
|
665
|
+
// so we have to convert arguments to an `Array` that allows `can.each` to loop over them.
|
|
666
|
+
can.each(Array.prototype.slice.call(arguments), function( v, i, args ) {
|
|
667
|
+
if ( ! v )
|
|
668
|
+
return;
|
|
669
|
+
if ( v.constructor === Array ) {
|
|
670
|
+
args.callee.apply(self, v)
|
|
671
|
+
} else {
|
|
672
|
+
// Immediately call the `function` if the deferred has been resolved.
|
|
673
|
+
if (self._status === _status)
|
|
674
|
+
v.apply(self, self._resultArgs || []);
|
|
675
|
+
|
|
676
|
+
self[type].push(v);
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
return this;
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
can.extend( Deferred.prototype, {
|
|
684
|
+
pipe : function(done, fail){
|
|
685
|
+
var d = can.Deferred();
|
|
686
|
+
this.done(function(){
|
|
687
|
+
d.resolve( done.apply(this, arguments) );
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
this.fail(function(){
|
|
691
|
+
if(fail){
|
|
692
|
+
d.reject( fail.apply(this, arguments) );
|
|
693
|
+
} else {
|
|
694
|
+
d.reject.apply(d, arguments);
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
return d;
|
|
698
|
+
},
|
|
699
|
+
resolveWith : resolveFunc("_doneFuncs","rs"),
|
|
700
|
+
rejectWith : resolveFunc("_failFuncs","rj"),
|
|
701
|
+
done : doneFunc("_doneFuncs","rs"),
|
|
702
|
+
fail : doneFunc("_failFuncs","rj"),
|
|
703
|
+
always : function() {
|
|
704
|
+
var args = can.makeArray(arguments);
|
|
705
|
+
if (args.length && args[0])
|
|
706
|
+
this.done(args[0]).fail(args[0]);
|
|
707
|
+
|
|
708
|
+
return this;
|
|
709
|
+
},
|
|
710
|
+
|
|
711
|
+
then : function() {
|
|
712
|
+
var args = can.makeArray( arguments );
|
|
713
|
+
// Fail `function`(s)
|
|
714
|
+
if (args.length > 1 && args[1])
|
|
715
|
+
this.fail(args[1]);
|
|
716
|
+
|
|
717
|
+
// Done `function`(s)
|
|
718
|
+
if (args.length && args[0])
|
|
719
|
+
this.done(args[0]);
|
|
720
|
+
|
|
721
|
+
return this;
|
|
722
|
+
},
|
|
723
|
+
|
|
724
|
+
isResolved : function() {
|
|
725
|
+
return this._status === "rs";
|
|
726
|
+
},
|
|
727
|
+
|
|
728
|
+
isRejected : function() {
|
|
729
|
+
return this._status === "rj";
|
|
730
|
+
},
|
|
731
|
+
|
|
732
|
+
reject : function() {
|
|
733
|
+
return this.rejectWith(this, arguments);
|
|
734
|
+
},
|
|
735
|
+
|
|
736
|
+
resolve : function() {
|
|
737
|
+
return this.resolveWith(this, arguments);
|
|
738
|
+
},
|
|
739
|
+
|
|
740
|
+
exec : function(context, dst, args, st) {
|
|
741
|
+
if (this._status !== "")
|
|
742
|
+
return this;
|
|
743
|
+
|
|
744
|
+
this._status = st;
|
|
745
|
+
|
|
746
|
+
can.each(dst, function(d){
|
|
747
|
+
d.apply(context, args);
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
return this;
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
// ##string.js
|
|
755
|
+
// _Miscellaneous string utility functions._
|
|
756
|
+
|
|
757
|
+
// Several of the methods in this plugin use code adapated from Prototype
|
|
758
|
+
// Prototype JavaScript framework, version 1.6.0.1.
|
|
759
|
+
// © 2005-2007 Sam Stephenson
|
|
760
|
+
var undHash = /_|-/,
|
|
761
|
+
colons = /==/,
|
|
762
|
+
words = /([A-Z]+)([A-Z][a-z])/g,
|
|
763
|
+
lowUp = /([a-z\d])([A-Z])/g,
|
|
764
|
+
dash = /([a-z\d])([A-Z])/g,
|
|
765
|
+
replacer = /\{([^\}]+)\}/g,
|
|
766
|
+
quote = /"/g,
|
|
767
|
+
singleQuote = /'/g,
|
|
768
|
+
|
|
769
|
+
// Returns the `prop` property from `obj`.
|
|
770
|
+
// If `add` is true and `prop` doesn't exist in `obj`, create it as an
|
|
771
|
+
// empty object.
|
|
772
|
+
getNext = function( obj, prop, add ) {
|
|
773
|
+
return prop in obj ?
|
|
774
|
+
obj[ prop ] :
|
|
775
|
+
( add && ( obj[ prop ] = {} ));
|
|
776
|
+
},
|
|
777
|
+
|
|
778
|
+
// Returns `true` if the object can have properties (no `null`s).
|
|
779
|
+
isContainer = function( current ) {
|
|
780
|
+
return /^f|^o/.test( typeof current );
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
can.extend(can, {
|
|
784
|
+
// Escapes strings for HTML.
|
|
785
|
+
esc : function( content ) {
|
|
786
|
+
return ( "" + content )
|
|
787
|
+
.replace(/&/g, '&')
|
|
788
|
+
.replace(/</g, '<')
|
|
789
|
+
.replace(/>/g, '>')
|
|
790
|
+
.replace(quote, '"')
|
|
791
|
+
.replace(singleQuote, "'");
|
|
792
|
+
},
|
|
793
|
+
|
|
794
|
+
getObject : function( name, roots, add ) {
|
|
795
|
+
|
|
796
|
+
// The parts of the name we are looking up
|
|
797
|
+
// `['App','Models','Recipe']`
|
|
798
|
+
var parts = name ? name.split('.') : [],
|
|
799
|
+
length = parts.length,
|
|
800
|
+
current,
|
|
801
|
+
r = 0,
|
|
802
|
+
ret, i;
|
|
803
|
+
|
|
804
|
+
// Make sure roots is an `array`.
|
|
805
|
+
roots = can.isArray(roots) ? roots : [roots || window];
|
|
806
|
+
|
|
807
|
+
if ( ! length ) {
|
|
808
|
+
return roots[0];
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// For each root, mark it as current.
|
|
812
|
+
while( current = roots[r++] ) {
|
|
813
|
+
|
|
814
|
+
// Walk current to the 2nd to last object or until there
|
|
815
|
+
// is not a container.
|
|
816
|
+
for (i =0; i < length - 1 && isContainer( current ); i++ ) {
|
|
817
|
+
current = getNext( current, parts[i], add );
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// If we can get a property from the 2nd to last object...
|
|
821
|
+
if( isContainer(current) ) {
|
|
822
|
+
|
|
823
|
+
// Get (and possibly set) the property.
|
|
824
|
+
ret = getNext(current, parts[i], add);
|
|
825
|
+
|
|
826
|
+
// If there is a value, we exit.
|
|
827
|
+
if ( ret !== undefined ) {
|
|
828
|
+
// If `add` is `false`, delete the property
|
|
829
|
+
if ( add === false ) {
|
|
830
|
+
delete current[parts[i]];
|
|
831
|
+
}
|
|
832
|
+
return ret;
|
|
833
|
+
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
},
|
|
838
|
+
// Capitalizes a string.
|
|
839
|
+
capitalize: function( s, cache ) {
|
|
840
|
+
// Used to make newId.
|
|
841
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
842
|
+
},
|
|
843
|
+
|
|
844
|
+
// Underscores a string.
|
|
845
|
+
underscore: function( s ) {
|
|
846
|
+
return s
|
|
847
|
+
.replace(colons, '/')
|
|
848
|
+
.replace(words, '$1_$2')
|
|
849
|
+
.replace(lowUp, '$1_$2')
|
|
850
|
+
.replace(dash, '_')
|
|
851
|
+
.toLowerCase();
|
|
852
|
+
},
|
|
853
|
+
// Micro-templating.
|
|
854
|
+
sub: function( str, data, remove ) {
|
|
855
|
+
|
|
856
|
+
var obs = [];
|
|
857
|
+
|
|
858
|
+
obs.push( str.replace( replacer, function( whole, inside ) {
|
|
859
|
+
|
|
860
|
+
// Convert inside to type.
|
|
861
|
+
var ob = can.getObject( inside, data, remove === undefined? remove : !remove );
|
|
862
|
+
|
|
863
|
+
// If a container, push into objs (which will return objects found).
|
|
864
|
+
if ( isContainer( ob ) ) {
|
|
865
|
+
obs.push( ob );
|
|
866
|
+
return "";
|
|
867
|
+
} else {
|
|
868
|
+
return "" + ob;
|
|
869
|
+
}
|
|
870
|
+
}));
|
|
871
|
+
|
|
872
|
+
return obs.length <= 1 ? obs[0] : obs;
|
|
873
|
+
},
|
|
874
|
+
|
|
875
|
+
// These regex's are used throughout the rest of can, so let's make
|
|
876
|
+
// them available.
|
|
877
|
+
replacer : replacer,
|
|
878
|
+
undHash : undHash
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
// ## construct.js
|
|
882
|
+
// `can.Construct`
|
|
883
|
+
// _This is a modified version of
|
|
884
|
+
// [John Resig's class](http://ejohn.org/blog/simple-javascript-inheritance/).
|
|
885
|
+
// It provides class level inheritance and callbacks._
|
|
886
|
+
|
|
887
|
+
// A private flag used to initialize a new class instance without
|
|
888
|
+
// initializing it's bindings.
|
|
889
|
+
var initializing = 0;
|
|
890
|
+
|
|
891
|
+
can.Construct = function() {
|
|
892
|
+
if (arguments.length) {
|
|
893
|
+
return can.Construct.extend.apply(can.Construct, arguments);
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
can.extend(can.Construct, {
|
|
898
|
+
newInstance: function() {
|
|
899
|
+
// Get a raw instance object (`init` is not called).
|
|
900
|
+
var inst = this.instance(),
|
|
901
|
+
arg = arguments,
|
|
902
|
+
args;
|
|
903
|
+
|
|
904
|
+
// Call `setup` if there is a `setup`
|
|
905
|
+
if ( inst.setup ) {
|
|
906
|
+
args = inst.setup.apply(inst, arguments);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// Call `init` if there is an `init`
|
|
910
|
+
// If `setup` returned `args`, use those as the arguments
|
|
911
|
+
if ( inst.init ) {
|
|
912
|
+
inst.init.apply(inst, args || arguments);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
return inst;
|
|
916
|
+
},
|
|
917
|
+
// Overwrites an object with methods. Used in the `super` plugin.
|
|
918
|
+
// `newProps` - New properties to add.
|
|
919
|
+
// `oldProps` - Where the old properties might be (used with `super`).
|
|
920
|
+
// `addTo` - What we are adding to.
|
|
921
|
+
_inherit: function( newProps, oldProps, addTo ) {
|
|
922
|
+
can.extend(addTo || newProps, newProps || {})
|
|
923
|
+
},
|
|
924
|
+
// used for overwriting a single property.
|
|
925
|
+
// this should be used for patching other objects
|
|
926
|
+
// the super plugin overwrites this
|
|
927
|
+
_overwrite : function(what, oldProps, propName, val){
|
|
928
|
+
what[propName] = val;
|
|
929
|
+
},
|
|
930
|
+
// Set `defaults` as the merger of the parent `defaults` and this
|
|
931
|
+
// object's `defaults`. If you overwrite this method, make sure to
|
|
932
|
+
// include option merging logic.
|
|
933
|
+
setup: function( base, fullName ) {
|
|
934
|
+
this.defaults = can.extend(true,{}, base.defaults, this.defaults);
|
|
935
|
+
},
|
|
936
|
+
// Create's a new `class` instance without initializing by setting the
|
|
937
|
+
// `initializing` flag.
|
|
938
|
+
instance: function() {
|
|
939
|
+
|
|
940
|
+
// Prevents running `init`.
|
|
941
|
+
initializing = 1;
|
|
942
|
+
|
|
943
|
+
var inst = new this();
|
|
944
|
+
|
|
945
|
+
// Allow running `init`.
|
|
946
|
+
initializing = 0;
|
|
947
|
+
|
|
948
|
+
return inst;
|
|
949
|
+
},
|
|
950
|
+
// Extends classes.
|
|
951
|
+
extend: function( fullName, klass, proto ) {
|
|
952
|
+
// Figure out what was passed and normalize it.
|
|
953
|
+
if ( typeof fullName != 'string' ) {
|
|
954
|
+
proto = klass;
|
|
955
|
+
klass = fullName;
|
|
956
|
+
fullName = null;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
if ( ! proto ) {
|
|
960
|
+
proto = klass;
|
|
961
|
+
klass = null;
|
|
962
|
+
}
|
|
963
|
+
proto = proto || {};
|
|
964
|
+
|
|
965
|
+
var _super_class = this,
|
|
966
|
+
_super = this.prototype,
|
|
967
|
+
name, shortName, namespace, prototype;
|
|
968
|
+
|
|
969
|
+
// Instantiate a base class (but only create the instance,
|
|
970
|
+
// don't run the init constructor).
|
|
971
|
+
prototype = this.instance();
|
|
972
|
+
|
|
973
|
+
// Copy the properties over onto the new prototype.
|
|
974
|
+
can.Construct._inherit(proto, _super, prototype);
|
|
975
|
+
|
|
976
|
+
// The dummy class constructor.
|
|
977
|
+
function Constructor() {
|
|
978
|
+
// All construction is actually done in the init method.
|
|
979
|
+
if ( ! initializing ) {
|
|
980
|
+
return this.constructor !== Constructor && arguments.length ?
|
|
981
|
+
// We are being called without `new` or we are extending.
|
|
982
|
+
arguments.callee.extend.apply(arguments.callee, arguments) :
|
|
983
|
+
// We are being called with `new`.
|
|
984
|
+
this.constructor.newInstance.apply(this.constructor, arguments);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// Copy old stuff onto class (can probably be merged w/ inherit)
|
|
989
|
+
for ( name in _super_class ) {
|
|
990
|
+
if ( _super_class.hasOwnProperty(name) ) {
|
|
991
|
+
Constructor[name] = _super_class[name];
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// Copy new static properties on class.
|
|
996
|
+
can.Construct._inherit(klass, _super_class, Constructor);
|
|
997
|
+
|
|
998
|
+
// Setup namespaces.
|
|
999
|
+
if ( fullName ) {
|
|
1000
|
+
|
|
1001
|
+
var parts = fullName.split('.'),
|
|
1002
|
+
shortName = parts.pop(),
|
|
1003
|
+
current = can.getObject(parts.join('.'), window, true),
|
|
1004
|
+
namespace = current,
|
|
1005
|
+
_fullName = can.underscore(fullName.replace(/\./g, "_")),
|
|
1006
|
+
_shortName = can.underscore(shortName);
|
|
1007
|
+
|
|
1008
|
+
//@steal-remove-start
|
|
1009
|
+
if(current[shortName]){
|
|
1010
|
+
|
|
1011
|
+
}
|
|
1012
|
+
//@steal-remove-end
|
|
1013
|
+
|
|
1014
|
+
current[shortName] = Constructor;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// Set things that shouldn't be overwritten.
|
|
1018
|
+
can.extend(Constructor, {
|
|
1019
|
+
constructor: Constructor,
|
|
1020
|
+
prototype: prototype,
|
|
1021
|
+
namespace: namespace,
|
|
1022
|
+
shortName: shortName,
|
|
1023
|
+
_shortName : _shortName,
|
|
1024
|
+
fullName: fullName,
|
|
1025
|
+
_fullName: _fullName
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
// Make sure our prototype looks nice.
|
|
1029
|
+
Constructor.prototype.constructor = Constructor;
|
|
1030
|
+
|
|
1031
|
+
|
|
1032
|
+
// Call the class `setup` and `init`
|
|
1033
|
+
var t = [_super_class].concat(can.makeArray(arguments)),
|
|
1034
|
+
args = Constructor.setup.apply(Constructor, t );
|
|
1035
|
+
|
|
1036
|
+
if ( Constructor.init ) {
|
|
1037
|
+
Constructor.init.apply(Constructor, args || t );
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
return Constructor;
|
|
1041
|
+
//
|
|
1042
|
+
//
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
// ## observe.js
|
|
1048
|
+
// `can.Observe`
|
|
1049
|
+
// _Provides the observable pattern for JavaScript Objects._
|
|
1050
|
+
//
|
|
1051
|
+
// Returns `true` if something is an object with properties of its own.
|
|
1052
|
+
var canMakeObserve = function( obj ) {
|
|
1053
|
+
return obj && typeof obj === 'object' && !(obj instanceof Date);
|
|
1054
|
+
},
|
|
1055
|
+
|
|
1056
|
+
// Removes all listeners.
|
|
1057
|
+
unhookup = function(items, namespace){
|
|
1058
|
+
return can.each(items, function(item){
|
|
1059
|
+
if(item && item.unbind){
|
|
1060
|
+
item.unbind("change" + namespace);
|
|
1061
|
+
}
|
|
1062
|
+
});
|
|
1063
|
+
},
|
|
1064
|
+
// Listens to changes on `val` and "bubbles" the event up.
|
|
1065
|
+
// `val` - The object to listen for changes on.
|
|
1066
|
+
// `prop` - The property name is at on.
|
|
1067
|
+
// `parent` - The parent object of prop.
|
|
1068
|
+
hookupBubble = function( val, prop, parent ) {
|
|
1069
|
+
// If it's an `array` make a list, otherwise a val.
|
|
1070
|
+
if (val instanceof Observe){
|
|
1071
|
+
// We have an `observe` already...
|
|
1072
|
+
// Make sure it is not listening to this already
|
|
1073
|
+
unhookup([val], parent._namespace);
|
|
1074
|
+
} else if ( can.isArray(val) ) {
|
|
1075
|
+
val = new Observe.List(val);
|
|
1076
|
+
} else {
|
|
1077
|
+
val = new Observe(val);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// Listen to all changes and `batchTrigger` upwards.
|
|
1081
|
+
val.bind("change" + parent._namespace, function( ev, attr ) {
|
|
1082
|
+
// `batchTrigger` the type on this...
|
|
1083
|
+
var args = can.makeArray(arguments),
|
|
1084
|
+
ev = args.shift();
|
|
1085
|
+
args[0] = prop === "*" ?
|
|
1086
|
+
parent.indexOf(val)+"." + args[0] :
|
|
1087
|
+
prop + "." + args[0];
|
|
1088
|
+
// track objects dispatched on this observe
|
|
1089
|
+
ev.triggeredNS = ev.triggeredNS || {};
|
|
1090
|
+
// if it has already been dispatched exit
|
|
1091
|
+
if (ev.triggeredNS[parent._namespace]) {
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
ev.triggeredNS[parent._namespace] = true;
|
|
1095
|
+
|
|
1096
|
+
can.trigger(parent, ev, args);
|
|
1097
|
+
can.trigger(parent,args[0],args);
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
return val;
|
|
1101
|
+
},
|
|
1102
|
+
|
|
1103
|
+
// An `id` to track events for a given observe.
|
|
1104
|
+
observeId = 0,
|
|
1105
|
+
// A reference to an `array` of events that will be dispatched.
|
|
1106
|
+
collecting = undefined,
|
|
1107
|
+
// Call to start collecting events (`Observe` sends all events at
|
|
1108
|
+
// once).
|
|
1109
|
+
collect = function() {
|
|
1110
|
+
if (!collecting ) {
|
|
1111
|
+
collecting = [];
|
|
1112
|
+
return true;
|
|
1113
|
+
}
|
|
1114
|
+
},
|
|
1115
|
+
// Creates an event on item, but will not send immediately
|
|
1116
|
+
// if collecting events.
|
|
1117
|
+
// `item` - The item the event should happen on.
|
|
1118
|
+
// `event` - The event name, ex: `change`.
|
|
1119
|
+
// `args` - Tn array of arguments.
|
|
1120
|
+
batchTrigger = function( item, event, args ) {
|
|
1121
|
+
// Don't send events if initalizing.
|
|
1122
|
+
if ( ! item._init) {
|
|
1123
|
+
if (!collecting ) {
|
|
1124
|
+
return can.trigger(item, event, args);
|
|
1125
|
+
} else {
|
|
1126
|
+
collecting.push([
|
|
1127
|
+
item,
|
|
1128
|
+
{
|
|
1129
|
+
type: event,
|
|
1130
|
+
batchNum : batchNum
|
|
1131
|
+
},
|
|
1132
|
+
args ] );
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
},
|
|
1136
|
+
// Which batch of events this is for -- might not want to send multiple
|
|
1137
|
+
// messages on the same batch. This is mostly for event delegation.
|
|
1138
|
+
batchNum = 1,
|
|
1139
|
+
// Sends all pending events.
|
|
1140
|
+
sendCollection = function() {
|
|
1141
|
+
var items = collecting.slice(0);
|
|
1142
|
+
collecting = undefined;
|
|
1143
|
+
batchNum++;
|
|
1144
|
+
can.each(items, function( item ) {
|
|
1145
|
+
can.trigger.apply(can, item)
|
|
1146
|
+
})
|
|
1147
|
+
|
|
1148
|
+
},
|
|
1149
|
+
// A helper used to serialize an `Observe` or `Observe.List`.
|
|
1150
|
+
// `observe` - The observable.
|
|
1151
|
+
// `how` - To serialize with `attr` or `serialize`.
|
|
1152
|
+
// `where` - To put properties, in an `{}` or `[]`.
|
|
1153
|
+
serialize = function( observe, how, where ) {
|
|
1154
|
+
// Go through each property.
|
|
1155
|
+
observe.each(function( val, name ) {
|
|
1156
|
+
// If the value is an `object`, and has an `attrs` or `serialize` function.
|
|
1157
|
+
where[name] = canMakeObserve(val) && can.isFunction( val[how] ) ?
|
|
1158
|
+
// Call `attrs` or `serialize` to get the original data back.
|
|
1159
|
+
val[how]() :
|
|
1160
|
+
// Otherwise return the value.
|
|
1161
|
+
val
|
|
1162
|
+
})
|
|
1163
|
+
return where;
|
|
1164
|
+
},
|
|
1165
|
+
$method = function( name ) {
|
|
1166
|
+
return function() {
|
|
1167
|
+
return can[name].apply(this, arguments );
|
|
1168
|
+
}
|
|
1169
|
+
},
|
|
1170
|
+
bind = $method('addEvent'),
|
|
1171
|
+
unbind = $method('removeEvent'),
|
|
1172
|
+
attrParts = function(attr){
|
|
1173
|
+
return can.isArray(attr) ? attr : (""+attr).split(".")
|
|
1174
|
+
};
|
|
1175
|
+
var Observe = can.Construct('can.Observe', {
|
|
1176
|
+
// keep so it can be overwritten
|
|
1177
|
+
setup : function(){
|
|
1178
|
+
can.Construct.setup.apply(this, arguments)
|
|
1179
|
+
},
|
|
1180
|
+
bind : bind,
|
|
1181
|
+
unbind: unbind,
|
|
1182
|
+
id: "id"
|
|
1183
|
+
},
|
|
1184
|
+
{
|
|
1185
|
+
setup: function( obj ) {
|
|
1186
|
+
// `_data` is where we keep the properties.
|
|
1187
|
+
this._data = {};
|
|
1188
|
+
// The namespace this `object` uses to listen to events.
|
|
1189
|
+
this._namespace = ".observe" + (++observeId);
|
|
1190
|
+
// Sets all `attrs`.
|
|
1191
|
+
this._init = 1;
|
|
1192
|
+
this.attr(obj);
|
|
1193
|
+
delete this._init;
|
|
1194
|
+
},
|
|
1195
|
+
attr: function( attr, val ) {
|
|
1196
|
+
// This is super obfuscated for space -- basically, we're checking
|
|
1197
|
+
// if the type of the attribute is not a `number` or a `string`.
|
|
1198
|
+
if ( !~ "ns".indexOf((typeof attr).charAt(0))) {
|
|
1199
|
+
return this._attrs(attr, val)
|
|
1200
|
+
} else if ( val === undefined ) {// If we are getting a value.
|
|
1201
|
+
// Let people know we are reading.
|
|
1202
|
+
Observe.__reading && Observe.__reading(this, attr)
|
|
1203
|
+
return this._get(attr)
|
|
1204
|
+
} else {
|
|
1205
|
+
// Otherwise we are setting.
|
|
1206
|
+
this._set(attr, val);
|
|
1207
|
+
return this;
|
|
1208
|
+
}
|
|
1209
|
+
},
|
|
1210
|
+
each: function() {
|
|
1211
|
+
return can.each.apply(undefined, [this.__get()].concat(can.makeArray(arguments)))
|
|
1212
|
+
},
|
|
1213
|
+
removeAttr: function( attr ) {
|
|
1214
|
+
// Convert the `attr` into parts (if nested).
|
|
1215
|
+
var parts = attrParts(attr),
|
|
1216
|
+
// The actual property to remove.
|
|
1217
|
+
prop = parts.shift(),
|
|
1218
|
+
// The current value.
|
|
1219
|
+
current = this._data[prop];
|
|
1220
|
+
|
|
1221
|
+
// If we have more parts, call `removeAttr` on that part.
|
|
1222
|
+
if ( parts.length ) {
|
|
1223
|
+
return current.removeAttr(parts)
|
|
1224
|
+
} else {
|
|
1225
|
+
// Otherwise, `delete`.
|
|
1226
|
+
delete this._data[prop];
|
|
1227
|
+
// Create the event.
|
|
1228
|
+
if (!(prop in this.constructor.prototype)) {
|
|
1229
|
+
delete this[prop]
|
|
1230
|
+
}
|
|
1231
|
+
batchTrigger(this, "change", [prop, "remove", undefined, current]);
|
|
1232
|
+
batchTrigger(this, prop, [undefined, current]);
|
|
1233
|
+
return current;
|
|
1234
|
+
}
|
|
1235
|
+
},
|
|
1236
|
+
// Reads a property from the `object`.
|
|
1237
|
+
_get: function( attr ) {
|
|
1238
|
+
var parts = attrParts(attr),
|
|
1239
|
+
current = this.__get(parts.shift());
|
|
1240
|
+
return parts.length ? current ? current._get(parts) : undefined : current;
|
|
1241
|
+
},
|
|
1242
|
+
// Reads a property directly if an `attr` is provided, otherwise
|
|
1243
|
+
// returns the "real" data object itself.
|
|
1244
|
+
__get: function( attr ) {
|
|
1245
|
+
return attr ? this._data[attr] : this._data;
|
|
1246
|
+
},
|
|
1247
|
+
// Sets `attr` prop as value on this object where.
|
|
1248
|
+
// `attr` - Is a string of properties or an array of property values.
|
|
1249
|
+
// `value` - The raw value to set.
|
|
1250
|
+
_set: function( attr, value ) {
|
|
1251
|
+
// Convert `attr` to attr parts (if it isn't already).
|
|
1252
|
+
var parts = attrParts(attr),
|
|
1253
|
+
// The immediate prop we are setting.
|
|
1254
|
+
prop = parts.shift(),
|
|
1255
|
+
// The current value.
|
|
1256
|
+
current = this.__get(prop);
|
|
1257
|
+
|
|
1258
|
+
// If we have an `object` and remaining parts.
|
|
1259
|
+
if ( canMakeObserve(current) && parts.length ) {
|
|
1260
|
+
// That `object` should set it (this might need to call attr).
|
|
1261
|
+
current._set(parts, value)
|
|
1262
|
+
} else if (!parts.length ) {
|
|
1263
|
+
// We're in "real" set territory.
|
|
1264
|
+
if(this.__convert){
|
|
1265
|
+
value = this.__convert(prop, value)
|
|
1266
|
+
}
|
|
1267
|
+
this.__set(prop, value, current)
|
|
1268
|
+
|
|
1269
|
+
} else {
|
|
1270
|
+
throw "can.Observe: Object does not exist"
|
|
1271
|
+
}
|
|
1272
|
+
},
|
|
1273
|
+
__set : function(prop, value, current){
|
|
1274
|
+
|
|
1275
|
+
// Otherwise, we are setting it on this `object`.
|
|
1276
|
+
// TODO: Check if value is object and transform
|
|
1277
|
+
// are we changing the value.
|
|
1278
|
+
if ( value !== current ) {
|
|
1279
|
+
|
|
1280
|
+
// Check if we are adding this for the first time --
|
|
1281
|
+
// if we are, we need to create an `add` event.
|
|
1282
|
+
var changeType = this.__get().hasOwnProperty(prop) ? "set" : "add";
|
|
1283
|
+
|
|
1284
|
+
// Set the value on data.
|
|
1285
|
+
this.___set(prop,
|
|
1286
|
+
|
|
1287
|
+
// If we are getting an object.
|
|
1288
|
+
canMakeObserve(value) ?
|
|
1289
|
+
|
|
1290
|
+
// Hook it up to send event.
|
|
1291
|
+
hookupBubble(value, prop, this) :
|
|
1292
|
+
// Value is normal.
|
|
1293
|
+
value);
|
|
1294
|
+
|
|
1295
|
+
// `batchTrigger` the change event.
|
|
1296
|
+
batchTrigger(this, "change", [prop, changeType, value, current]);
|
|
1297
|
+
batchTrigger(this, prop, [value, current]);
|
|
1298
|
+
// If we can stop listening to our old value, do it.
|
|
1299
|
+
current && unhookup([current], this._namespace);
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
},
|
|
1303
|
+
// Directly sets a property on this `object`.
|
|
1304
|
+
___set: function( prop, val ) {
|
|
1305
|
+
this._data[prop] = val;
|
|
1306
|
+
// Add property directly for easy writing.
|
|
1307
|
+
// Check if its on the `prototype` so we don't overwrite methods like `attrs`.
|
|
1308
|
+
if (!(prop in this.constructor.prototype)) {
|
|
1309
|
+
this[prop] = val
|
|
1310
|
+
}
|
|
1311
|
+
},
|
|
1312
|
+
bind: bind,
|
|
1313
|
+
unbind: unbind,
|
|
1314
|
+
serialize: function() {
|
|
1315
|
+
return serialize(this, 'serialize', {});
|
|
1316
|
+
},
|
|
1317
|
+
_attrs: function( props, remove ) {
|
|
1318
|
+
if ( props === undefined ) {
|
|
1319
|
+
return serialize(this, 'attr', {})
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
props = can.extend(true, {}, props);
|
|
1323
|
+
var prop,
|
|
1324
|
+
collectingStarted = collect(),
|
|
1325
|
+
self = this,
|
|
1326
|
+
newVal;
|
|
1327
|
+
|
|
1328
|
+
this.each(function(curVal, prop){
|
|
1329
|
+
newVal = props[prop];
|
|
1330
|
+
|
|
1331
|
+
// If we are merging...
|
|
1332
|
+
if ( newVal === undefined ) {
|
|
1333
|
+
remove && self.removeAttr(prop);
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
if ( canMakeObserve(curVal) && canMakeObserve(newVal) ) {
|
|
1337
|
+
curVal.attr(newVal, remove)
|
|
1338
|
+
} else if ( curVal != newVal ) {
|
|
1339
|
+
self._set(prop, newVal)
|
|
1340
|
+
} else {
|
|
1341
|
+
|
|
1342
|
+
}
|
|
1343
|
+
delete props[prop];
|
|
1344
|
+
})
|
|
1345
|
+
// Add remaining props.
|
|
1346
|
+
for ( var prop in props ) {
|
|
1347
|
+
newVal = props[prop];
|
|
1348
|
+
this._set(prop, newVal)
|
|
1349
|
+
}
|
|
1350
|
+
if ( collectingStarted ) {
|
|
1351
|
+
sendCollection();
|
|
1352
|
+
}
|
|
1353
|
+
return this;
|
|
1354
|
+
}
|
|
1355
|
+
});
|
|
1356
|
+
// Helpers for `observable` lists.
|
|
1357
|
+
var splice = [].splice,
|
|
1358
|
+
list = Observe('can.Observe.List',
|
|
1359
|
+
{
|
|
1360
|
+
setup: function( instances, options ) {
|
|
1361
|
+
this.length = 0;
|
|
1362
|
+
this._namespace = ".observe" + (++observeId);
|
|
1363
|
+
this._init = 1;
|
|
1364
|
+
this.bind('change',can.proxy(this._changes,this));
|
|
1365
|
+
this.push.apply(this, can.makeArray(instances || []));
|
|
1366
|
+
can.extend(this, options);
|
|
1367
|
+
delete this._init;
|
|
1368
|
+
},
|
|
1369
|
+
_changes : function(ev, attr, how, newVal, oldVal){
|
|
1370
|
+
// `batchTrigger` direct add and remove events...
|
|
1371
|
+
if ( !~ attr.indexOf('.')){
|
|
1372
|
+
|
|
1373
|
+
if( how === 'add' ) {
|
|
1374
|
+
batchTrigger(this, how, [newVal,+attr]);
|
|
1375
|
+
batchTrigger(this,'length',[this.length]);
|
|
1376
|
+
} else if( how === 'remove' ) {
|
|
1377
|
+
batchTrigger(this, how, [oldVal, +attr]);
|
|
1378
|
+
batchTrigger(this,'length',[this.length]);
|
|
1379
|
+
} else {
|
|
1380
|
+
batchTrigger(this,how,[newVal, +attr])
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
}
|
|
1384
|
+
},
|
|
1385
|
+
__get : function(attr){
|
|
1386
|
+
return attr ? this[attr] : this;
|
|
1387
|
+
},
|
|
1388
|
+
___set : function(attr, val){
|
|
1389
|
+
this[attr] = val;
|
|
1390
|
+
if(+attr >= this.length){
|
|
1391
|
+
this.length = (+attr+1)
|
|
1392
|
+
}
|
|
1393
|
+
},
|
|
1394
|
+
// Returns the serialized form of this list.
|
|
1395
|
+
serialize: function() {
|
|
1396
|
+
return serialize(this, 'serialize', []);
|
|
1397
|
+
},
|
|
1398
|
+
//
|
|
1399
|
+
splice: function( index, howMany ) {
|
|
1400
|
+
var args = can.makeArray(arguments),
|
|
1401
|
+
i;
|
|
1402
|
+
|
|
1403
|
+
for ( i = 2; i < args.length; i++ ) {
|
|
1404
|
+
var val = args[i];
|
|
1405
|
+
if ( canMakeObserve(val) ) {
|
|
1406
|
+
args[i] = hookupBubble(val, "*", this)
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
if ( howMany === undefined ) {
|
|
1410
|
+
howMany = args[1] = this.length - index;
|
|
1411
|
+
}
|
|
1412
|
+
var removed = splice.apply(this, args);
|
|
1413
|
+
if ( howMany > 0 ) {
|
|
1414
|
+
batchTrigger(this, "change", [""+index, "remove", undefined, removed]);
|
|
1415
|
+
unhookup(removed, this._namespace);
|
|
1416
|
+
}
|
|
1417
|
+
if ( args.length > 2 ) {
|
|
1418
|
+
batchTrigger(this, "change", [""+index, "add", args.slice(2), removed]);
|
|
1419
|
+
}
|
|
1420
|
+
return removed;
|
|
1421
|
+
},
|
|
1422
|
+
_attrs: function( props, remove ) {
|
|
1423
|
+
if ( props === undefined ) {
|
|
1424
|
+
return serialize(this, 'attr', []);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
// Create a copy.
|
|
1428
|
+
props = props.slice(0);
|
|
1429
|
+
|
|
1430
|
+
var len = Math.min(props.length, this.length),
|
|
1431
|
+
collectingStarted = collect(),
|
|
1432
|
+
prop;
|
|
1433
|
+
|
|
1434
|
+
for ( var prop = 0; prop < len; prop++ ) {
|
|
1435
|
+
var curVal = this[prop],
|
|
1436
|
+
newVal = props[prop];
|
|
1437
|
+
|
|
1438
|
+
if ( canMakeObserve(curVal) && canMakeObserve(newVal) ) {
|
|
1439
|
+
curVal.attr(newVal, remove)
|
|
1440
|
+
} else if ( curVal != newVal ) {
|
|
1441
|
+
this._set(prop, newVal)
|
|
1442
|
+
} else {
|
|
1443
|
+
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
if ( props.length > this.length ) {
|
|
1447
|
+
// Add in the remaining props.
|
|
1448
|
+
this.push(props.slice(this.length))
|
|
1449
|
+
} else if ( props.length < this.length && remove ) {
|
|
1450
|
+
this.splice(props.length)
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
if ( collectingStarted ) {
|
|
1454
|
+
sendCollection()
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
}),
|
|
1458
|
+
|
|
1459
|
+
// Converts to an `array` of arguments.
|
|
1460
|
+
getArgs = function( args ) {
|
|
1461
|
+
return args[0] && can.isArray(args[0]) ?
|
|
1462
|
+
args[0] :
|
|
1463
|
+
can.makeArray(args);
|
|
1464
|
+
};
|
|
1465
|
+
// Create `push`, `pop`, `shift`, and `unshift`
|
|
1466
|
+
can.each({
|
|
1467
|
+
push: "length",
|
|
1468
|
+
unshift: 0
|
|
1469
|
+
},
|
|
1470
|
+
// Adds a method
|
|
1471
|
+
// `name` - The method name.
|
|
1472
|
+
// `where` - Where items in the `array` should be added.
|
|
1473
|
+
function( where, name ) {
|
|
1474
|
+
list.prototype[name] = function() {
|
|
1475
|
+
// Get the items being added.
|
|
1476
|
+
var args = getArgs(arguments),
|
|
1477
|
+
// Where we are going to add items.
|
|
1478
|
+
len = where ? this.length : 0;
|
|
1479
|
+
|
|
1480
|
+
// Go through and convert anything to an `observe` that needs to be converted.
|
|
1481
|
+
for ( var i = 0; i < args.length; i++ ) {
|
|
1482
|
+
var val = args[i];
|
|
1483
|
+
if ( canMakeObserve(val) ) {
|
|
1484
|
+
args[i] = hookupBubble(val, "*", this)
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// Call the original method.
|
|
1489
|
+
var res = [][name].apply(this, args);
|
|
1490
|
+
|
|
1491
|
+
if ( !this.comparator || !args.length ) {
|
|
1492
|
+
batchTrigger(this, "change", [""+len, "add", args, undefined])
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
return res;
|
|
1496
|
+
}
|
|
1497
|
+
});
|
|
1498
|
+
|
|
1499
|
+
can.each({
|
|
1500
|
+
pop: "length",
|
|
1501
|
+
shift: 0
|
|
1502
|
+
},
|
|
1503
|
+
// Creates a `remove` type method
|
|
1504
|
+
function( where, name ) {
|
|
1505
|
+
list.prototype[name] = function() {
|
|
1506
|
+
|
|
1507
|
+
var args = getArgs(arguments),
|
|
1508
|
+
len = where && this.length ? this.length - 1 : 0;
|
|
1509
|
+
|
|
1510
|
+
var res = [][name].apply(this, args)
|
|
1511
|
+
|
|
1512
|
+
// Create a change where the args are
|
|
1513
|
+
// `*` - Change on potentially multiple properties.
|
|
1514
|
+
// `remove` - Items removed.
|
|
1515
|
+
// `undefined` - The new values (there are none).
|
|
1516
|
+
// `res` - The old, removed values (should these be unbound).
|
|
1517
|
+
// `len` - Where these items were removed.
|
|
1518
|
+
batchTrigger(this, "change", [""+len, "remove", undefined, [res]])
|
|
1519
|
+
|
|
1520
|
+
if ( res && res.unbind ) {
|
|
1521
|
+
res.unbind("change" + this._namespace)
|
|
1522
|
+
}
|
|
1523
|
+
return res;
|
|
1524
|
+
}
|
|
1525
|
+
});
|
|
1526
|
+
|
|
1527
|
+
list.prototype.
|
|
1528
|
+
indexOf = [].indexOf || function(item){
|
|
1529
|
+
return can.inArray(item, this)
|
|
1530
|
+
};
|
|
1531
|
+
|
|
1532
|
+
|
|
1533
|
+
// ## model.js
|
|
1534
|
+
// `can.Model`
|
|
1535
|
+
// _A `can.Observe` that connects to a RESTful interface._
|
|
1536
|
+
//
|
|
1537
|
+
// Generic deferred piping function
|
|
1538
|
+
var pipe = function( def, model, func ) {
|
|
1539
|
+
var d = new can.Deferred();
|
|
1540
|
+
def.then(function(){
|
|
1541
|
+
arguments[0] = model[func](arguments[0])
|
|
1542
|
+
d.resolve.apply(d, arguments)
|
|
1543
|
+
},function(){
|
|
1544
|
+
d.rejectWith.apply(this,arguments)
|
|
1545
|
+
})
|
|
1546
|
+
return d;
|
|
1547
|
+
},
|
|
1548
|
+
modelNum = 0,
|
|
1549
|
+
ignoreHookup = /change.observe\d+/,
|
|
1550
|
+
getId = function( inst ) {
|
|
1551
|
+
return inst[inst.constructor.id]
|
|
1552
|
+
},
|
|
1553
|
+
// Ajax `options` generator function
|
|
1554
|
+
ajax = function( ajaxOb, data, type, dataType, success, error ) {
|
|
1555
|
+
|
|
1556
|
+
|
|
1557
|
+
// If we get a string, handle it.
|
|
1558
|
+
if ( typeof ajaxOb == "string" ) {
|
|
1559
|
+
// If there's a space, it's probably the type.
|
|
1560
|
+
var parts = ajaxOb.split(" ")
|
|
1561
|
+
ajaxOb = {
|
|
1562
|
+
url : parts.pop()
|
|
1563
|
+
};
|
|
1564
|
+
if(parts.length){
|
|
1565
|
+
ajaxOb.type = parts.pop();
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
// If we are a non-array object, copy to a new attrs.
|
|
1570
|
+
ajaxOb.data = typeof data == "object" && !can.isArray(data) ?
|
|
1571
|
+
can.extend(ajaxOb.data || {}, data) : data;
|
|
1572
|
+
|
|
1573
|
+
|
|
1574
|
+
// Get the url with any templated values filled out.
|
|
1575
|
+
ajaxOb.url = can.sub(ajaxOb.url, ajaxOb.data, true);
|
|
1576
|
+
|
|
1577
|
+
return can.ajax(can.extend({
|
|
1578
|
+
type: type || "post",
|
|
1579
|
+
dataType: dataType ||"json",
|
|
1580
|
+
success : success,
|
|
1581
|
+
error: error
|
|
1582
|
+
}, ajaxOb ));
|
|
1583
|
+
},
|
|
1584
|
+
makeRequest = function( self, type, success, error, method ) {
|
|
1585
|
+
var deferred ,
|
|
1586
|
+
args = [self.serialize()],
|
|
1587
|
+
// The model.
|
|
1588
|
+
model = self.constructor,
|
|
1589
|
+
jqXHR;
|
|
1590
|
+
|
|
1591
|
+
// `destroy` does not need data.
|
|
1592
|
+
if ( type == 'destroy' ) {
|
|
1593
|
+
args.shift();
|
|
1594
|
+
}
|
|
1595
|
+
// `update` and `destroy` need the `id`.
|
|
1596
|
+
if ( type !== 'create' ) {
|
|
1597
|
+
args.unshift(getId(self))
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
jqXHR = model[type].apply(model, args);
|
|
1601
|
+
|
|
1602
|
+
deferred = jqXHR.pipe(function(data){
|
|
1603
|
+
self[method || type + "d"](data, jqXHR);
|
|
1604
|
+
return self
|
|
1605
|
+
})
|
|
1606
|
+
|
|
1607
|
+
// Hook up `abort`
|
|
1608
|
+
if(jqXHR.abort){
|
|
1609
|
+
deferred.abort = function(){
|
|
1610
|
+
jqXHR.abort();
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
return deferred.then(success,error);
|
|
1615
|
+
},
|
|
1616
|
+
|
|
1617
|
+
// This object describes how to make an ajax request for each ajax method.
|
|
1618
|
+
// The available properties are:
|
|
1619
|
+
// `url` - The default url to use as indicated as a property on the model.
|
|
1620
|
+
// `type` - The default http request type
|
|
1621
|
+
// `data` - A method that takes the `arguments` and returns `data` used for ajax.
|
|
1622
|
+
//
|
|
1623
|
+
//
|
|
1624
|
+
//
|
|
1625
|
+
ajaxMethods = {
|
|
1626
|
+
create : {
|
|
1627
|
+
url : "_shortName",
|
|
1628
|
+
type :"post"
|
|
1629
|
+
},
|
|
1630
|
+
update : {
|
|
1631
|
+
data : function(id, attrs){
|
|
1632
|
+
attrs = attrs || {};
|
|
1633
|
+
var identity = this.id;
|
|
1634
|
+
if ( attrs[identity] && attrs[identity] !== id ) {
|
|
1635
|
+
attrs["new" + can.capitalize(id)] = attrs[identity];
|
|
1636
|
+
delete attrs[identity];
|
|
1637
|
+
}
|
|
1638
|
+
attrs[identity] = id;
|
|
1639
|
+
return attrs;
|
|
1640
|
+
},
|
|
1641
|
+
type : "put"
|
|
1642
|
+
},
|
|
1643
|
+
destroy : {
|
|
1644
|
+
type : "delete",
|
|
1645
|
+
data : function(id){
|
|
1646
|
+
var args = {};
|
|
1647
|
+
args[this.id] = id;
|
|
1648
|
+
return args;
|
|
1649
|
+
}
|
|
1650
|
+
},
|
|
1651
|
+
findAll : {
|
|
1652
|
+
url : "_shortName"
|
|
1653
|
+
},
|
|
1654
|
+
findOne: {}
|
|
1655
|
+
},
|
|
1656
|
+
// Makes an ajax request `function` from a string.
|
|
1657
|
+
// `ajaxMethod` - The `ajaxMethod` object defined above.
|
|
1658
|
+
// `str` - The string the user provided. Ex: `findAll: "/recipes.json"`.
|
|
1659
|
+
ajaxMaker = function(ajaxMethod, str){
|
|
1660
|
+
// Return a `function` that serves as the ajax method.
|
|
1661
|
+
return function(data){
|
|
1662
|
+
// If the ajax method has it's own way of getting `data`, use that.
|
|
1663
|
+
data = ajaxMethod.data ?
|
|
1664
|
+
ajaxMethod.data.apply(this, arguments) :
|
|
1665
|
+
// Otherwise use the data passed in.
|
|
1666
|
+
data;
|
|
1667
|
+
// Return the ajax method with `data` and the `type` provided.
|
|
1668
|
+
return ajax(str || this[ajaxMethod.url || "_url"], data, ajaxMethod.type || "get")
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
|
|
1673
|
+
|
|
1674
|
+
can.Observe("can.Model",{
|
|
1675
|
+
setup : function(base){
|
|
1676
|
+
can.Observe.apply(this, arguments);
|
|
1677
|
+
if(this === can.Model){
|
|
1678
|
+
return;
|
|
1679
|
+
}
|
|
1680
|
+
var self = this,
|
|
1681
|
+
clean = can.proxy(this._clean, self);
|
|
1682
|
+
|
|
1683
|
+
can.each(ajaxMethods, function(method, name){
|
|
1684
|
+
if ( ! can.isFunction( self[name] )) {
|
|
1685
|
+
self[name] = ajaxMaker(method, self[name]);
|
|
1686
|
+
}
|
|
1687
|
+
if (self["make"+can.capitalize(name)]){
|
|
1688
|
+
var newMethod = self["make"+can.capitalize(name)](self[name]);
|
|
1689
|
+
can.Construct._overwrite(self, base, name,function(){
|
|
1690
|
+
this._super;
|
|
1691
|
+
this._reqs++;
|
|
1692
|
+
return newMethod.apply(this, arguments).then(clean, clean);
|
|
1693
|
+
})
|
|
1694
|
+
}
|
|
1695
|
+
});
|
|
1696
|
+
|
|
1697
|
+
if(!self.fullName || self.fullName == base.fullName){
|
|
1698
|
+
self.fullName = self._shortName = "Model"+(++modelNum);
|
|
1699
|
+
}
|
|
1700
|
+
// Ddd ajax converters.
|
|
1701
|
+
this.store = {};
|
|
1702
|
+
this._reqs = 0;
|
|
1703
|
+
this._url = this._shortName+"/{"+this.id+"}"
|
|
1704
|
+
},
|
|
1705
|
+
_ajax : ajaxMaker,
|
|
1706
|
+
_clean : function(){
|
|
1707
|
+
this._reqs--;
|
|
1708
|
+
if(!this._reqs){
|
|
1709
|
+
for(var id in this.store) {
|
|
1710
|
+
if(!this.store[id]._bindings){
|
|
1711
|
+
delete this.store[id];
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
},
|
|
1716
|
+
models: function( instancesRawData ) {
|
|
1717
|
+
|
|
1718
|
+
if ( ! instancesRawData ) {
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
if ( instancesRawData instanceof this.List ) {
|
|
1723
|
+
return instancesRawData;
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
// Get the list type.
|
|
1727
|
+
var self = this,
|
|
1728
|
+
res = new( self.List || ML),
|
|
1729
|
+
// Did we get an `array`?
|
|
1730
|
+
arr = can.isArray(instancesRawData),
|
|
1731
|
+
|
|
1732
|
+
// Did we get a model list?
|
|
1733
|
+
ml = (instancesRawData instanceof ML),
|
|
1734
|
+
|
|
1735
|
+
// Get the raw `array` of objects.
|
|
1736
|
+
raw = arr ?
|
|
1737
|
+
|
|
1738
|
+
// If an `array`, return the `array`.
|
|
1739
|
+
instancesRawData :
|
|
1740
|
+
|
|
1741
|
+
// Otherwise if a model list.
|
|
1742
|
+
(ml ?
|
|
1743
|
+
|
|
1744
|
+
// Get the raw objects from the list.
|
|
1745
|
+
instancesRawData.serialize() :
|
|
1746
|
+
|
|
1747
|
+
// Get the object's data.
|
|
1748
|
+
instancesRawData.data),
|
|
1749
|
+
i = 0;
|
|
1750
|
+
|
|
1751
|
+
|
|
1752
|
+
|
|
1753
|
+
can.each(raw, function( rawPart ) {
|
|
1754
|
+
res.push( self.model( rawPart ));
|
|
1755
|
+
});
|
|
1756
|
+
|
|
1757
|
+
if ( ! arr ) { // Push other stuff onto `array`.
|
|
1758
|
+
can.each(instancesRawData, function(val, prop){
|
|
1759
|
+
if ( prop !== 'data' ) {
|
|
1760
|
+
res[prop] = val;
|
|
1761
|
+
}
|
|
1762
|
+
})
|
|
1763
|
+
}
|
|
1764
|
+
return res;
|
|
1765
|
+
},
|
|
1766
|
+
model: function( attributes ) {
|
|
1767
|
+
if (!attributes ) {
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
if ( attributes instanceof this ) {
|
|
1771
|
+
attributes = attributes.serialize();
|
|
1772
|
+
}
|
|
1773
|
+
var model = this.store[attributes[this.id]] ? this.store[attributes[this.id]].attr(attributes) : new this( attributes );
|
|
1774
|
+
if(this._reqs){
|
|
1775
|
+
this.store[attributes[this.id]] = model;
|
|
1776
|
+
}
|
|
1777
|
+
return model;
|
|
1778
|
+
}
|
|
1779
|
+
},
|
|
1780
|
+
{
|
|
1781
|
+
isNew: function() {
|
|
1782
|
+
var id = getId(this);
|
|
1783
|
+
return ! ( id || id === 0 ); // If `null` or `undefined`
|
|
1784
|
+
},
|
|
1785
|
+
save: function( success, error ) {
|
|
1786
|
+
return makeRequest(this, this.isNew() ? 'create' : 'update', success, error);
|
|
1787
|
+
},
|
|
1788
|
+
destroy: function( success, error ) {
|
|
1789
|
+
return makeRequest(this, 'destroy', success, error, 'destroyed');
|
|
1790
|
+
},
|
|
1791
|
+
bind : function(eventName){
|
|
1792
|
+
if ( ! ignoreHookup.test( eventName )) {
|
|
1793
|
+
if ( ! this._bindings ) {
|
|
1794
|
+
this.constructor.store[getId(this)] = this;
|
|
1795
|
+
this._bindings = 0;
|
|
1796
|
+
}
|
|
1797
|
+
this._bindings++;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
return can.Observe.prototype.bind.apply( this, arguments );
|
|
1801
|
+
},
|
|
1802
|
+
unbind : function(eventName){
|
|
1803
|
+
if(!ignoreHookup.test(eventName)) {
|
|
1804
|
+
this._bindings--;
|
|
1805
|
+
if(!this._bindings){
|
|
1806
|
+
delete this.constructor.store[getId(this)];
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
return can.Observe.prototype.unbind.apply(this, arguments);
|
|
1810
|
+
},
|
|
1811
|
+
// Change `id`.
|
|
1812
|
+
___set: function( prop, val ) {
|
|
1813
|
+
can.Observe.prototype.___set.call(this,prop, val)
|
|
1814
|
+
// If we add an `id`, move it to the store.
|
|
1815
|
+
if(prop === this.constructor.id && this._bindings){
|
|
1816
|
+
this.constructor.store[getId(this)] = this;
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
});
|
|
1820
|
+
|
|
1821
|
+
|
|
1822
|
+
|
|
1823
|
+
|
|
1824
|
+
can.each({makeFindAll : "models", makeFindOne: "model"}, function(method, name){
|
|
1825
|
+
can.Model[name] = function(oldFind){
|
|
1826
|
+
return function(params, success, error){
|
|
1827
|
+
return pipe( oldFind.call( this, params ),
|
|
1828
|
+
this,
|
|
1829
|
+
method ).then(success,error)
|
|
1830
|
+
}
|
|
1831
|
+
};
|
|
1832
|
+
});
|
|
1833
|
+
|
|
1834
|
+
can.each([
|
|
1835
|
+
"created",
|
|
1836
|
+
"updated",
|
|
1837
|
+
"destroyed"], function( funcName ) {
|
|
1838
|
+
can.Model.prototype[funcName] = function( attrs ) {
|
|
1839
|
+
var stub,
|
|
1840
|
+
constructor = this.constructor;
|
|
1841
|
+
|
|
1842
|
+
// Update attributes if attributes have been passed
|
|
1843
|
+
stub = attrs && typeof attrs == 'object' && this.attr(attrs.attr ? attrs.attr() : attrs);
|
|
1844
|
+
|
|
1845
|
+
// Call event on the instance
|
|
1846
|
+
can.trigger(this,funcName);
|
|
1847
|
+
can.trigger(this,"change",funcName)
|
|
1848
|
+
|
|
1849
|
+
|
|
1850
|
+
// Call event on the instance's Class
|
|
1851
|
+
can.trigger(constructor,funcName, this);
|
|
1852
|
+
};
|
|
1853
|
+
});
|
|
1854
|
+
|
|
1855
|
+
// Model lists are just like `Observe.List` except that when their items are
|
|
1856
|
+
// destroyed, it automatically gets removed from the list.
|
|
1857
|
+
var ML = can.Observe.List('can.Model.List',{
|
|
1858
|
+
setup : function(){
|
|
1859
|
+
can.Observe.List.prototype.setup.apply(this, arguments );
|
|
1860
|
+
// Send destroy events.
|
|
1861
|
+
var self = this;
|
|
1862
|
+
this.bind('change', function(ev, how){
|
|
1863
|
+
if(/\w+\.destroyed/.test(how)){
|
|
1864
|
+
self.splice(self.indexOf(ev.target),1);
|
|
1865
|
+
}
|
|
1866
|
+
})
|
|
1867
|
+
}
|
|
1868
|
+
})
|
|
1869
|
+
|
|
1870
|
+
;
|
|
1871
|
+
|
|
1872
|
+
|
|
1873
|
+
// ## deparam.js
|
|
1874
|
+
// `can.deparam`
|
|
1875
|
+
// _Takes a string of name value pairs and returns a Object literal that represents those params._
|
|
1876
|
+
var digitTest = /^\d+$/,
|
|
1877
|
+
keyBreaker = /([^\[\]]+)|(\[\])/g,
|
|
1878
|
+
paramTest = /([^?#]*)(#.*)?$/,
|
|
1879
|
+
prep = function( str ) {
|
|
1880
|
+
return decodeURIComponent( str.replace(/\+/g, " ") );
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
|
|
1884
|
+
can.extend(can, {
|
|
1885
|
+
deparam: function(params){
|
|
1886
|
+
|
|
1887
|
+
var data = {},
|
|
1888
|
+
pairs, lastPart;
|
|
1889
|
+
|
|
1890
|
+
if ( params && paramTest.test( params )) {
|
|
1891
|
+
|
|
1892
|
+
pairs = params.split('&'),
|
|
1893
|
+
|
|
1894
|
+
can.each( pairs, function( pair ) {
|
|
1895
|
+
|
|
1896
|
+
var parts = pair.split('='),
|
|
1897
|
+
key = prep( parts.shift() ),
|
|
1898
|
+
value = prep( parts.join("=") );
|
|
1899
|
+
|
|
1900
|
+
current = data;
|
|
1901
|
+
parts = key.match(keyBreaker);
|
|
1902
|
+
|
|
1903
|
+
for ( var j = 0, l = parts.length - 1; j < l; j++ ) {
|
|
1904
|
+
if (!current[parts[j]] ) {
|
|
1905
|
+
// If what we are pointing to looks like an `array`
|
|
1906
|
+
current[parts[j]] = digitTest.test(parts[j+1]) || parts[j+1] == "[]" ? [] : {}
|
|
1907
|
+
}
|
|
1908
|
+
current = current[parts[j]];
|
|
1909
|
+
}
|
|
1910
|
+
lastPart = parts.pop()
|
|
1911
|
+
if ( lastPart == "[]" ) {
|
|
1912
|
+
current.push(value)
|
|
1913
|
+
} else {
|
|
1914
|
+
current[lastPart] = value;
|
|
1915
|
+
}
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
return data;
|
|
1919
|
+
}
|
|
1920
|
+
});
|
|
1921
|
+
|
|
1922
|
+
// ## route.js
|
|
1923
|
+
// `can.route`
|
|
1924
|
+
// _Helps manage browser history (and client state) by synchronizing the
|
|
1925
|
+
// `window.location.hash` with a `can.Observe`._
|
|
1926
|
+
//
|
|
1927
|
+
// Helper methods used for matching routes.
|
|
1928
|
+
var
|
|
1929
|
+
// `RegExp` used to match route variables of the type ':name'.
|
|
1930
|
+
// Any word character or a period is matched.
|
|
1931
|
+
matcher = /\:([\w\.]+)/g,
|
|
1932
|
+
// Regular expression for identifying &key=value lists.
|
|
1933
|
+
paramsMatcher = /^(?:&[^=]+=[^&]*)+/,
|
|
1934
|
+
// Converts a JS Object into a list of parameters that can be
|
|
1935
|
+
// inserted into an html element tag.
|
|
1936
|
+
makeProps = function( props ) {
|
|
1937
|
+
var tags = [];
|
|
1938
|
+
can.each(props, function(val, name){
|
|
1939
|
+
tags.push( ( name === 'className' ? 'class' : name )+ '="' +
|
|
1940
|
+
(name === "href" ? val : can.esc(val) ) + '"');
|
|
1941
|
+
});
|
|
1942
|
+
return tags.join(" ");
|
|
1943
|
+
},
|
|
1944
|
+
// Checks if a route matches the data provided. If any route variable
|
|
1945
|
+
// is not present in the data, the route does not match. If all route
|
|
1946
|
+
// variables are present in the data, the number of matches is returned
|
|
1947
|
+
// to allow discerning between general and more specific routes.
|
|
1948
|
+
matchesData = function(route, data) {
|
|
1949
|
+
var count = 0, i = 0, defaults = {};
|
|
1950
|
+
// look at default values, if they match ...
|
|
1951
|
+
for( var name in route.defaults ) {
|
|
1952
|
+
if(route.defaults[name] === data[name]){
|
|
1953
|
+
// mark as matched
|
|
1954
|
+
defaults[name] = 1;
|
|
1955
|
+
count++;
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
for (; i < route.names.length; i++ ) {
|
|
1959
|
+
if (!data.hasOwnProperty(route.names[i]) ) {
|
|
1960
|
+
return -1;
|
|
1961
|
+
}
|
|
1962
|
+
if(!defaults[route.names[i]]){
|
|
1963
|
+
count++;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
return count;
|
|
1969
|
+
},
|
|
1970
|
+
onready = !0,
|
|
1971
|
+
location = window.location,
|
|
1972
|
+
each = can.each,
|
|
1973
|
+
extend = can.extend;
|
|
1974
|
+
|
|
1975
|
+
can.route = function( url, defaults ) {
|
|
1976
|
+
defaults = defaults || {}
|
|
1977
|
+
// Extract the variable names and replace with `RegExp` that will match
|
|
1978
|
+
// an atual URL with values.
|
|
1979
|
+
var names = [],
|
|
1980
|
+
test = url.replace(matcher, function( whole, name, i ) {
|
|
1981
|
+
names.push(name);
|
|
1982
|
+
var next = "\\"+( url.substr(i+whole.length,1) || "&" )
|
|
1983
|
+
// a name without a default value HAS to have a value
|
|
1984
|
+
// a name that has a default value can be empty
|
|
1985
|
+
// The `\\` is for string-escaping giving single `\` for `RegExp` escaping.
|
|
1986
|
+
return "([^" +next+"]"+(defaults[name] ? "*" : "+")+")"
|
|
1987
|
+
});
|
|
1988
|
+
|
|
1989
|
+
// Add route in a form that can be easily figured out.
|
|
1990
|
+
can.route.routes[url] = {
|
|
1991
|
+
// A regular expression that will match the route when variable values
|
|
1992
|
+
// are present; i.e. for `:page/:type` the `RegExp` is `/([\w\.]*)/([\w\.]*)/` which
|
|
1993
|
+
// will match for any value of `:page` and `:type` (word chars or period).
|
|
1994
|
+
test: new RegExp("^" + test+"($|&)"),
|
|
1995
|
+
// The original URL, same as the index for this entry in routes.
|
|
1996
|
+
route: url,
|
|
1997
|
+
// An `array` of all the variable names in this route.
|
|
1998
|
+
names: names,
|
|
1999
|
+
// Default values provided for the variables.
|
|
2000
|
+
defaults: defaults,
|
|
2001
|
+
// The number of parts in the URL separated by `/`.
|
|
2002
|
+
length: url.split('/').length
|
|
2003
|
+
}
|
|
2004
|
+
return can.route;
|
|
2005
|
+
};
|
|
2006
|
+
|
|
2007
|
+
extend(can.route, {
|
|
2008
|
+
param: function( data , _setRoute ) {
|
|
2009
|
+
// Check if the provided data keys match the names in any routes;
|
|
2010
|
+
// Get the one with the most matches.
|
|
2011
|
+
var route,
|
|
2012
|
+
// Need to have at least 1 match.
|
|
2013
|
+
matches = 0,
|
|
2014
|
+
matchCount,
|
|
2015
|
+
routeName = data.route,
|
|
2016
|
+
propCount = 0;
|
|
2017
|
+
|
|
2018
|
+
delete data.route;
|
|
2019
|
+
|
|
2020
|
+
each(data, function(){propCount++});
|
|
2021
|
+
// Otherwise find route.
|
|
2022
|
+
each(can.route.routes, function(temp, name){
|
|
2023
|
+
// best route is the first with all defaults matching
|
|
2024
|
+
|
|
2025
|
+
|
|
2026
|
+
matchCount = matchesData(temp, data);
|
|
2027
|
+
if ( matchCount > matches ) {
|
|
2028
|
+
route = temp;
|
|
2029
|
+
matches = matchCount
|
|
2030
|
+
}
|
|
2031
|
+
if(matchCount >= propCount){
|
|
2032
|
+
return false;
|
|
2033
|
+
}
|
|
2034
|
+
});
|
|
2035
|
+
// If we have a route name in our `can.route` data, and it's
|
|
2036
|
+
// just as good as what currently matches, use that
|
|
2037
|
+
if (can.route.routes[routeName] && matchesData(can.route.routes[routeName], data ) === matches) {
|
|
2038
|
+
route = can.route.routes[routeName];
|
|
2039
|
+
}
|
|
2040
|
+
// If this is match...
|
|
2041
|
+
if ( route ) {
|
|
2042
|
+
var cpy = extend({}, data),
|
|
2043
|
+
// Create the url by replacing the var names with the provided data.
|
|
2044
|
+
// If the default value is found an empty string is inserted.
|
|
2045
|
+
res = route.route.replace(matcher, function( whole, name ) {
|
|
2046
|
+
delete cpy[name];
|
|
2047
|
+
return data[name] === route.defaults[name] ? "" : encodeURIComponent( data[name] );
|
|
2048
|
+
}),
|
|
2049
|
+
after;
|
|
2050
|
+
// Remove matching default values
|
|
2051
|
+
each(route.defaults, function(val,name){
|
|
2052
|
+
if(cpy[name] === val) {
|
|
2053
|
+
delete cpy[name]
|
|
2054
|
+
}
|
|
2055
|
+
})
|
|
2056
|
+
|
|
2057
|
+
// The remaining elements of data are added as
|
|
2058
|
+
// `&` separated parameters to the url.
|
|
2059
|
+
after = can.param(cpy);
|
|
2060
|
+
// if we are paraming for setting the hash
|
|
2061
|
+
// we also want to make sure the route value is updated
|
|
2062
|
+
if(_setRoute){
|
|
2063
|
+
can.route.attr('route',route.route);
|
|
2064
|
+
}
|
|
2065
|
+
return res + (after ? "&" + after : "")
|
|
2066
|
+
}
|
|
2067
|
+
// If no route was found, there is no hash URL, only paramters.
|
|
2068
|
+
return can.isEmptyObject(data) ? "" : "&" + can.param(data);
|
|
2069
|
+
},
|
|
2070
|
+
deparam: function( url ) {
|
|
2071
|
+
// See if the url matches any routes by testing it against the `route.test` `RegExp`.
|
|
2072
|
+
// By comparing the URL length the most specialized route that matches is used.
|
|
2073
|
+
var route = {
|
|
2074
|
+
length: -1
|
|
2075
|
+
};
|
|
2076
|
+
each(can.route.routes, function(temp, name){
|
|
2077
|
+
if ( temp.test.test(url) && temp.length > route.length ) {
|
|
2078
|
+
route = temp;
|
|
2079
|
+
}
|
|
2080
|
+
});
|
|
2081
|
+
// If a route was matched.
|
|
2082
|
+
if ( route.length > -1 ) {
|
|
2083
|
+
var // Since `RegExp` backreferences are used in `route.test` (parens)
|
|
2084
|
+
// the parts will contain the full matched string and each variable (back-referenced) value.
|
|
2085
|
+
parts = url.match(route.test),
|
|
2086
|
+
// Start will contain the full matched string; parts contain the variable values.
|
|
2087
|
+
start = parts.shift(),
|
|
2088
|
+
// The remainder will be the `&key=value` list at the end of the URL.
|
|
2089
|
+
remainder = url.substr(start.length - (parts[parts.length-1] === "&" ? 1 : 0) ),
|
|
2090
|
+
// If there is a remainder and it contains a `&key=value` list deparam it.
|
|
2091
|
+
obj = (remainder && paramsMatcher.test(remainder)) ? can.deparam( remainder.slice(1) ) : {};
|
|
2092
|
+
|
|
2093
|
+
// Add the default values for this route.
|
|
2094
|
+
obj = extend(true, {}, route.defaults, obj);
|
|
2095
|
+
// Overwrite each of the default values in `obj` with those in
|
|
2096
|
+
// parts if that part is not empty.
|
|
2097
|
+
each(parts,function(part, i){
|
|
2098
|
+
if ( part && part !== '&') {
|
|
2099
|
+
obj[route.names[i]] = decodeURIComponent( part );
|
|
2100
|
+
}
|
|
2101
|
+
});
|
|
2102
|
+
obj.route = route.route;
|
|
2103
|
+
return obj;
|
|
2104
|
+
}
|
|
2105
|
+
// If no route was matched, it is parsed as a `&key=value` list.
|
|
2106
|
+
if ( url.charAt(0) !== '&' ) {
|
|
2107
|
+
url = '&' + url;
|
|
2108
|
+
}
|
|
2109
|
+
return paramsMatcher.test(url) ? can.deparam( url.slice(1) ) : {};
|
|
2110
|
+
},
|
|
2111
|
+
data: new can.Observe({}),
|
|
2112
|
+
routes: {},
|
|
2113
|
+
ready: function(val) {
|
|
2114
|
+
if( val === false ) {
|
|
2115
|
+
onready = val;
|
|
2116
|
+
}
|
|
2117
|
+
if( val === true || onready === true ) {
|
|
2118
|
+
setState();
|
|
2119
|
+
}
|
|
2120
|
+
return can.route;
|
|
2121
|
+
},
|
|
2122
|
+
url: function( options, merge ) {
|
|
2123
|
+
if (merge) {
|
|
2124
|
+
options = extend({}, curParams, options)
|
|
2125
|
+
}
|
|
2126
|
+
return "#!" + can.route.param(options)
|
|
2127
|
+
},
|
|
2128
|
+
link: function( name, options, props, merge ) {
|
|
2129
|
+
return "<a " + makeProps(
|
|
2130
|
+
extend({
|
|
2131
|
+
href: can.route.url(options, merge)
|
|
2132
|
+
}, props)) + ">" + name + "</a>";
|
|
2133
|
+
},
|
|
2134
|
+
current: function( options ) {
|
|
2135
|
+
return location.hash == "#!" + can.route.param(options)
|
|
2136
|
+
}
|
|
2137
|
+
});
|
|
2138
|
+
|
|
2139
|
+
|
|
2140
|
+
// The functions in the following list applied to `can.route` (e.g. `can.route.attr('...')`) will
|
|
2141
|
+
// instead act on the `can.route.data` observe.
|
|
2142
|
+
each(['bind','unbind','delegate','undelegate','attr','removeAttr'], function(name){
|
|
2143
|
+
can.route[name] = function(){
|
|
2144
|
+
return can.route.data[name].apply(can.route.data, arguments)
|
|
2145
|
+
}
|
|
2146
|
+
})
|
|
2147
|
+
|
|
2148
|
+
var // A ~~throttled~~ debounced function called multiple times will only fire once the
|
|
2149
|
+
// timer runs down. Each call resets the timer.
|
|
2150
|
+
timer,
|
|
2151
|
+
// Intermediate storage for `can.route.data`.
|
|
2152
|
+
curParams,
|
|
2153
|
+
// Deparameterizes the portion of the hash of interest and assign the
|
|
2154
|
+
// values to the `can.route.data` removing existing values no longer in the hash.
|
|
2155
|
+
// setState is called typically by hashchange which fires asynchronously
|
|
2156
|
+
// So it's possible that someone started changing the data before the
|
|
2157
|
+
// hashchange event fired. For this reason, it will not set the route data
|
|
2158
|
+
// if the data is changing and the hash already matches the hash that was set.
|
|
2159
|
+
setState = function() {
|
|
2160
|
+
var hash = location.href.split(/#!?/)[1] || ""
|
|
2161
|
+
curParams = can.route.deparam( hash );
|
|
2162
|
+
|
|
2163
|
+
|
|
2164
|
+
// if the hash data is currently changing, and
|
|
2165
|
+
// the hash is what we set it to anyway, do NOT change the hash
|
|
2166
|
+
if(!changingData || hash !== lastHash){
|
|
2167
|
+
can.route.attr(curParams, true);
|
|
2168
|
+
}
|
|
2169
|
+
},
|
|
2170
|
+
// The last hash caused by a data change
|
|
2171
|
+
lastHash,
|
|
2172
|
+
// Are data changes pending that haven't yet updated the hash
|
|
2173
|
+
changingData;
|
|
2174
|
+
|
|
2175
|
+
// If the hash changes, update the `can.route.data`.
|
|
2176
|
+
can.bind.call(window,'hashchange', setState);
|
|
2177
|
+
|
|
2178
|
+
// If the `can.route.data` changes, update the hash.
|
|
2179
|
+
// Using `.serialize()` retrieves the raw data contained in the `observable`.
|
|
2180
|
+
// This function is ~~throttled~~ debounced so it only updates once even if multiple values changed.
|
|
2181
|
+
// This might be able to use batchNum and avoid this.
|
|
2182
|
+
can.route.bind("change", function(ev, attr) {
|
|
2183
|
+
// indicate that data is changing
|
|
2184
|
+
changingData = 1;
|
|
2185
|
+
clearTimeout( timer );
|
|
2186
|
+
timer = setTimeout(function() {
|
|
2187
|
+
// indicate that the hash is set to look like the data
|
|
2188
|
+
changingData = 0;
|
|
2189
|
+
var serialized = can.route.data.serialize();
|
|
2190
|
+
location.hash = "#!" + (lastHash = can.route.param(serialized, true))
|
|
2191
|
+
}, 1);
|
|
2192
|
+
});
|
|
2193
|
+
// `onready` event...
|
|
2194
|
+
can.bind.call(document,"ready",can.route.ready);
|
|
2195
|
+
|
|
2196
|
+
(function() {
|
|
2197
|
+
|
|
2198
|
+
|
|
2199
|
+
// ## control.js
|
|
2200
|
+
// `can.Control`
|
|
2201
|
+
// _Controller_
|
|
2202
|
+
|
|
2203
|
+
// Binds an element, returns a function that unbinds.
|
|
2204
|
+
var bind = function( el, ev, callback ) {
|
|
2205
|
+
|
|
2206
|
+
can.bind.call( el, ev, callback )
|
|
2207
|
+
|
|
2208
|
+
return function() {
|
|
2209
|
+
can.unbind.call(el, ev, callback);
|
|
2210
|
+
};
|
|
2211
|
+
},
|
|
2212
|
+
isFunction = can.isFunction,
|
|
2213
|
+
extend = can.extend,
|
|
2214
|
+
each = can.each,
|
|
2215
|
+
slice = [].slice,
|
|
2216
|
+
paramReplacer = /\{([^\}]+)\}/g,
|
|
2217
|
+
special = can.getObject("$.event.special") || {},
|
|
2218
|
+
|
|
2219
|
+
// Binds an element, returns a function that unbinds.
|
|
2220
|
+
delegate = function( el, selector, ev, callback ) {
|
|
2221
|
+
can.delegate.call(el, selector, ev, callback)
|
|
2222
|
+
return function() {
|
|
2223
|
+
can.undelegate.call(el, selector, ev, callback);
|
|
2224
|
+
};
|
|
2225
|
+
},
|
|
2226
|
+
|
|
2227
|
+
// Calls bind or unbind depending if there is a selector.
|
|
2228
|
+
binder = function( el, ev, callback, selector ) {
|
|
2229
|
+
return selector ?
|
|
2230
|
+
delegate( el, can.trim( selector ), ev, callback ) :
|
|
2231
|
+
bind( el, ev, callback );
|
|
2232
|
+
},
|
|
2233
|
+
|
|
2234
|
+
// Moves `this` to the first argument, wraps it with `jQuery` if it's an element
|
|
2235
|
+
shifter = function shifter(context, name) {
|
|
2236
|
+
var method = typeof name == "string" ? context[name] : name;
|
|
2237
|
+
if(!isFunction(method)){
|
|
2238
|
+
method = context[method];
|
|
2239
|
+
}
|
|
2240
|
+
return function() {
|
|
2241
|
+
context.called = name;
|
|
2242
|
+
return method.apply(context, [this.nodeName ? can.$(this) : this].concat( slice.call(arguments, 0)));
|
|
2243
|
+
};
|
|
2244
|
+
},
|
|
2245
|
+
basicProcessor;
|
|
2246
|
+
|
|
2247
|
+
can.Construct("can.Control",
|
|
2248
|
+
{
|
|
2249
|
+
// Setup pre-processes which methods are event listeners.
|
|
2250
|
+
setup: function() {
|
|
2251
|
+
|
|
2252
|
+
// Allow contollers to inherit "defaults" from super-classes as it
|
|
2253
|
+
// done in `can.Construct`
|
|
2254
|
+
can.Construct.setup.apply( this, arguments );
|
|
2255
|
+
|
|
2256
|
+
// If you didn't provide a name, or are `control`, don't do anything.
|
|
2257
|
+
if ( this !== can.Control ) {
|
|
2258
|
+
|
|
2259
|
+
// Cache the underscored names.
|
|
2260
|
+
var control = this,
|
|
2261
|
+
funcName;
|
|
2262
|
+
|
|
2263
|
+
// Calculate and cache actions.
|
|
2264
|
+
control.actions = {};
|
|
2265
|
+
for ( funcName in control.prototype ) {
|
|
2266
|
+
if ( control._isAction(funcName) ) {
|
|
2267
|
+
control.actions[funcName] = control._action(funcName);
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
},
|
|
2272
|
+
// Return `true` if is an action.
|
|
2273
|
+
_isAction: function( methodName ) {
|
|
2274
|
+
|
|
2275
|
+
var val = this.prototype[methodName],
|
|
2276
|
+
type = typeof val;
|
|
2277
|
+
// if not the constructor
|
|
2278
|
+
return (methodName !== 'constructor') &&
|
|
2279
|
+
// and is a function or links to a function
|
|
2280
|
+
( type == "function" || (type == "string" && isFunction(this.prototype[val] ) ) ) &&
|
|
2281
|
+
// and is in special, a processor, or has a funny character
|
|
2282
|
+
!! ( special[methodName] || processors[methodName] || /[^\w]/.test(methodName) );
|
|
2283
|
+
},
|
|
2284
|
+
// Takes a method name and the options passed to a control
|
|
2285
|
+
// and tries to return the data necessary to pass to a processor
|
|
2286
|
+
// (something that binds things).
|
|
2287
|
+
_action: function( methodName, options ) {
|
|
2288
|
+
|
|
2289
|
+
// If we don't have options (a `control` instance), we'll run this
|
|
2290
|
+
// later.
|
|
2291
|
+
paramReplacer.lastIndex = 0;
|
|
2292
|
+
if ( options || ! paramReplacer.test( methodName )) {
|
|
2293
|
+
// If we have options, run sub to replace templates `{}` with a
|
|
2294
|
+
// value from the options or the window
|
|
2295
|
+
var convertedName = options ? can.sub(methodName, [options, window]) : methodName,
|
|
2296
|
+
|
|
2297
|
+
// If a `{}` resolves to an object, `convertedName` will be
|
|
2298
|
+
// an array
|
|
2299
|
+
arr = can.isArray(convertedName),
|
|
2300
|
+
|
|
2301
|
+
// Get the parts of the function
|
|
2302
|
+
// `[convertedName, delegatePart, eventPart]`
|
|
2303
|
+
// `/^(?:(.*?)\s)?([\w\.\:>]+)$/` - Breaker `RegExp`.
|
|
2304
|
+
parts = (arr ? convertedName[1] : convertedName).match(/^(?:(.*?)\s)?([\w\.\:>]+)$/);
|
|
2305
|
+
|
|
2306
|
+
var event = parts[2],
|
|
2307
|
+
processor = processors[event] || basicProcessor;
|
|
2308
|
+
return {
|
|
2309
|
+
processor: processor,
|
|
2310
|
+
parts: parts,
|
|
2311
|
+
delegate : arr ? convertedName[0] : undefined
|
|
2312
|
+
};
|
|
2313
|
+
}
|
|
2314
|
+
},
|
|
2315
|
+
// An object of `{eventName : function}` pairs that Control uses to
|
|
2316
|
+
// hook up events auto-magically.
|
|
2317
|
+
processors: {},
|
|
2318
|
+
// A object of name-value pairs that act as default values for a
|
|
2319
|
+
// control instance
|
|
2320
|
+
defaults: {}
|
|
2321
|
+
},
|
|
2322
|
+
{
|
|
2323
|
+
// Sets `this.element`, saves the control in `data, binds event
|
|
2324
|
+
// handlers.
|
|
2325
|
+
setup: function( element, options ) {
|
|
2326
|
+
|
|
2327
|
+
var cls = this.constructor,
|
|
2328
|
+
pluginname = cls.pluginName || cls._fullName,
|
|
2329
|
+
arr;
|
|
2330
|
+
|
|
2331
|
+
// Want the raw element here.
|
|
2332
|
+
this.element = can.$(element)
|
|
2333
|
+
|
|
2334
|
+
if ( pluginname && pluginname !== 'can_control') {
|
|
2335
|
+
// Set element and `className` on element.
|
|
2336
|
+
this.element.addClass(pluginname);
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
(arr = can.data(this.element,"controls")) || can.data(this.element,"controls",arr = []);
|
|
2340
|
+
arr.push(this);
|
|
2341
|
+
|
|
2342
|
+
// Option merging.
|
|
2343
|
+
this.options = extend({}, cls.defaults, options);
|
|
2344
|
+
|
|
2345
|
+
// Bind all event handlers.
|
|
2346
|
+
this.on();
|
|
2347
|
+
|
|
2348
|
+
// Get's passed into `init`.
|
|
2349
|
+
return [this.element, this.options];
|
|
2350
|
+
},
|
|
2351
|
+
on: function( el, selector, eventName, func ) {
|
|
2352
|
+
|
|
2353
|
+
if ( ! el ) {
|
|
2354
|
+
|
|
2355
|
+
// Adds bindings.
|
|
2356
|
+
this.off();
|
|
2357
|
+
|
|
2358
|
+
// Go through the cached list of actions and use the processor
|
|
2359
|
+
// to bind
|
|
2360
|
+
var cls = this.constructor,
|
|
2361
|
+
bindings = this._bindings,
|
|
2362
|
+
actions = cls.actions,
|
|
2363
|
+
element = this.element,
|
|
2364
|
+
destroyCB = shifter(this,"destroy"),
|
|
2365
|
+
funcName, ready;
|
|
2366
|
+
|
|
2367
|
+
for ( funcName in actions ) {
|
|
2368
|
+
if ( actions.hasOwnProperty( funcName )) {
|
|
2369
|
+
ready = actions[funcName] || cls._action(funcName, this.options);
|
|
2370
|
+
bindings.push(
|
|
2371
|
+
ready.processor(ready.delegate || element,
|
|
2372
|
+
ready.parts[2],
|
|
2373
|
+
ready.parts[1],
|
|
2374
|
+
funcName,
|
|
2375
|
+
this));
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
|
|
2380
|
+
// Setup to be destroyed...
|
|
2381
|
+
// don't bind because we don't want to remove it.
|
|
2382
|
+
can.bind.call(element,"destroyed", destroyCB);
|
|
2383
|
+
bindings.push(function( el ) {
|
|
2384
|
+
can.unbind.call(el,"destroyed", destroyCB);
|
|
2385
|
+
});
|
|
2386
|
+
return bindings.length;
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
if ( typeof el == 'string' ) {
|
|
2390
|
+
func = eventName;
|
|
2391
|
+
eventName = selector;
|
|
2392
|
+
selector = el;
|
|
2393
|
+
el = this.element;
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
if ( typeof func == 'string' ) {
|
|
2397
|
+
func = shifter(this,func);
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
this._bindings.push( binder( el, eventName, func, selector ));
|
|
2401
|
+
|
|
2402
|
+
return this._bindings.length;
|
|
2403
|
+
},
|
|
2404
|
+
// Unbinds all event handlers on the controller.
|
|
2405
|
+
off : function(){
|
|
2406
|
+
var el = this.element[0]
|
|
2407
|
+
each(this._bindings || [], function( value ) {
|
|
2408
|
+
value(el);
|
|
2409
|
+
});
|
|
2410
|
+
// Adds bindings.
|
|
2411
|
+
this._bindings = [];
|
|
2412
|
+
},
|
|
2413
|
+
// Prepares a `control` for garbage collection
|
|
2414
|
+
destroy: function() {
|
|
2415
|
+
var Class = this.constructor,
|
|
2416
|
+
pluginName = Class.pluginName || Class._fullName,
|
|
2417
|
+
controls;
|
|
2418
|
+
|
|
2419
|
+
// Unbind bindings.
|
|
2420
|
+
this.off();
|
|
2421
|
+
|
|
2422
|
+
if(pluginName && pluginName !== 'can_control'){
|
|
2423
|
+
// Remove the `className`.
|
|
2424
|
+
this.element.removeClass(pluginName);
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
// Remove from `data`.
|
|
2428
|
+
controls = can.data(this.element,"controls");
|
|
2429
|
+
controls.splice(can.inArray(this, controls),1);
|
|
2430
|
+
|
|
2431
|
+
can.trigger( this, "destroyed"); // In case we want to know if the `control` is removed.
|
|
2432
|
+
|
|
2433
|
+
this.element = null;
|
|
2434
|
+
}
|
|
2435
|
+
});
|
|
2436
|
+
|
|
2437
|
+
var processors = can.Control.processors,
|
|
2438
|
+
|
|
2439
|
+
// Processors do the binding.
|
|
2440
|
+
// They return a function that unbinds when called.
|
|
2441
|
+
//
|
|
2442
|
+
// The basic processor that binds events.
|
|
2443
|
+
basicProcessor = function( el, event, selector, methodName, control ) {
|
|
2444
|
+
return binder( el, event, shifter(control, methodName), selector);
|
|
2445
|
+
};
|
|
2446
|
+
|
|
2447
|
+
// Set common events to be processed as a `basicProcessor`
|
|
2448
|
+
each(["change", "click", "contextmenu", "dblclick", "keydown", "keyup",
|
|
2449
|
+
"keypress", "mousedown", "mousemove", "mouseout", "mouseover",
|
|
2450
|
+
"mouseup", "reset", "resize", "scroll", "select", "submit", "focusin",
|
|
2451
|
+
"focusout", "mouseenter", "mouseleave"], function( v ) {
|
|
2452
|
+
processors[v] = basicProcessor;
|
|
2453
|
+
});
|
|
2454
|
+
|
|
2455
|
+
}());
|
|
2456
|
+
|
|
2457
|
+
|
|
2458
|
+
// ## control/route.js
|
|
2459
|
+
// _Controller route integration._
|
|
2460
|
+
|
|
2461
|
+
can.Control.processors.route = function( el, event, selector, funcName, controller ) {
|
|
2462
|
+
can.route( selector || "" )
|
|
2463
|
+
var batchNum,
|
|
2464
|
+
check = function( ev, attr, how ) {
|
|
2465
|
+
if ( can.route.attr('route') === ( selector || "" ) &&
|
|
2466
|
+
( ev.batchNum === undefined || ev.batchNum !== batchNum ) ) {
|
|
2467
|
+
|
|
2468
|
+
batchNum = ev.batchNum;
|
|
2469
|
+
|
|
2470
|
+
var d = can.route.attr();
|
|
2471
|
+
delete d.route;
|
|
2472
|
+
if(can.isFunction(controller[funcName])){
|
|
2473
|
+
controller[funcName]( d )
|
|
2474
|
+
}else {
|
|
2475
|
+
controller[controller[funcName]](d)
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
can.route.bind( 'change', check );
|
|
2481
|
+
return function() {
|
|
2482
|
+
can.route.unbind( 'change', check )
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
;
|
|
2486
|
+
|
|
2487
|
+
// ## view.js
|
|
2488
|
+
// `can.view`
|
|
2489
|
+
// _Templating abstraction._
|
|
2490
|
+
|
|
2491
|
+
var isFunction = can.isFunction,
|
|
2492
|
+
makeArray = can.makeArray,
|
|
2493
|
+
// Used for hookup `id`s.
|
|
2494
|
+
hookupId = 1,
|
|
2495
|
+
$view = can.view = function(view, data, helpers, callback){
|
|
2496
|
+
// Get the result.
|
|
2497
|
+
var result = $view.render(view, data, helpers, callback);
|
|
2498
|
+
if(can.isDeferred(result)){
|
|
2499
|
+
return result.pipe(function(result){
|
|
2500
|
+
return $view.frag(result);
|
|
2501
|
+
})
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
// Convert it into a dom frag.
|
|
2505
|
+
return $view.frag(result);
|
|
2506
|
+
};
|
|
2507
|
+
|
|
2508
|
+
can.extend( $view, {
|
|
2509
|
+
// creates a frag and hooks it up all at once
|
|
2510
|
+
frag: function(result, parentNode ){
|
|
2511
|
+
return $view.hookup( $view.fragment(result), parentNode );
|
|
2512
|
+
},
|
|
2513
|
+
// simply creates a frag
|
|
2514
|
+
// this is used internally to create a frag
|
|
2515
|
+
// insert it
|
|
2516
|
+
// then hook it up
|
|
2517
|
+
fragment: function(result){
|
|
2518
|
+
var frag = can.buildFragment(result,document.body);
|
|
2519
|
+
// If we have an empty frag...
|
|
2520
|
+
if(!frag.childNodes.length) {
|
|
2521
|
+
frag.appendChild(document.createTextNode(''))
|
|
2522
|
+
}
|
|
2523
|
+
return frag;
|
|
2524
|
+
},
|
|
2525
|
+
// Convert a path like string into something that's ok for an `element` ID.
|
|
2526
|
+
toId : function( src ) {
|
|
2527
|
+
return can.map(src.toString().split(/\/|\./g), function( part ) {
|
|
2528
|
+
// Dont include empty strings in toId functions
|
|
2529
|
+
if ( part ) {
|
|
2530
|
+
return part;
|
|
2531
|
+
}
|
|
2532
|
+
}).join("_");
|
|
2533
|
+
},
|
|
2534
|
+
hookup: function(fragment, parentNode ){
|
|
2535
|
+
var hookupEls = [],
|
|
2536
|
+
id,
|
|
2537
|
+
func,
|
|
2538
|
+
el,
|
|
2539
|
+
i=0;
|
|
2540
|
+
|
|
2541
|
+
// Get all `childNodes`.
|
|
2542
|
+
can.each(fragment.childNodes ? can.makeArray(fragment.childNodes) : fragment, function(node){
|
|
2543
|
+
if(node.nodeType === 1){
|
|
2544
|
+
hookupEls.push(node)
|
|
2545
|
+
hookupEls.push.apply(hookupEls, can.makeArray( node.getElementsByTagName('*')))
|
|
2546
|
+
}
|
|
2547
|
+
});
|
|
2548
|
+
// Filter by `data-view-id` attribute.
|
|
2549
|
+
for (; el = hookupEls[i++]; ) {
|
|
2550
|
+
|
|
2551
|
+
if ( el.getAttribute && (id = el.getAttribute('data-view-id')) && (func = $view.hookups[id]) ) {
|
|
2552
|
+
func(el, parentNode, id);
|
|
2553
|
+
delete $view.hookups[id];
|
|
2554
|
+
el.removeAttribute('data-view-id');
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
return fragment;
|
|
2558
|
+
},
|
|
2559
|
+
hookups: {},
|
|
2560
|
+
hook: function( cb ) {
|
|
2561
|
+
$view.hookups[++hookupId] = cb;
|
|
2562
|
+
return " data-view-id='"+hookupId+"'";
|
|
2563
|
+
},
|
|
2564
|
+
cached: {},
|
|
2565
|
+
cache: true,
|
|
2566
|
+
register: function( info ) {
|
|
2567
|
+
this.types["." + info.suffix] = info;
|
|
2568
|
+
},
|
|
2569
|
+
types: {},
|
|
2570
|
+
ext: ".ejs",
|
|
2571
|
+
registerScript: function() {},
|
|
2572
|
+
preload: function( ) {},
|
|
2573
|
+
render: function( view, data, helpers, callback ) {
|
|
2574
|
+
// If helpers is a `function`, it is actually a callback.
|
|
2575
|
+
if ( isFunction( helpers )) {
|
|
2576
|
+
callback = helpers;
|
|
2577
|
+
helpers = undefined;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
// See if we got passed any deferreds.
|
|
2581
|
+
var deferreds = getDeferreds(data);
|
|
2582
|
+
|
|
2583
|
+
|
|
2584
|
+
if ( deferreds.length ) { // Does data contain any deferreds?
|
|
2585
|
+
// The deferred that resolves into the rendered content...
|
|
2586
|
+
var deferred = new can.Deferred();
|
|
2587
|
+
|
|
2588
|
+
// Add the view request to the list of deferreds.
|
|
2589
|
+
deferreds.push(get(view, true))
|
|
2590
|
+
|
|
2591
|
+
// Wait for the view and all deferreds to finish...
|
|
2592
|
+
can.when.apply(can, deferreds).then(function( resolved ) {
|
|
2593
|
+
// Get all the resolved deferreds.
|
|
2594
|
+
var objs = makeArray(arguments),
|
|
2595
|
+
// Renderer is the last index of the data.
|
|
2596
|
+
renderer = objs.pop(),
|
|
2597
|
+
// The result of the template rendering with data.
|
|
2598
|
+
result;
|
|
2599
|
+
|
|
2600
|
+
// Make data look like the resolved deferreds.
|
|
2601
|
+
if ( can.isDeferred(data) ) {
|
|
2602
|
+
data = usefulPart(resolved);
|
|
2603
|
+
}
|
|
2604
|
+
else {
|
|
2605
|
+
// Go through each prop in data again and
|
|
2606
|
+
// replace the defferreds with what they resolved to.
|
|
2607
|
+
for ( var prop in data ) {
|
|
2608
|
+
if ( can.isDeferred(data[prop]) ) {
|
|
2609
|
+
data[prop] = usefulPart(objs.shift());
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
// Get the rendered result.
|
|
2614
|
+
result = renderer(data, helpers);
|
|
2615
|
+
|
|
2616
|
+
// Resolve with the rendered view.
|
|
2617
|
+
deferred.resolve(result);
|
|
2618
|
+
// If there's a `callback`, call it back with the result.
|
|
2619
|
+
callback && callback(result);
|
|
2620
|
+
});
|
|
2621
|
+
// Return the deferred...
|
|
2622
|
+
return deferred;
|
|
2623
|
+
}
|
|
2624
|
+
else {
|
|
2625
|
+
// No deferreds! Render this bad boy.
|
|
2626
|
+
var response,
|
|
2627
|
+
// If there's a `callback` function
|
|
2628
|
+
async = isFunction( callback ),
|
|
2629
|
+
// Get the `view` type
|
|
2630
|
+
deferred = get(view, async);
|
|
2631
|
+
|
|
2632
|
+
// If we are `async`...
|
|
2633
|
+
if ( async ) {
|
|
2634
|
+
// Return the deferred
|
|
2635
|
+
response = deferred;
|
|
2636
|
+
// And fire callback with the rendered result.
|
|
2637
|
+
deferred.then(function( renderer ) {
|
|
2638
|
+
callback(renderer(data, helpers))
|
|
2639
|
+
})
|
|
2640
|
+
} else {
|
|
2641
|
+
// Otherwise, the deferred is complete, so
|
|
2642
|
+
// set response to the result of the rendering.
|
|
2643
|
+
deferred.then(function( renderer ) {
|
|
2644
|
+
response = renderer(data, helpers);
|
|
2645
|
+
});
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
return response;
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
});
|
|
2652
|
+
// Returns `true` if something looks like a deferred.
|
|
2653
|
+
can.isDeferred = function( obj ) {
|
|
2654
|
+
return obj && isFunction(obj.then) && isFunction(obj.pipe) // Check if `obj` is a `can.Deferred`.
|
|
2655
|
+
}
|
|
2656
|
+
// Makes sure there's a template, if not, have `steal` provide a warning.
|
|
2657
|
+
var checkText = function( text, url ) {
|
|
2658
|
+
if ( ! text.length ) {
|
|
2659
|
+
//@steal-remove-start
|
|
2660
|
+
//@steal-remove-end
|
|
2661
|
+
throw "can.view: No template or empty template:" + url;
|
|
2662
|
+
}
|
|
2663
|
+
},
|
|
2664
|
+
// `Returns a `view` renderer deferred.
|
|
2665
|
+
// `url` - The url to the template.
|
|
2666
|
+
// `async` - If the ajax request should be asynchronous.
|
|
2667
|
+
// Returns a deferred.
|
|
2668
|
+
get = function( url, async ) {
|
|
2669
|
+
|
|
2670
|
+
|
|
2671
|
+
var suffix = url.match(/\.[\w\d]+$/),
|
|
2672
|
+
type,
|
|
2673
|
+
// If we are reading a script element for the content of the template,
|
|
2674
|
+
// `el` will be set to that script element.
|
|
2675
|
+
el,
|
|
2676
|
+
// A unique identifier for the view (used for caching).
|
|
2677
|
+
// This is typically derived from the element id or
|
|
2678
|
+
// the url for the template.
|
|
2679
|
+
id,
|
|
2680
|
+
// The ajax request used to retrieve the template content.
|
|
2681
|
+
jqXHR,
|
|
2682
|
+
// Used to generate the response.
|
|
2683
|
+
response = function( text ) {
|
|
2684
|
+
// Get the renderer function.
|
|
2685
|
+
var func = type.renderer(id, text),
|
|
2686
|
+
d = new can.Deferred();
|
|
2687
|
+
d.resolve(func)
|
|
2688
|
+
// Cache if we are caching.
|
|
2689
|
+
if ( $view.cache ) {
|
|
2690
|
+
$view.cached[id] = d;
|
|
2691
|
+
}
|
|
2692
|
+
// Return the objects for the response's `dataTypes`
|
|
2693
|
+
// (in this case view).
|
|
2694
|
+
return d;
|
|
2695
|
+
};
|
|
2696
|
+
|
|
2697
|
+
//If the url has a #, we assume we want to use an inline template
|
|
2698
|
+
//from a script element and not current page's HTML
|
|
2699
|
+
if( url.match(/^#/) ) {
|
|
2700
|
+
url = url.substr(1);
|
|
2701
|
+
}
|
|
2702
|
+
// If we have an inline template, derive the suffix from the `text/???` part.
|
|
2703
|
+
// This only supports `<script>` tags.
|
|
2704
|
+
if ( el = document.getElementById(url) ) {
|
|
2705
|
+
suffix = "."+el.type.match(/\/(x\-)?(.+)/)[2];
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
// If there is no suffix, add one.
|
|
2709
|
+
if (!suffix && !$view.cached[url] ) {
|
|
2710
|
+
url += ( suffix = $view.ext );
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
if ( can.isArray( suffix )) {
|
|
2714
|
+
suffix = suffix[0]
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
// Convert to a unique and valid id.
|
|
2718
|
+
id = can.view.toId(url);
|
|
2719
|
+
|
|
2720
|
+
// If an absolute path, use `steal` to get it.
|
|
2721
|
+
// You should only be using `//` if you are using `steal`.
|
|
2722
|
+
if ( url.match(/^\/\//) ) {
|
|
2723
|
+
var sub = url.substr(2);
|
|
2724
|
+
url = ! window.steal ?
|
|
2725
|
+
"/" + sub :
|
|
2726
|
+
steal.root.mapJoin(sub);
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
// Set the template engine type.
|
|
2730
|
+
type = $view.types[suffix];
|
|
2731
|
+
|
|
2732
|
+
// If it is cached,
|
|
2733
|
+
if ( $view.cached[id] ) {
|
|
2734
|
+
// Return the cached deferred renderer.
|
|
2735
|
+
return $view.cached[id];
|
|
2736
|
+
|
|
2737
|
+
// Otherwise if we are getting this from a `<script>` element.
|
|
2738
|
+
} else if ( el ) {
|
|
2739
|
+
// Resolve immediately with the element's `innerHTML`.
|
|
2740
|
+
return response(el.innerHTML);
|
|
2741
|
+
} else {
|
|
2742
|
+
// Make an ajax request for text.
|
|
2743
|
+
var d = new can.Deferred();
|
|
2744
|
+
can.ajax({
|
|
2745
|
+
async: async,
|
|
2746
|
+
url: url,
|
|
2747
|
+
dataType: "text",
|
|
2748
|
+
error: function(jqXHR) {
|
|
2749
|
+
checkText("", url);
|
|
2750
|
+
d.reject(jqXHR);
|
|
2751
|
+
},
|
|
2752
|
+
success: function( text ) {
|
|
2753
|
+
// Make sure we got some text back.
|
|
2754
|
+
checkText(text, url);
|
|
2755
|
+
d.resolve(type.renderer(id, text))
|
|
2756
|
+
// Cache if if we are caching.
|
|
2757
|
+
if ( $view.cache ) {
|
|
2758
|
+
$view.cached[id] = d;
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2761
|
+
}
|
|
2762
|
+
});
|
|
2763
|
+
return d;
|
|
2764
|
+
}
|
|
2765
|
+
},
|
|
2766
|
+
// Gets an `array` of deferreds from an `object`.
|
|
2767
|
+
// This only goes one level deep.
|
|
2768
|
+
getDeferreds = function( data ) {
|
|
2769
|
+
var deferreds = [];
|
|
2770
|
+
|
|
2771
|
+
// pull out deferreds
|
|
2772
|
+
if ( can.isDeferred(data) ) {
|
|
2773
|
+
return [data]
|
|
2774
|
+
} else {
|
|
2775
|
+
for ( var prop in data ) {
|
|
2776
|
+
if ( can.isDeferred(data[prop]) ) {
|
|
2777
|
+
deferreds.push(data[prop]);
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
return deferreds;
|
|
2782
|
+
},
|
|
2783
|
+
// Gets the useful part of a resolved deferred.
|
|
2784
|
+
// This is for `model`s and `can.ajax` that resolve to an `array`.
|
|
2785
|
+
usefulPart = function( resolved ) {
|
|
2786
|
+
return can.isArray(resolved) && resolved[1] === 'success' ? resolved[0] : resolved
|
|
2787
|
+
};
|
|
2788
|
+
|
|
2789
|
+
|
|
2790
|
+
if ( window.steal ) {
|
|
2791
|
+
steal.type("view js", function( options, success, error ) {
|
|
2792
|
+
var type = can.view.types["." + options.type],
|
|
2793
|
+
id = can.view.toId(options.rootSrc);
|
|
2794
|
+
|
|
2795
|
+
options.text = "steal('" + (type.plugin || "can/view/" + options.type) + "').then(function($){" + "can.view.preload('" + id + "'," + options.text + ");\n})";
|
|
2796
|
+
success();
|
|
2797
|
+
})
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
//!steal-pluginify-remove-start
|
|
2801
|
+
can.extend(can.view, {
|
|
2802
|
+
register: function( info ) {
|
|
2803
|
+
this.types["." + info.suffix] = info;
|
|
2804
|
+
|
|
2805
|
+
if ( window.steal ) {
|
|
2806
|
+
steal.type(info.suffix + " view js", function( options, success, error ) {
|
|
2807
|
+
var type = can.view.types["." + options.type],
|
|
2808
|
+
id = can.view.toId(options.rootSrc+'');
|
|
2809
|
+
|
|
2810
|
+
options.text = type.script(id, options.text)
|
|
2811
|
+
success();
|
|
2812
|
+
})
|
|
2813
|
+
}
|
|
2814
|
+
can.view[info.suffix] = function(id, text){
|
|
2815
|
+
$view.preload(id, info.renderer(id, text) )
|
|
2816
|
+
}
|
|
2817
|
+
},
|
|
2818
|
+
registerScript: function( type, id, src ) {
|
|
2819
|
+
return "can.view.preload('" + id + "'," + $view.types["." + type].script(id, src) + ");";
|
|
2820
|
+
},
|
|
2821
|
+
preload: function( id, renderer ) {
|
|
2822
|
+
can.view.cached[id] = new can.Deferred().resolve(function( data, helpers ) {
|
|
2823
|
+
return renderer.call(data, data, helpers);
|
|
2824
|
+
});
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
});
|
|
2828
|
+
//!steal-pluginify-remove-end
|
|
2829
|
+
|
|
2830
|
+
|
|
2831
|
+
// returns the
|
|
2832
|
+
// - observes and attr methods are called by func
|
|
2833
|
+
// - the value returned by func
|
|
2834
|
+
// ex: `{value: 100, observed: [{obs: o, attr: "completed"}]}`
|
|
2835
|
+
var getValueAndObserved = function(func, self){
|
|
2836
|
+
|
|
2837
|
+
var oldReading;
|
|
2838
|
+
if (can.Observe) {
|
|
2839
|
+
// Set a callback on can.Observe to know
|
|
2840
|
+
// when an attr is read.
|
|
2841
|
+
// Keep a reference to the old reader
|
|
2842
|
+
// if there is one. This is used
|
|
2843
|
+
// for nested live binding.
|
|
2844
|
+
oldReading = can.Observe.__reading;
|
|
2845
|
+
can.Observe.__reading = function(obj, attr){
|
|
2846
|
+
// Add the observe and attr that was read
|
|
2847
|
+
// to `observed`
|
|
2848
|
+
observed.push({
|
|
2849
|
+
obj: obj,
|
|
2850
|
+
attr: attr
|
|
2851
|
+
});
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
var observed = [],
|
|
2856
|
+
// Call the "wrapping" function to get the value. `observed`
|
|
2857
|
+
// will have the observe/attribute pairs that were read.
|
|
2858
|
+
value = func.call(self);
|
|
2859
|
+
|
|
2860
|
+
// Set back so we are no longer reading.
|
|
2861
|
+
if(can.Observe){
|
|
2862
|
+
can.Observe.__reading = oldReading;
|
|
2863
|
+
}
|
|
2864
|
+
return {
|
|
2865
|
+
value : value,
|
|
2866
|
+
observed : observed
|
|
2867
|
+
}
|
|
2868
|
+
},
|
|
2869
|
+
// Calls `callback(newVal, oldVal)` everytime an observed property
|
|
2870
|
+
// called within `getterSetter` is changed and creates a new result of `getterSetter`.
|
|
2871
|
+
// Also returns an object that can teardown all event handlers.
|
|
2872
|
+
computeBinder = function(getterSetter, context, callback){
|
|
2873
|
+
// track what we are observing
|
|
2874
|
+
var observing = {},
|
|
2875
|
+
// a flag indicating if this observe/attr pair is already bound
|
|
2876
|
+
matched = true,
|
|
2877
|
+
// the data to return
|
|
2878
|
+
data = {
|
|
2879
|
+
// we will maintain the value while live-binding is taking place
|
|
2880
|
+
value : undefined,
|
|
2881
|
+
// a teardown method that stops listening
|
|
2882
|
+
teardown: function(){
|
|
2883
|
+
for ( var name in observing ) {
|
|
2884
|
+
var ob = observing[name];
|
|
2885
|
+
ob.observe.obj.unbind(ob.observe.attr, onchanged);
|
|
2886
|
+
delete observing[name];
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
};
|
|
2890
|
+
|
|
2891
|
+
// when a property value is cahnged
|
|
2892
|
+
var onchanged = function(){
|
|
2893
|
+
// store the old value
|
|
2894
|
+
var oldValue = data.value,
|
|
2895
|
+
// get the new value
|
|
2896
|
+
newvalue = getValueAndBind();
|
|
2897
|
+
// update the value reference (in case someone reads)
|
|
2898
|
+
data.value = newvalue
|
|
2899
|
+
// if a change happened
|
|
2900
|
+
if(newvalue !== oldValue){
|
|
2901
|
+
callback(newvalue, oldValue);
|
|
2902
|
+
};
|
|
2903
|
+
};
|
|
2904
|
+
|
|
2905
|
+
// gets the value returned by `getterSetter` and also binds to any attributes
|
|
2906
|
+
// read by the call
|
|
2907
|
+
var getValueAndBind = function(){
|
|
2908
|
+
var info = getValueAndObserved( getterSetter, context ),
|
|
2909
|
+
newObserveSet = info.observed;
|
|
2910
|
+
|
|
2911
|
+
var value = info.value;
|
|
2912
|
+
matched = !matched;
|
|
2913
|
+
|
|
2914
|
+
// go through every attribute read by this observe
|
|
2915
|
+
can.each(newObserveSet, function(ob){
|
|
2916
|
+
// if the observe/attribute pair is being observed
|
|
2917
|
+
if(observing[ob.obj._namespace+"|"+ob.attr]){
|
|
2918
|
+
// mark at as observed
|
|
2919
|
+
observing[ob.obj._namespace+"|"+ob.attr].matched = matched;
|
|
2920
|
+
} else {
|
|
2921
|
+
// otherwise, set the observe/attribute on oldObserved, marking it as being observed
|
|
2922
|
+
observing[ob.obj._namespace+"|"+ob.attr] = {
|
|
2923
|
+
matched: matched,
|
|
2924
|
+
observe: ob
|
|
2925
|
+
};
|
|
2926
|
+
ob.obj.bind(ob.attr, onchanged)
|
|
2927
|
+
}
|
|
2928
|
+
});
|
|
2929
|
+
|
|
2930
|
+
// Iterate through oldObserved, looking for observe/attributes
|
|
2931
|
+
// that are no longer being bound and unbind them
|
|
2932
|
+
for ( var name in observing ) {
|
|
2933
|
+
var ob = observing[name];
|
|
2934
|
+
if(ob.matched !== matched){
|
|
2935
|
+
ob.observe.obj.unbind(ob.observe.attr, onchanged);
|
|
2936
|
+
delete observing[name];
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
return value;
|
|
2940
|
+
}
|
|
2941
|
+
// set the initial value
|
|
2942
|
+
data.value = getValueAndBind();
|
|
2943
|
+
data.isListening = ! can.isEmptyObject(observing);
|
|
2944
|
+
return data;
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
// if no one is listening ... we can not calculate every time
|
|
2948
|
+
can.compute = function(getterSetter, context){
|
|
2949
|
+
if(getterSetter.isComputed){
|
|
2950
|
+
return getterSetter;
|
|
2951
|
+
}
|
|
2952
|
+
// get the value right away
|
|
2953
|
+
// TODO: eventually we can defer this until a bind or a read
|
|
2954
|
+
var computedData,
|
|
2955
|
+
bindings = 0,
|
|
2956
|
+
computed,
|
|
2957
|
+
canbind = true;
|
|
2958
|
+
if(typeof getterSetter === "function"){
|
|
2959
|
+
computed = function(value){
|
|
2960
|
+
if(value === undefined){
|
|
2961
|
+
// we are reading
|
|
2962
|
+
if(computedData){
|
|
2963
|
+
return computedData.value;
|
|
2964
|
+
} else {
|
|
2965
|
+
return getterSetter.call(context || this)
|
|
2966
|
+
}
|
|
2967
|
+
} else {
|
|
2968
|
+
return getterSetter.apply(context || this, arguments)
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
} else {
|
|
2973
|
+
// we just gave it a value
|
|
2974
|
+
computed = function(val){
|
|
2975
|
+
if(val === undefined){
|
|
2976
|
+
return getterSetter;
|
|
2977
|
+
} else {
|
|
2978
|
+
var old = getterSetter;
|
|
2979
|
+
getterSetter = val;
|
|
2980
|
+
if( old !== val){
|
|
2981
|
+
can.trigger(computed, "change",[val, old]);
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
return val;
|
|
2985
|
+
}
|
|
2986
|
+
|
|
2987
|
+
}
|
|
2988
|
+
canbind = false;
|
|
2989
|
+
}
|
|
2990
|
+
computed.isComputed = true;
|
|
2991
|
+
|
|
2992
|
+
|
|
2993
|
+
computed.bind = function(ev, handler){
|
|
2994
|
+
can.addEvent.apply(computed, arguments);
|
|
2995
|
+
if( bindings === 0 && canbind){
|
|
2996
|
+
// setup live-binding
|
|
2997
|
+
computedData = computeBinder(getterSetter, context || this, function(newValue, oldValue){
|
|
2998
|
+
can.trigger(computed, "change",[newValue, oldValue])
|
|
2999
|
+
});
|
|
3000
|
+
}
|
|
3001
|
+
bindings++;
|
|
3002
|
+
}
|
|
3003
|
+
computed.unbind = function(ev, handler){
|
|
3004
|
+
can.removeEvent.apply(computed, arguments);
|
|
3005
|
+
bindings--;
|
|
3006
|
+
if( bindings === 0 && canbind){
|
|
3007
|
+
computedData.teardown();
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
};
|
|
3011
|
+
return computed;
|
|
3012
|
+
};
|
|
3013
|
+
can.compute.binder = computeBinder;
|
|
3014
|
+
|
|
3015
|
+
// ## ejs.js
|
|
3016
|
+
// `can.EJS`
|
|
3017
|
+
// _Embedded JavaScript Templates._
|
|
3018
|
+
|
|
3019
|
+
// Helper methods.
|
|
3020
|
+
var myEval = function( script ) {
|
|
3021
|
+
eval(script);
|
|
3022
|
+
},
|
|
3023
|
+
extend = can.extend,
|
|
3024
|
+
// Regular expressions for caching.
|
|
3025
|
+
quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
|
|
3026
|
+
attrReg = /([^\s]+)=$/,
|
|
3027
|
+
newLine = /(\r|\n)+/g,
|
|
3028
|
+
attributeReplace = /__!!__/g,
|
|
3029
|
+
tagMap = {
|
|
3030
|
+
"": "span",
|
|
3031
|
+
table: "tr",
|
|
3032
|
+
tr: "td",
|
|
3033
|
+
ol: "li",
|
|
3034
|
+
ul: "li",
|
|
3035
|
+
tbody: "tr",
|
|
3036
|
+
thead: "tr",
|
|
3037
|
+
tfoot: "tr"
|
|
3038
|
+
},
|
|
3039
|
+
// Escapes characters starting with `\`.
|
|
3040
|
+
clean = function( content ) {
|
|
3041
|
+
return content
|
|
3042
|
+
.split('\\').join("\\\\")
|
|
3043
|
+
.split("\n").join("\\n")
|
|
3044
|
+
.split('"').join('\\"')
|
|
3045
|
+
.split("\t").join("\\t");
|
|
3046
|
+
},
|
|
3047
|
+
bracketNum = function(content){
|
|
3048
|
+
return (--content.split("{").length) - (--content.split("}").length);
|
|
3049
|
+
},
|
|
3050
|
+
// Cross-browser attribute methods.
|
|
3051
|
+
// These should be mapped to the underlying library.
|
|
3052
|
+
attrMap = {
|
|
3053
|
+
"class" : "className"
|
|
3054
|
+
},
|
|
3055
|
+
bool = can.each(["checked","disabled","readonly","required"], function(n){
|
|
3056
|
+
attrMap[n] = n;
|
|
3057
|
+
}),
|
|
3058
|
+
setAttr = function(el, attrName, val){
|
|
3059
|
+
attrMap[attrName] ?
|
|
3060
|
+
(el[attrMap[attrName]] = can.inArray(attrName,bool) > -1? true : val):
|
|
3061
|
+
el.setAttribute(attrName, val);
|
|
3062
|
+
},
|
|
3063
|
+
getAttr = function(el, attrName){
|
|
3064
|
+
return attrMap[attrName]?
|
|
3065
|
+
el[attrMap[attrName]]:
|
|
3066
|
+
el.getAttribute(attrName);
|
|
3067
|
+
},
|
|
3068
|
+
removeAttr = function(el, attrName){
|
|
3069
|
+
if(can.inArray(attrName,bool) > -1){
|
|
3070
|
+
el[attrName] = false;
|
|
3071
|
+
} else{
|
|
3072
|
+
el.removeAttribute(attrName)
|
|
3073
|
+
}
|
|
3074
|
+
},
|
|
3075
|
+
// a helper to get the parentNode for a given element el
|
|
3076
|
+
// if el is in a documentFragment, it will return defaultParentNode
|
|
3077
|
+
getParentNode = function(el, defaultParentNode){
|
|
3078
|
+
return defaultParentNode && el.parentNode.nodeType === 11 ? defaultParentNode : el.parentNode;
|
|
3079
|
+
},
|
|
3080
|
+
// helper to know if property is not an expando on oldObserved's list of observes
|
|
3081
|
+
// this should probably be removed and oldObserved should just have a
|
|
3082
|
+
// property with observes
|
|
3083
|
+
observeProp = function(name){
|
|
3084
|
+
return name.indexOf("|") >= 0;
|
|
3085
|
+
},
|
|
3086
|
+
// Returns escaped/sanatized content for anything other than a live-binding
|
|
3087
|
+
contentEscape = function( txt ) {
|
|
3088
|
+
return (typeof txt == 'string' || typeof txt == 'number') ?
|
|
3089
|
+
can.esc( txt ) :
|
|
3090
|
+
contentText(txt);
|
|
3091
|
+
},
|
|
3092
|
+
// Returns text content for anything other than a live-binding
|
|
3093
|
+
contentText = function( input ) {
|
|
3094
|
+
|
|
3095
|
+
// If it's a string, return.
|
|
3096
|
+
if ( typeof input == 'string' ) {
|
|
3097
|
+
return input;
|
|
3098
|
+
}
|
|
3099
|
+
// If has no value, return an empty string.
|
|
3100
|
+
if ( !input && input != 0 ) {
|
|
3101
|
+
return '';
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3104
|
+
// If it's an object, and it has a hookup method.
|
|
3105
|
+
var hook = (input.hookup &&
|
|
3106
|
+
|
|
3107
|
+
// Make a function call the hookup method.
|
|
3108
|
+
function( el, id ) {
|
|
3109
|
+
input.hookup.call(input, el, id);
|
|
3110
|
+
}) ||
|
|
3111
|
+
|
|
3112
|
+
// Or if it's a `function`, just use the input.
|
|
3113
|
+
(typeof input == 'function' && input);
|
|
3114
|
+
|
|
3115
|
+
// Finally, if there is a `function` to hookup on some dom,
|
|
3116
|
+
// add it to pending hookups.
|
|
3117
|
+
if ( hook ) {
|
|
3118
|
+
pendingHookups.push(hook);
|
|
3119
|
+
return '';
|
|
3120
|
+
}
|
|
3121
|
+
|
|
3122
|
+
// Finally, if all else is `false`, `toString()` it.
|
|
3123
|
+
return "" + input;
|
|
3124
|
+
},
|
|
3125
|
+
// The EJS constructor function
|
|
3126
|
+
EJS = function( options ) {
|
|
3127
|
+
// Supports calling EJS without the constructor
|
|
3128
|
+
// This returns a function that renders the template.
|
|
3129
|
+
if ( this.constructor != EJS ) {
|
|
3130
|
+
var ejs = new EJS(options);
|
|
3131
|
+
return function( data, helpers ) {
|
|
3132
|
+
return ejs.render(data, helpers);
|
|
3133
|
+
};
|
|
3134
|
+
}
|
|
3135
|
+
// If we get a `function` directly, it probably is coming from
|
|
3136
|
+
// a `steal`-packaged view.
|
|
3137
|
+
if ( typeof options == "function" ) {
|
|
3138
|
+
this.template = {
|
|
3139
|
+
fn: options
|
|
3140
|
+
};
|
|
3141
|
+
return;
|
|
3142
|
+
}
|
|
3143
|
+
// Set options on self.
|
|
3144
|
+
extend(this, options);
|
|
3145
|
+
this.template = scan(this.text, this.name);
|
|
3146
|
+
};
|
|
3147
|
+
|
|
3148
|
+
can.EJS = EJS;
|
|
3149
|
+
EJS.prototype.
|
|
3150
|
+
render = function( object, extraHelpers ) {
|
|
3151
|
+
object = object || {};
|
|
3152
|
+
return this.template.fn.call(object, object, new EJS.Helpers(object, extraHelpers || {}));
|
|
3153
|
+
};
|
|
3154
|
+
extend(EJS, {
|
|
3155
|
+
// Called to return the content within a magic tag like `<%= %>`.
|
|
3156
|
+
// - escape - if the content returned should be escaped
|
|
3157
|
+
// - tagName - the tag name the magic tag is within or the one that proceeds the magic tag
|
|
3158
|
+
// - status - where the tag is in. The status can be:
|
|
3159
|
+
// - _STRING_ - The name of the attribute the magic tag is within
|
|
3160
|
+
// - `1` - The magic tag is within a tag like `<div <%= %>>`
|
|
3161
|
+
// - `0` - The magic tag is outside (or between) tags like `<div><%= %></div>`
|
|
3162
|
+
// - self - the `this` the template was called with
|
|
3163
|
+
// - func - the "wrapping" function. For example: `<%= task.attr('name') %>` becomes
|
|
3164
|
+
// `(function(){return task.attr('name')})
|
|
3165
|
+
txt : function(escape, tagName, status, self, func){
|
|
3166
|
+
// call the "wrapping" function and get the binding information
|
|
3167
|
+
var binding = can.compute.binder(func, self, function(newVal, oldVal){
|
|
3168
|
+
// call the update method we will define for each
|
|
3169
|
+
// type of attribute
|
|
3170
|
+
update(newVal, oldVal)
|
|
3171
|
+
});
|
|
3172
|
+
|
|
3173
|
+
// If we had no observes just return the value returned by func.
|
|
3174
|
+
if(!binding.isListening){
|
|
3175
|
+
return (escape || status !== 0? contentEscape : contentText)(binding.value);
|
|
3176
|
+
}
|
|
3177
|
+
// The following are helper methods or varaibles that will
|
|
3178
|
+
// be defined by one of the various live-updating schemes.
|
|
3179
|
+
|
|
3180
|
+
// The parent element we are listening to for teardown
|
|
3181
|
+
var parentElement,
|
|
3182
|
+
// if the parent element is removed, teardown the binding
|
|
3183
|
+
setupTeardownOnDestroy = function(el){
|
|
3184
|
+
can.bind.call(el,'destroyed', binding.teardown)
|
|
3185
|
+
parentElement = el;
|
|
3186
|
+
},
|
|
3187
|
+
// if there is no parent, undo bindings
|
|
3188
|
+
teardownCheck = function(parent){
|
|
3189
|
+
if(!parent){
|
|
3190
|
+
binding.teardown();
|
|
3191
|
+
can.unbind.call(parentElement,'destroyed', binding.teardown)
|
|
3192
|
+
}
|
|
3193
|
+
},
|
|
3194
|
+
// the tag type to insert
|
|
3195
|
+
tag = (tagMap[tagName] || "span"),
|
|
3196
|
+
// this will be filled in if binding.isListening
|
|
3197
|
+
update;
|
|
3198
|
+
|
|
3199
|
+
|
|
3200
|
+
// The magic tag is outside or between tags.
|
|
3201
|
+
if(status == 0){
|
|
3202
|
+
// Return an element tag with a hookup in place of the content
|
|
3203
|
+
return "<" +tag+can.view.hook(
|
|
3204
|
+
escape ?
|
|
3205
|
+
// If we are escaping, replace the parentNode with
|
|
3206
|
+
// a text node who's value is `func`'s return value.
|
|
3207
|
+
function(el, parentNode){
|
|
3208
|
+
// updates the text of the text node
|
|
3209
|
+
update = function(newVal){
|
|
3210
|
+
node.nodeValue = ""+newVal;
|
|
3211
|
+
teardownCheck(node.parentNode);
|
|
3212
|
+
};
|
|
3213
|
+
|
|
3214
|
+
var parent = getParentNode(el, parentNode),
|
|
3215
|
+
node = document.createTextNode(binding.value);
|
|
3216
|
+
|
|
3217
|
+
parent.insertBefore(node, el);
|
|
3218
|
+
parent.removeChild(el);
|
|
3219
|
+
setupTeardownOnDestroy(parent);
|
|
3220
|
+
}
|
|
3221
|
+
:
|
|
3222
|
+
// If we are not escaping, replace the parentNode with a
|
|
3223
|
+
// documentFragment created as with `func`'s return value.
|
|
3224
|
+
function(span, parentNode){
|
|
3225
|
+
// updates the elements with the new content
|
|
3226
|
+
update = function(newVal){
|
|
3227
|
+
// is this still part of the DOM?
|
|
3228
|
+
var attached = nodes[0].parentNode;
|
|
3229
|
+
// update the nodes in the DOM with the new rendered value
|
|
3230
|
+
if( attached ) {
|
|
3231
|
+
nodes = makeAndPut(newVal, nodes);
|
|
3232
|
+
}
|
|
3233
|
+
teardownCheck(nodes[0].parentNode)
|
|
3234
|
+
}
|
|
3235
|
+
|
|
3236
|
+
// make sure we have a valid parentNode
|
|
3237
|
+
parentNode = getParentNode(span, parentNode)
|
|
3238
|
+
// A helper function to manage inserting the contents
|
|
3239
|
+
// and removing the old contents
|
|
3240
|
+
var makeAndPut = function(val, remove){
|
|
3241
|
+
// create the fragment, but don't hook it up
|
|
3242
|
+
// we need to insert it into the document first
|
|
3243
|
+
|
|
3244
|
+
var frag = can.view.frag(val, parentNode),
|
|
3245
|
+
// keep a reference to each node
|
|
3246
|
+
nodes = can.map(frag.childNodes,function(node){
|
|
3247
|
+
return node;
|
|
3248
|
+
}),
|
|
3249
|
+
last = remove[remove.length - 1];
|
|
3250
|
+
|
|
3251
|
+
// Insert it in the `document` or `documentFragment`
|
|
3252
|
+
if( last.nextSibling ){
|
|
3253
|
+
last.parentNode.insertBefore(frag, last.nextSibling)
|
|
3254
|
+
} else {
|
|
3255
|
+
last.parentNode.appendChild(frag)
|
|
3256
|
+
}
|
|
3257
|
+
// Remove the old content.
|
|
3258
|
+
can.remove( can.$(remove) );
|
|
3259
|
+
|
|
3260
|
+
return nodes;
|
|
3261
|
+
},
|
|
3262
|
+
// nodes are the nodes that any updates will replace
|
|
3263
|
+
// at this point, these nodes could be part of a documentFragment
|
|
3264
|
+
nodes = makeAndPut(binding.value, [span]);
|
|
3265
|
+
|
|
3266
|
+
|
|
3267
|
+
setupTeardownOnDestroy(parentNode);
|
|
3268
|
+
|
|
3269
|
+
}) + "></" +tag+">";
|
|
3270
|
+
// In a tag, but not in an attribute
|
|
3271
|
+
} else if(status === 1){
|
|
3272
|
+
// remember the old attr name
|
|
3273
|
+
var attrName = binding.value.replace(/['"]/g, '').split('=')[0];
|
|
3274
|
+
pendingHookups.push(function(el) {
|
|
3275
|
+
update = function(newVal){
|
|
3276
|
+
var parts = (newVal|| "").replace(/['"]/g, '').split('='),
|
|
3277
|
+
newAttrName = parts[0];
|
|
3278
|
+
|
|
3279
|
+
// Remove if we have a change and used to have an `attrName`.
|
|
3280
|
+
if((newAttrName != attrName) && attrName){
|
|
3281
|
+
removeAttr(el,attrName)
|
|
3282
|
+
}
|
|
3283
|
+
// Set if we have a new `attrName`.
|
|
3284
|
+
if(newAttrName){
|
|
3285
|
+
setAttr(el, newAttrName, parts[1]);
|
|
3286
|
+
attrName = newAttrName;
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
setupTeardownOnDestroy(el);
|
|
3290
|
+
});
|
|
3291
|
+
|
|
3292
|
+
return binding.value;
|
|
3293
|
+
} else { // In an attribute...
|
|
3294
|
+
pendingHookups.push(function(el){
|
|
3295
|
+
// update will call this attribute's render method
|
|
3296
|
+
// and set the attribute accordingly
|
|
3297
|
+
update = function(){
|
|
3298
|
+
setAttr(el, status, hook.render())
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
var wrapped = can.$(el),
|
|
3302
|
+
hooks;
|
|
3303
|
+
|
|
3304
|
+
// Get the list of hookups or create one for this element.
|
|
3305
|
+
// Hooks is a map of attribute names to hookup `data`s.
|
|
3306
|
+
// Each hookup data has:
|
|
3307
|
+
// `render` - A `function` to render the value of the attribute.
|
|
3308
|
+
// `funcs` - A list of hookup `function`s on that attribute.
|
|
3309
|
+
// `batchNum` - The last event `batchNum`, used for performance.
|
|
3310
|
+
(hooks = can.data(wrapped,'hooks')) || can.data(wrapped, 'hooks', hooks = {});
|
|
3311
|
+
|
|
3312
|
+
// Get the attribute value.
|
|
3313
|
+
var attr = getAttr(el, status),
|
|
3314
|
+
// Split the attribute value by the template.
|
|
3315
|
+
parts = attr.split("__!!__"),
|
|
3316
|
+
hook;
|
|
3317
|
+
|
|
3318
|
+
|
|
3319
|
+
// If we already had a hookup for this attribute...
|
|
3320
|
+
if(hooks[status]) {
|
|
3321
|
+
// Just add to that attribute's list of `function`s.
|
|
3322
|
+
hooks[status].bindings.push(binding);
|
|
3323
|
+
}
|
|
3324
|
+
else {
|
|
3325
|
+
// Create the hookup data.
|
|
3326
|
+
hooks[status] = {
|
|
3327
|
+
render: function() {
|
|
3328
|
+
var i =0,
|
|
3329
|
+
newAttr = attr.replace(attributeReplace, function() {
|
|
3330
|
+
return contentText( hook.bindings[i++].value );
|
|
3331
|
+
});
|
|
3332
|
+
return newAttr;
|
|
3333
|
+
},
|
|
3334
|
+
bindings: [binding],
|
|
3335
|
+
batchNum : undefined
|
|
3336
|
+
};
|
|
3337
|
+
};
|
|
3338
|
+
|
|
3339
|
+
// Save the hook for slightly faster performance.
|
|
3340
|
+
hook = hooks[status];
|
|
3341
|
+
|
|
3342
|
+
// Insert the value in parts.
|
|
3343
|
+
parts.splice(1,0,binding.value);
|
|
3344
|
+
|
|
3345
|
+
// Set the attribute.
|
|
3346
|
+
setAttr(el, status, parts.join(""));
|
|
3347
|
+
|
|
3348
|
+
// Bind on change.
|
|
3349
|
+
//liveBind(observed, el, binder,oldObserved);
|
|
3350
|
+
setupTeardownOnDestroy(el)
|
|
3351
|
+
})
|
|
3352
|
+
return "__!!__";
|
|
3353
|
+
}
|
|
3354
|
+
},
|
|
3355
|
+
pending: function() {
|
|
3356
|
+
if(pendingHookups.length) {
|
|
3357
|
+
var hooks = pendingHookups.slice(0);
|
|
3358
|
+
|
|
3359
|
+
pendingHookups = [];
|
|
3360
|
+
return can.view.hook(function(el){
|
|
3361
|
+
can.each(hooks, function(fn){
|
|
3362
|
+
fn(el);
|
|
3363
|
+
})
|
|
3364
|
+
});
|
|
3365
|
+
}else {
|
|
3366
|
+
return "";
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
});
|
|
3370
|
+
// Start scanning code.
|
|
3371
|
+
var tokenReg = new RegExp("(" +[ "<%%", "%%>", "<%==", "<%=",
|
|
3372
|
+
"<%#", "<%", "%>", "<", ">", '"', "'"].join("|")+")","g"),
|
|
3373
|
+
// Commands for caching.
|
|
3374
|
+
startTxt = 'var ___v1ew = [];',
|
|
3375
|
+
finishTxt = "return ___v1ew.join('')",
|
|
3376
|
+
put_cmd = "___v1ew.push(",
|
|
3377
|
+
insert_cmd = put_cmd,
|
|
3378
|
+
// Global controls (used by other functions to know where we are).
|
|
3379
|
+
//
|
|
3380
|
+
// Are we inside a tag?
|
|
3381
|
+
htmlTag = null,
|
|
3382
|
+
// Are we within a quote within a tag?
|
|
3383
|
+
quote = null,
|
|
3384
|
+
// What was the text before the current quote? (used to get the `attr` name)
|
|
3385
|
+
beforeQuote = null,
|
|
3386
|
+
// Used to mark where the element is.
|
|
3387
|
+
status = function(){
|
|
3388
|
+
// `t` - `1`.
|
|
3389
|
+
// `h` - `0`.
|
|
3390
|
+
// `q` - String `beforeQuote`.
|
|
3391
|
+
return quote ? "'"+beforeQuote.match(attrReg)[1]+"'" : (htmlTag ? 1 : 0)
|
|
3392
|
+
},
|
|
3393
|
+
pendingHookups = [],
|
|
3394
|
+
scan = function(source, name){
|
|
3395
|
+
var tokens = [],
|
|
3396
|
+
last = 0;
|
|
3397
|
+
|
|
3398
|
+
source = source.replace(newLine, "\n");
|
|
3399
|
+
source.replace(tokenReg, function(whole, part, offset){
|
|
3400
|
+
// if the next token starts after the last token ends
|
|
3401
|
+
// push what's in between
|
|
3402
|
+
if(offset > last){
|
|
3403
|
+
tokens.push( source.substring(last, offset) );
|
|
3404
|
+
}
|
|
3405
|
+
// push the token
|
|
3406
|
+
tokens.push(part);
|
|
3407
|
+
// update the position of the last part of the last token
|
|
3408
|
+
last = offset+part.length;
|
|
3409
|
+
})
|
|
3410
|
+
// if there's something at the end, add it
|
|
3411
|
+
if(last < source.length){
|
|
3412
|
+
tokens.push(source.substr(last))
|
|
3413
|
+
}
|
|
3414
|
+
|
|
3415
|
+
var content = '',
|
|
3416
|
+
buff = [startTxt],
|
|
3417
|
+
// Helper `function` for putting stuff in the view concat.
|
|
3418
|
+
put = function( content, bonus ) {
|
|
3419
|
+
buff.push(put_cmd, '"', clean(content), '"'+(bonus||'')+');');
|
|
3420
|
+
},
|
|
3421
|
+
// A stack used to keep track of how we should end a bracket
|
|
3422
|
+
// `}`.
|
|
3423
|
+
// Once we have a `<%= %>` with a `leftBracket`,
|
|
3424
|
+
// we store how the file should end here (either `))` or `;`).
|
|
3425
|
+
endStack =[],
|
|
3426
|
+
// The last token, used to remember which tag we are in.
|
|
3427
|
+
lastToken,
|
|
3428
|
+
// The corresponding magic tag.
|
|
3429
|
+
startTag = null,
|
|
3430
|
+
// Was there a magic tag inside an html tag?
|
|
3431
|
+
magicInTag = false,
|
|
3432
|
+
// The current tag name.
|
|
3433
|
+
tagName = '',
|
|
3434
|
+
// stack of tagNames
|
|
3435
|
+
tagNames = [],
|
|
3436
|
+
// Declared here.
|
|
3437
|
+
bracketCount,
|
|
3438
|
+
i = 0,
|
|
3439
|
+
token;
|
|
3440
|
+
|
|
3441
|
+
// Reinitialize the tag state goodness.
|
|
3442
|
+
htmlTag = quote = beforeQuote = null;
|
|
3443
|
+
|
|
3444
|
+
for (; (token = tokens[i++]) !== undefined;) {
|
|
3445
|
+
|
|
3446
|
+
if ( startTag === null ) {
|
|
3447
|
+
switch ( token ) {
|
|
3448
|
+
case '<%':
|
|
3449
|
+
case '<%=':
|
|
3450
|
+
case '<%==':
|
|
3451
|
+
magicInTag = 1;
|
|
3452
|
+
case '<%#':
|
|
3453
|
+
// A new line -- just add whatever content within a clean.
|
|
3454
|
+
// Reset everything.
|
|
3455
|
+
startTag = token;
|
|
3456
|
+
if ( content.length ) {
|
|
3457
|
+
put(content);
|
|
3458
|
+
}
|
|
3459
|
+
content = '';
|
|
3460
|
+
break;
|
|
3461
|
+
|
|
3462
|
+
case '<%%':
|
|
3463
|
+
// Replace `<%%` with `<%`.
|
|
3464
|
+
content += '<%';
|
|
3465
|
+
break;
|
|
3466
|
+
case '<':
|
|
3467
|
+
// Make sure we are not in a comment.
|
|
3468
|
+
if(tokens[i].indexOf("!--") !== 0) {
|
|
3469
|
+
htmlTag = 1;
|
|
3470
|
+
magicInTag = 0;
|
|
3471
|
+
}
|
|
3472
|
+
content += token;
|
|
3473
|
+
break;
|
|
3474
|
+
case '>':
|
|
3475
|
+
htmlTag = 0;
|
|
3476
|
+
// TODO: all `<%=` in tags should be added to pending hookups.
|
|
3477
|
+
if(magicInTag){
|
|
3478
|
+
put(content, ",can.EJS.pending(),\">\"");
|
|
3479
|
+
content = '';
|
|
3480
|
+
} else {
|
|
3481
|
+
content += token;
|
|
3482
|
+
}
|
|
3483
|
+
// if it's a tag like <input/>
|
|
3484
|
+
if(lastToken.substr(-1) == "/"){
|
|
3485
|
+
// remove the current tag in the stack
|
|
3486
|
+
tagNames.pop();
|
|
3487
|
+
// set the current tag to the previous parent
|
|
3488
|
+
tagName = tagNames[tagNames.length-1];
|
|
3489
|
+
}
|
|
3490
|
+
break;
|
|
3491
|
+
case "'":
|
|
3492
|
+
case '"':
|
|
3493
|
+
// If we are in an html tag, finding matching quotes.
|
|
3494
|
+
if(htmlTag){
|
|
3495
|
+
// We have a quote and it matches.
|
|
3496
|
+
if(quote && quote === token){
|
|
3497
|
+
// We are exiting the quote.
|
|
3498
|
+
quote = null;
|
|
3499
|
+
// Otherwise we are creating a quote.
|
|
3500
|
+
// TODO: does this handle `\`?
|
|
3501
|
+
} else if(quote === null){
|
|
3502
|
+
quote = token;
|
|
3503
|
+
beforeQuote = lastToken;
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3506
|
+
default:
|
|
3507
|
+
// Track the current tag
|
|
3508
|
+
if(lastToken === '<'){
|
|
3509
|
+
tagName = token.split(' ')[0];
|
|
3510
|
+
// If
|
|
3511
|
+
if( tagName.indexOf("/") === 0 && tagNames.pop() === tagName.substr(1) ) {
|
|
3512
|
+
tagName = tagNames[tagNames.length-1]|| tagName.substr(1)
|
|
3513
|
+
} else {
|
|
3514
|
+
tagNames.push(tagName);
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
content += token;
|
|
3518
|
+
break;
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
else {
|
|
3522
|
+
// We have a start tag.
|
|
3523
|
+
switch ( token ) {
|
|
3524
|
+
case '%>':
|
|
3525
|
+
// `%>`
|
|
3526
|
+
switch ( startTag ) {
|
|
3527
|
+
case '<%':
|
|
3528
|
+
// `<%`
|
|
3529
|
+
|
|
3530
|
+
// Get the number of `{ minus }`
|
|
3531
|
+
bracketCount = bracketNum(content);
|
|
3532
|
+
|
|
3533
|
+
// We are ending a block.
|
|
3534
|
+
if (bracketCount == 1) {
|
|
3535
|
+
|
|
3536
|
+
// We are starting on.
|
|
3537
|
+
buff.push(insert_cmd, "can.EJS.txt(0,'"+tagName+"'," + status() + ",this,function(){", startTxt, content);
|
|
3538
|
+
|
|
3539
|
+
endStack.push({
|
|
3540
|
+
before: "",
|
|
3541
|
+
after: finishTxt+"}));\n"
|
|
3542
|
+
})
|
|
3543
|
+
}
|
|
3544
|
+
else {
|
|
3545
|
+
|
|
3546
|
+
// How are we ending this statement?
|
|
3547
|
+
var last = // If the stack has value and we are ending a block...
|
|
3548
|
+
endStack.length && bracketCount == -1 ? // Use the last item in the block stack.
|
|
3549
|
+
endStack.pop() : // Or use the default ending.
|
|
3550
|
+
{
|
|
3551
|
+
after: ";"
|
|
3552
|
+
};
|
|
3553
|
+
|
|
3554
|
+
// If we are ending a returning block,
|
|
3555
|
+
// add the finish text which returns the result of the
|
|
3556
|
+
// block.
|
|
3557
|
+
if (last.before) {
|
|
3558
|
+
buff.push(last.before)
|
|
3559
|
+
}
|
|
3560
|
+
// Add the remaining content.
|
|
3561
|
+
buff.push(content, ";",last.after);
|
|
3562
|
+
}
|
|
3563
|
+
break;
|
|
3564
|
+
case '<%=':
|
|
3565
|
+
case '<%==':
|
|
3566
|
+
// We have an extra `{` -> `block`.
|
|
3567
|
+
// Get the number of `{ minus }`.
|
|
3568
|
+
bracketCount = bracketNum(content);
|
|
3569
|
+
// If we have more `{`, it means there is a block.
|
|
3570
|
+
if( bracketCount ){
|
|
3571
|
+
// When we return to the same # of `{` vs `}` end with a `doubleParent`.
|
|
3572
|
+
endStack.push({
|
|
3573
|
+
before : finishTxt,
|
|
3574
|
+
after: "}));"
|
|
3575
|
+
})
|
|
3576
|
+
}
|
|
3577
|
+
// Check if its a func like `()->`
|
|
3578
|
+
if(quickFunc.test(content)){
|
|
3579
|
+
var parts = content.match(quickFunc)
|
|
3580
|
+
content = "function(__){var "+parts[1]+"=can.$(__);"+parts[2]+"}"
|
|
3581
|
+
}
|
|
3582
|
+
|
|
3583
|
+
// If we have `<%== a(function(){ %>` then we want
|
|
3584
|
+
// `can.EJS.text(0,this, function(){ return a(function(){ var _v1ew = [];`.
|
|
3585
|
+
buff.push(insert_cmd, "can.EJS.txt("+(startTag === '<%=' ? 1 : 0)+",'"+tagName+"'," + status()+",this,function(){ return ", content,
|
|
3586
|
+
// If we have a block.
|
|
3587
|
+
bracketCount ?
|
|
3588
|
+
// Start with startTxt `"var _v1ew = [];"`.
|
|
3589
|
+
startTxt :
|
|
3590
|
+
// If not, add `doubleParent` to close push and text.
|
|
3591
|
+
"}));"
|
|
3592
|
+
);
|
|
3593
|
+
break;
|
|
3594
|
+
}
|
|
3595
|
+
startTag = null;
|
|
3596
|
+
content = '';
|
|
3597
|
+
break;
|
|
3598
|
+
case '<%%':
|
|
3599
|
+
content += '<%';
|
|
3600
|
+
break;
|
|
3601
|
+
default:
|
|
3602
|
+
content += token;
|
|
3603
|
+
break;
|
|
3604
|
+
}
|
|
3605
|
+
|
|
3606
|
+
}
|
|
3607
|
+
lastToken = token;
|
|
3608
|
+
}
|
|
3609
|
+
|
|
3610
|
+
// Put it together...
|
|
3611
|
+
if ( content.length ) {
|
|
3612
|
+
// Should be `content.dump` in Ruby.
|
|
3613
|
+
put(content)
|
|
3614
|
+
}
|
|
3615
|
+
buff.push(";")
|
|
3616
|
+
|
|
3617
|
+
var template = buff.join(''),
|
|
3618
|
+
out = {
|
|
3619
|
+
out: 'with(_VIEW) { with (_CONTEXT) {' + template + " "+finishTxt+"}}"
|
|
3620
|
+
};
|
|
3621
|
+
// Use `eval` instead of creating a function, because it is easier to debug.
|
|
3622
|
+
myEval.call(out, 'this.fn = (function(_CONTEXT,_VIEW){' + out.out + '});\r\n//@ sourceURL=' + name + ".js");
|
|
3623
|
+
return out;
|
|
3624
|
+
};
|
|
3625
|
+
|
|
3626
|
+
|
|
3627
|
+
|
|
3628
|
+
EJS.Helpers = function( data, extras ) {
|
|
3629
|
+
this._data = data;
|
|
3630
|
+
this._extras = extras;
|
|
3631
|
+
extend(this, extras);
|
|
3632
|
+
};
|
|
3633
|
+
EJS.Helpers.prototype = {
|
|
3634
|
+
// TODO Deprecated!!
|
|
3635
|
+
list : function(list, cb){
|
|
3636
|
+
can.each(list, function(item, i){
|
|
3637
|
+
cb(item, i, list)
|
|
3638
|
+
})
|
|
3639
|
+
}
|
|
3640
|
+
};
|
|
3641
|
+
|
|
3642
|
+
// Options for `steal`'s build.
|
|
3643
|
+
can.view.register({
|
|
3644
|
+
suffix: "ejs",
|
|
3645
|
+
// returns a `function` that renders the view.
|
|
3646
|
+
script: function( id, src ) {
|
|
3647
|
+
return "can.EJS(function(_CONTEXT,_VIEW) { " + new EJS({
|
|
3648
|
+
text: src,
|
|
3649
|
+
name: id
|
|
3650
|
+
}).template.out + " })";
|
|
3651
|
+
},
|
|
3652
|
+
renderer: function( id, text ) {
|
|
3653
|
+
return EJS({
|
|
3654
|
+
text: text,
|
|
3655
|
+
name: id
|
|
3656
|
+
});
|
|
3657
|
+
}
|
|
3658
|
+
});
|
|
3659
|
+
|
|
3660
|
+
// Register as an AMD module if supported, otherwise attach to the window
|
|
3661
|
+
if ( typeof define === "function" && define.amd ) {
|
|
3662
|
+
define( "can", [], function () { return can; } );
|
|
3663
|
+
} else {
|
|
3664
|
+
window.can = can;
|
|
3665
|
+
}
|
|
3666
|
+
|
|
3667
|
+
return can;
|
|
3668
|
+
});
|
|
3669
|
+
})(can = {}, this )
|