From 7420dbc803cfdd800a2e1011381b14f4867f93e2 Mon Sep 17 00:00:00 2001 From: David Luecke Date: Tue, 3 May 2016 15:16:35 -0700 Subject: [PATCH 01/10] Start migrating to individual modules --- .gitignore | 1 + build.js | 21 + can.js | 7 +- component/component.js | 508 +- component/component.md | 407 - component/component_bindings_test.js | 1720 -- component/component_test.js | 1914 +- component/events.md | 61 - component/examples/1.html | 42 - component/examples/2.html | 66 - component/examples/3.html | 86 - component/examples/4.html | 175 - component/examples/accordion.html | 101 - ...child-component-content-siblings-leak.html | 48 - component/examples/click_me.html | 22 - component/examples/component-tag.html | 32 - component/examples/grid.js | 32 - component/examples/hello-world.html | 26 - component/examples/list.html | 67 - component/examples/my_greeting_full.html | 25 - component/examples/name_editor.html | 38 - .../nested-component-content-leak.html | 43 - component/examples/nested-component-leak.html | 41 - component/examples/paginate.html | 233 - component/examples/paginate_events_next.html | 29 - .../paginate_events_next_update_page.html | 32 - component/examples/paginate_next.html | 25 - component/examples/paginate_next_event.html | 62 - component/examples/selected.html | 63 - component/examples/tabs.component | 98 - component/examples/tabs.html | 144 - component/examples/treecombo.html | 176 - component/extend.md | 37 - component/helpers.md | 21 - component/leakscope.md | 85 - component/scope.md | 5 - component/tag.md | 11 - component/template.md | 177 - component/test.html | 13 - component/view-model.md | 285 - compute/async.md | 44 - compute/async_computer.md | 19 - compute/benchmark/benchmark.html | 1 - compute/benchmark/compute_benchmark.js | 106 - compute/compute.js | 97 +- compute/compute.md | 291 - compute/compute_settings.md | 18 - compute/compute_test.js | 883 +- compute/computed.md | 13 - compute/get_value_and_bind.js | 305 - compute/proto_benchmark.html | 25 - compute/proto_compute.js | 479 - compute/read.js | 290 - compute/read_test.js | 143 - compute/test.html | 3 - construct/construct.html | 71 - construct/construct.js | 764 +- construct/construct_test.js | 165 +- construct/doc/construct.md | 47 - construct/super/super.html | 52 - construct/super/super.js | 45 - construct/super/super.md | 104 - construct/super/super_test.js | 94 - construct/super/test.html | 3 - construct/test.html | 3 - control/control.extend.md | 69 - control/control.html | 93 - control/control.js | 335 +- control/control.md | 382 - control/control.processor.md | 19 - control/control_test.js | 303 +- control/doc/defaults.md | 46 - control/doc/destroy.md | 71 - control/doc/element.md | 77 - control/doc/on.md | 96 - control/doc/options.md | 41 - control/doc/processors.md | 91 - control/doc/setup.md | 45 - control/eventDescription.md | 5 - control/eventHandler.md | 5 - control/perf.html | 201 - control/test.html | 3 - event/delegate/delegate.js | 85 - event/delegate/delegate.md | 33 - event/delegate/delegate_test.js | 95 - event/delegate/test.html | 3 - event/event.js | 446 +- event/event.md | 65 - event/event_test.js | 265 +- event/namespace/namespace.js | 103 - event/namespace/namespace.md | 9 - event/namespace/namespace_test.js | 76 - event/namespace/test.html | 3 - event/propagate/propagate.js | 92 - event/propagate/propagate.md | 97 - event/propagate/propagate_test.js | 134 - event/propagate/test.html | 3 - event/test.html | 3 - event/test_all.html | 14 - list/doc/Map.md | 32 - list/doc/extend.md | 14 - list/doc/filter.md | 24 - list/doc/list.md | 121 - list/doc/prototype.attr.md | 243 - list/doc/prototype.each.md | 36 - list/doc/prototype.map.md | 47 - list/doc/prototype.reverse.md | 25 - list/doc/prototype.splice.md | 65 - list/doc/reverse.html | 55 - list/list.js | 882 +- list/list_test.js | 371 +- list/promise/doc/always.md | 29 - list/promise/doc/done.md | 29 - list/promise/doc/fail.md | 28 - list/promise/doc/isPending.md | 18 - list/promise/doc/isRejected.md | 21 - list/promise/doc/isResolved.md | 21 - list/promise/doc/promise.md | 23 - list/promise/doc/reason.md | 19 - list/promise/doc/state.md | 20 - list/promise/doc/then.md | 45 - list/promise/promise.html | 104 - list/promise/promise.js | 75 - list/promise/promise_test.js | 182 - list/promise/test.html | 3 - list/sort/20k.txt | 20000 ---------------- list/sort/benchmark.html | 17 - list/sort/simple_sort.html | 63 - list/sort/sort.html | 106 - list/sort/sort.js | 435 - list/sort/sort.md | 125 - list/sort/sort_benchmark.js | 96 - list/sort/sort_test.js | 719 - list/sort/test.html | 3 - list/test.html | 3 - map/app/app.js | 70 - map/app/app_test.js | 21 - map/app/test.html | 3 - map/benchmark.html | 17 - map/bubble.js | 218 - map/define/define.js | 364 - map/define/define_test.js | 1230 - map/define/doc/TypeConstructor.md | 23 - map/define/doc/ValueConstructor.md | 21 - map/define/doc/attrDefinition.md | 176 - map/define/doc/define.md | 121 - map/define/doc/examples/make-model-year.html | 228 - map/define/doc/get.md | 194 - map/define/doc/remove.md | 24 - map/define/doc/serialize.md | 59 - map/define/doc/set.md | 227 - map/define/doc/type.md | 90 - map/define/doc/value.md | 36 - map/define/test.html | 3 - map/doc/map.md | 140 - map/doc/prototype.attr.md | 159 - map/doc/prototype.bind.md | 99 - map/doc/prototype.compute-attr.md | 68 - map/doc/prototype.compute.md | 28 - map/doc/prototype.default-attr.md | 33 - map/doc/prototype.each.md | 36 - map/doc/prototype.removeAttr.md | 24 - map/doc/prototype.serialize.md | 26 - map/doc/prototype.unbind.md | 31 - map/doc/static.keys.md | 20 - map/map.html | 90 - map/map.js | 659 +- map/map_benchmark.js | 41 - map/map_helpers.js | 194 - map/map_test.js | 370 +- map/test.html | 3 - model/doc/bind.md | 25 - model/doc/create.md | 91 - model/doc/deprecated_model.md | 41 - model/doc/deprecated_models.md | 44 - model/doc/destroy.md | 63 - model/doc/findAll.md | 151 - model/doc/findAllData.md | 30 - model/doc/findOne.md | 136 - model/doc/findOneData.md | 4 - model/doc/id.md | 11 - model/doc/isNew.md | 15 - model/doc/list.md | 53 - model/doc/makeFindAll.md | 83 - model/doc/makeFindOne.md | 83 - model/doc/model.md | 168 - model/doc/model_bind.md | 61 - model/doc/model_destroy.md | 40 - model/doc/model_save.md | 68 - model/doc/model_unbind.md | 27 - model/doc/parseModel.md | 72 - model/doc/parseModels.md | 96 - model/doc/removeAttr.md | 28 - model/doc/resource.md | 64 - model/doc/setup.md | 5 - model/doc/store.md | 88 - model/doc/unbind.md | 25 - model/doc/update.md | 91 - model/model.js | 686 +- model/model_list.md | 157 - model/model_test.js | 1745 +- model/test.html | 3 - model/test/4.json | 4 - model/test/associations_test.js | 96 - model/test/create.json | 4 - model/test/findAll.json | 4 - model/test/people.json | 4 - model/test/person.json | 4 - model/test/schools.json | 4 - model/test/update4.json | 4 - package.backup.json | 204 + package.json | 273 +- route/demo.html | 247 - route/docs/map.html | 157 - route/docs/map.md | 153 - route/historytabs.html | 133 - route/route.html | 115 - route/route.md | 258 - route/route_map.md | 153 - route/test.html | 3 - route/testing.html | 38 - test/all-tests.js | 23 - test/amd/jquery.html | 68 - test/benchmarks.js | 34 - test/builders/browserify/main.js | 18 - test/builders/browserify/prod.html | 8 - test/builders/browserify/test.js | 64 - test/builders/steal-tools/app.html | 34 - test/builders/steal-tools/app.js | 10 - .../steal-tools/bundle/components/one/one.js | 3 - .../bundle/components/one/one.stache | 1 - test/builders/steal-tools/bundle/config.js | 49 - test/builders/steal-tools/bundle/main.js | 6 - test/builders/steal-tools/bundle/prod.html | 4 - .../steal-tools/bundle/template.stache | 1 - test/builders/steal-tools/config.js | 48 - test/builders/steal-tools/helloworld.stache | 2 - test/builders/steal-tools/helpers.js | 6 - test/builders/steal-tools/import/app.js | 1 - test/builders/steal-tools/import/app.stache | 7 - test/builders/steal-tools/import/other.js | 1 - test/builders/steal-tools/import/thing.js | 1 - test/builders/steal-tools/prod-bundled.html | 2 - test/builders/steal-tools/prod.html | 4 - test/builders/steal-tools/test.js | 151 - test/compatibility/jquery.html | 66 - test/demos_and_tests.html | 67 - test/dev/jquery.html | 68 - test/index.html | 3 + test/jquery.html | 51 - test/performance-loading.html | 788 - test/server/test.js | 28 - test/templates/__configuration__-amd.html.ejs | 59 - .../__configuration__-compat.html.ejs | 51 - test/templates/__configuration__-dev.html.ejs | 55 - .../templates/__configuration__-dist.html.ejs | 56 - test/templates/__configuration__.html.ejs | 20 - test/templates/test.html.ejs | 22 - test/test.js | 49 +- util/can.js | 254 +- util/util.js | 8 +- 261 files changed, 343 insertions(+), 51657 deletions(-) create mode 100644 build.js delete mode 100644 component/component.md delete mode 100644 component/component_bindings_test.js delete mode 100644 component/events.md delete mode 100644 component/examples/1.html delete mode 100644 component/examples/2.html delete mode 100644 component/examples/3.html delete mode 100644 component/examples/4.html delete mode 100644 component/examples/accordion.html delete mode 100644 component/examples/child-component-content-siblings-leak.html delete mode 100644 component/examples/click_me.html delete mode 100644 component/examples/component-tag.html delete mode 100644 component/examples/grid.js delete mode 100644 component/examples/hello-world.html delete mode 100644 component/examples/list.html delete mode 100644 component/examples/my_greeting_full.html delete mode 100644 component/examples/name_editor.html delete mode 100644 component/examples/nested-component-content-leak.html delete mode 100644 component/examples/nested-component-leak.html delete mode 100644 component/examples/paginate.html delete mode 100644 component/examples/paginate_events_next.html delete mode 100644 component/examples/paginate_events_next_update_page.html delete mode 100644 component/examples/paginate_next.html delete mode 100644 component/examples/paginate_next_event.html delete mode 100644 component/examples/selected.html delete mode 100644 component/examples/tabs.component delete mode 100644 component/examples/tabs.html delete mode 100644 component/examples/treecombo.html delete mode 100644 component/extend.md delete mode 100644 component/helpers.md delete mode 100644 component/leakscope.md delete mode 100644 component/scope.md delete mode 100644 component/tag.md delete mode 100644 component/template.md delete mode 100644 component/test.html delete mode 100644 component/view-model.md delete mode 100644 compute/async.md delete mode 100644 compute/async_computer.md delete mode 100644 compute/benchmark/benchmark.html delete mode 100644 compute/benchmark/compute_benchmark.js delete mode 100644 compute/compute.md delete mode 100644 compute/compute_settings.md delete mode 100644 compute/computed.md delete mode 100644 compute/get_value_and_bind.js delete mode 100644 compute/proto_benchmark.html delete mode 100644 compute/proto_compute.js delete mode 100644 compute/read.js delete mode 100644 compute/read_test.js delete mode 100644 compute/test.html delete mode 100644 construct/construct.html delete mode 100644 construct/doc/construct.md delete mode 100644 construct/super/super.html delete mode 100644 construct/super/super.js delete mode 100644 construct/super/super.md delete mode 100644 construct/super/super_test.js delete mode 100644 construct/super/test.html delete mode 100644 construct/test.html delete mode 100644 control/control.extend.md delete mode 100644 control/control.html delete mode 100644 control/control.md delete mode 100644 control/control.processor.md delete mode 100644 control/doc/defaults.md delete mode 100644 control/doc/destroy.md delete mode 100644 control/doc/element.md delete mode 100644 control/doc/on.md delete mode 100644 control/doc/options.md delete mode 100644 control/doc/processors.md delete mode 100644 control/doc/setup.md delete mode 100644 control/eventDescription.md delete mode 100644 control/eventHandler.md delete mode 100644 control/perf.html delete mode 100644 control/test.html delete mode 100644 event/delegate/delegate.js delete mode 100644 event/delegate/delegate.md delete mode 100644 event/delegate/delegate_test.js delete mode 100644 event/delegate/test.html delete mode 100644 event/event.md delete mode 100644 event/namespace/namespace.js delete mode 100644 event/namespace/namespace.md delete mode 100644 event/namespace/namespace_test.js delete mode 100644 event/namespace/test.html delete mode 100644 event/propagate/propagate.js delete mode 100644 event/propagate/propagate.md delete mode 100644 event/propagate/propagate_test.js delete mode 100644 event/propagate/test.html delete mode 100644 event/test.html delete mode 100644 event/test_all.html delete mode 100644 list/doc/Map.md delete mode 100644 list/doc/extend.md delete mode 100644 list/doc/filter.md delete mode 100644 list/doc/list.md delete mode 100644 list/doc/prototype.attr.md delete mode 100644 list/doc/prototype.each.md delete mode 100644 list/doc/prototype.map.md delete mode 100644 list/doc/prototype.reverse.md delete mode 100644 list/doc/prototype.splice.md delete mode 100644 list/doc/reverse.html delete mode 100644 list/promise/doc/always.md delete mode 100644 list/promise/doc/done.md delete mode 100644 list/promise/doc/fail.md delete mode 100644 list/promise/doc/isPending.md delete mode 100644 list/promise/doc/isRejected.md delete mode 100644 list/promise/doc/isResolved.md delete mode 100644 list/promise/doc/promise.md delete mode 100644 list/promise/doc/reason.md delete mode 100644 list/promise/doc/state.md delete mode 100644 list/promise/doc/then.md delete mode 100644 list/promise/promise.html delete mode 100644 list/promise/promise.js delete mode 100644 list/promise/promise_test.js delete mode 100644 list/promise/test.html delete mode 100644 list/sort/20k.txt delete mode 100644 list/sort/benchmark.html delete mode 100644 list/sort/simple_sort.html delete mode 100644 list/sort/sort.html delete mode 100644 list/sort/sort.js delete mode 100644 list/sort/sort.md delete mode 100644 list/sort/sort_benchmark.js delete mode 100644 list/sort/sort_test.js delete mode 100644 list/sort/test.html delete mode 100644 list/test.html delete mode 100644 map/app/app.js delete mode 100644 map/app/app_test.js delete mode 100644 map/app/test.html delete mode 100644 map/benchmark.html delete mode 100644 map/bubble.js delete mode 100644 map/define/define.js delete mode 100644 map/define/define_test.js delete mode 100644 map/define/doc/TypeConstructor.md delete mode 100644 map/define/doc/ValueConstructor.md delete mode 100644 map/define/doc/attrDefinition.md delete mode 100644 map/define/doc/define.md delete mode 100644 map/define/doc/examples/make-model-year.html delete mode 100644 map/define/doc/get.md delete mode 100644 map/define/doc/remove.md delete mode 100644 map/define/doc/serialize.md delete mode 100644 map/define/doc/set.md delete mode 100644 map/define/doc/type.md delete mode 100644 map/define/doc/value.md delete mode 100644 map/define/test.html delete mode 100644 map/doc/map.md delete mode 100644 map/doc/prototype.attr.md delete mode 100644 map/doc/prototype.bind.md delete mode 100644 map/doc/prototype.compute-attr.md delete mode 100644 map/doc/prototype.compute.md delete mode 100644 map/doc/prototype.default-attr.md delete mode 100644 map/doc/prototype.each.md delete mode 100644 map/doc/prototype.removeAttr.md delete mode 100644 map/doc/prototype.serialize.md delete mode 100644 map/doc/prototype.unbind.md delete mode 100644 map/doc/static.keys.md delete mode 100644 map/map.html delete mode 100644 map/map_benchmark.js delete mode 100644 map/map_helpers.js delete mode 100644 map/test.html delete mode 100644 model/doc/bind.md delete mode 100644 model/doc/create.md delete mode 100644 model/doc/deprecated_model.md delete mode 100644 model/doc/deprecated_models.md delete mode 100644 model/doc/destroy.md delete mode 100644 model/doc/findAll.md delete mode 100644 model/doc/findAllData.md delete mode 100644 model/doc/findOne.md delete mode 100644 model/doc/findOneData.md delete mode 100644 model/doc/id.md delete mode 100644 model/doc/isNew.md delete mode 100644 model/doc/list.md delete mode 100644 model/doc/makeFindAll.md delete mode 100644 model/doc/makeFindOne.md delete mode 100644 model/doc/model.md delete mode 100644 model/doc/model_bind.md delete mode 100644 model/doc/model_destroy.md delete mode 100644 model/doc/model_save.md delete mode 100644 model/doc/model_unbind.md delete mode 100644 model/doc/parseModel.md delete mode 100644 model/doc/parseModels.md delete mode 100644 model/doc/removeAttr.md delete mode 100644 model/doc/resource.md delete mode 100644 model/doc/setup.md delete mode 100644 model/doc/store.md delete mode 100644 model/doc/unbind.md delete mode 100644 model/doc/update.md delete mode 100644 model/model_list.md delete mode 100644 model/test.html delete mode 100644 model/test/4.json delete mode 100644 model/test/associations_test.js delete mode 100644 model/test/create.json delete mode 100644 model/test/findAll.json delete mode 100644 model/test/people.json delete mode 100644 model/test/person.json delete mode 100644 model/test/schools.json delete mode 100644 model/test/update4.json create mode 100644 package.backup.json delete mode 100644 route/demo.html delete mode 100644 route/docs/map.html delete mode 100644 route/docs/map.md delete mode 100644 route/historytabs.html delete mode 100644 route/route.html delete mode 100644 route/route.md delete mode 100644 route/route_map.md delete mode 100644 route/test.html delete mode 100644 route/testing.html delete mode 100644 test/all-tests.js delete mode 100644 test/amd/jquery.html delete mode 100644 test/benchmarks.js delete mode 100644 test/builders/browserify/main.js delete mode 100644 test/builders/browserify/prod.html delete mode 100644 test/builders/browserify/test.js delete mode 100644 test/builders/steal-tools/app.html delete mode 100644 test/builders/steal-tools/app.js delete mode 100644 test/builders/steal-tools/bundle/components/one/one.js delete mode 100644 test/builders/steal-tools/bundle/components/one/one.stache delete mode 100644 test/builders/steal-tools/bundle/config.js delete mode 100644 test/builders/steal-tools/bundle/main.js delete mode 100644 test/builders/steal-tools/bundle/prod.html delete mode 100644 test/builders/steal-tools/bundle/template.stache delete mode 100644 test/builders/steal-tools/config.js delete mode 100644 test/builders/steal-tools/helloworld.stache delete mode 100644 test/builders/steal-tools/helpers.js delete mode 100644 test/builders/steal-tools/import/app.js delete mode 100644 test/builders/steal-tools/import/app.stache delete mode 100644 test/builders/steal-tools/import/other.js delete mode 100644 test/builders/steal-tools/import/thing.js delete mode 100644 test/builders/steal-tools/prod-bundled.html delete mode 100644 test/builders/steal-tools/prod.html delete mode 100644 test/builders/steal-tools/test.js delete mode 100644 test/compatibility/jquery.html delete mode 100644 test/demos_and_tests.html delete mode 100644 test/dev/jquery.html create mode 100644 test/index.html delete mode 100644 test/jquery.html delete mode 100644 test/performance-loading.html delete mode 100644 test/server/test.js delete mode 100644 test/templates/__configuration__-amd.html.ejs delete mode 100644 test/templates/__configuration__-compat.html.ejs delete mode 100644 test/templates/__configuration__-dev.html.ejs delete mode 100644 test/templates/__configuration__-dist.html.ejs delete mode 100644 test/templates/__configuration__.html.ejs delete mode 100644 test/templates/test.html.ejs diff --git a/.gitignore b/.gitignore index cbcd2c28f07..751d40b1bdf 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ docco test/builders/steal-tools/dist/ test/builders/steal-tools/bundle/dist/ test/builders/browserify/out.js +_backup/ \ No newline at end of file diff --git a/build.js b/build.js new file mode 100644 index 00000000000..2869d37cbf9 --- /dev/null +++ b/build.js @@ -0,0 +1,21 @@ +var stealTools = require("steal-tools"); + +stealTools.export({ + system: { + config: __dirname + "/package.json!npm" + }, + outputs: { + "+amd": {}, + "+global-js": { + ignore: function(name, load){ + return false; + } + } + } +}).catch(function(e){ + + setTimeout(function(){ + throw e; + },1); + +}); diff --git a/can.js b/can.js index 3d8bb2ec1ad..b548af03d2a 100644 --- a/can.js +++ b/can.js @@ -1,6 +1,5 @@ -var can = require('can/util/util'); -require('can/map/define/define'); -require('can/view/stache/stache'); +var can = require('./util/can'); + require('can/component/component'); -require('can/route/route'); + module.exports = can; diff --git a/component/component.js b/component/component.js index 46a20767867..1a56812124e 100644 --- a/component/component.js +++ b/component/component.js @@ -1,507 +1,3 @@ -/* jshint -W079 */ -// # can/component/component.js -// -// This implements the `can.Component` which allows you to create widgets -// that use a template, a view-model and custom tags. -// -// `can.Component` implements most of it's functionality in the `can.Component.setup` -// and the `can.Component.prototype.setup` functions. -// -// `can.Component.setup` prepares everything needed by the `can.Component.prototype.setup` -// to hookup the component. +var can = require('../util/can'); -var can = require("can/util/util"); -var elements = require("can/view/elements"); -var bindings = require("can/view/bindings/bindings"); - -require("can/view/callbacks/callbacks"); -require("can/control/control"); -require("can/map/map"); -require("can/view/stache/stache"); -require("can/util/view_model/view_model"); - -// ## Helpers -// Attribute names to ignore for setting viewModel values. -var paramReplacer = /\{([^\}]+)\}/g; - -var ComponentControl = can.Control.extend({ - // Change lookup to first look in the viewModel. - _lookup: function(options) { - return [options.scope, options, window]; - }, - _action: function(methodName, options, controlInstance) { - var hasObjectLookup, readyCompute; - - paramReplacer.lastIndex = 0; - - hasObjectLookup = paramReplacer.test(methodName); - - // If we don't have options (a `control` instance), we'll run this - // later. - if (!controlInstance && hasObjectLookup) { - return; - } else if (!hasObjectLookup) { - return can.Control._action.apply(this, arguments); - } else { - // We have `hasObjectLookup` and `controlInstance`. - - readyCompute = can.compute(function() { - var delegate; - - // Set the delegate target and get the name of the event we're listening to. - var name = methodName.replace(paramReplacer, function(matched, key) { - var value; - - // If we are listening directly on the `viewModel` set it as a delegate target. - if (key === "scope" || key === "viewModel") { - delegate = options.viewModel; - return ""; - } - - // Remove `viewModel.` from the start of the key and read the value from the `viewModel`. - key = key.replace(/^(scope|^viewModel)\./, ""); - value = can.compute.read(options.viewModel, can.compute.read.reads(key), { - // if we find a compute, we should bind on that and not read it - readCompute: false - }).value; - - // If `value` is undefined use `can.getObject` to get the value. - if (value === undefined) { - value = can.getObject(key); - } - - // If `value` is a string we just return it, otherwise we set it as a delegate target. - if (typeof value === "string") { - return value; - } else { - delegate = value; - return ""; - } - - }); - - // Get the name of the `event` we're listening to. - var parts = name.split(/\s+/g), - event = parts.pop(); - - // Return everything needed to handle the event we're listening to. - return { - processor: this.processors[event] || this.processors.click, - parts: [name, parts.join(" "), event], - delegate: delegate || undefined - }; - - }, this); - - // Create a handler function that we'll use to handle the `change` event on the `readyCompute`. - var handler = function(ev, ready) { - // unbinds the old binding - controlInstance._bindings.control[methodName](controlInstance.element); - // binds the new - controlInstance._bindings.control[methodName] = ready.processor( - ready.delegate || controlInstance.element, - ready.parts[2], ready.parts[1], methodName, controlInstance); - }; - - readyCompute.bind("change", handler); - - controlInstance._bindings.readyComputes[methodName] = { - compute: readyCompute, - handler: handler - }; - - return readyCompute(); - } - } - }, - // Extend `events` with a setup method that listens to changes in `viewModel` and - // rebinds all templated event handlers. - { - setup: function(el, options) { - this.scope = options.scope; - this.viewModel = options.viewModel; - return can.Control.prototype.setup.call(this, el, options); - }, - off: function() { - // If `this._bindings` exists we need to go through it's `readyComputes` and manually - // unbind `change` event listeners set by the controller. - if (this._bindings) { - can.each(this._bindings.readyComputes || {}, function(value) { - value.compute.unbind("change", value.handler); - }); - } - // Call `can.Control.prototype.off` function on this instance to cleanup the bindings. - can.Control.prototype.off.apply(this, arguments); - this._bindings.readyComputes = {}; - }, - destroy: function() { - can.Control.prototype.destroy.apply(this, arguments); - if (typeof this.options.destroy === 'function') { - this.options.destroy.apply(this, arguments); - } - } - }); - -/** - * @add can.Component - */ -var Component = can.Component = can.Construct.extend( - - // ## Static - /** - * @static - */ - - { - // ### setup - // - // When a component is extended, this sets up the component's internal constructor - // functions and templates for later fast initialization. - setup: function() { - can.Construct.setup.apply(this, arguments); - - // When `can.Component.setup` function is ran for the first time, `can.Component` doesn't exist yet - // which ensures that the following code is ran only in constructors that extend `can.Component`. - if (can.Component) { - var self = this, - protoViewModel = this.prototype.scope || this.prototype.viewModel; - - // Define a control using the `events` prototype property. - this.Control = ComponentControl.extend(this.prototype.events); - - // Look to convert `protoViewModel` to a Map constructor function. - if (!protoViewModel || (typeof protoViewModel === "object" && !(protoViewModel instanceof can.Map))) { - // If protoViewModel is an object, use that object as the prototype of an extended - // Map constructor function. - // A new instance of that Map constructor function will be created and - // set a the constructor instance's viewModel. - this.Map = can.Map.extend(protoViewModel || {}); - } else if (protoViewModel.prototype instanceof can.Map) { - // If viewModel is a can.Map constructor function, just use that. - this.Map = protoViewModel; - } - - // Look for default `@` values. If a `@` is found, these - // attributes string values will be set and 2-way bound on the - // component instance's viewModel. - this.attributeScopeMappings = {}; - can.each(this.Map ? this.Map.defaults : {}, function(val, prop) { - if (val === "@") { - self.attributeScopeMappings[prop] = prop; - } - }); - - // Convert the template into a renderer function. - if (this.prototype.template) { - // If `this.prototype.template` is a function create renderer from it by - // wrapping it with the anonymous function that will pass it the arguments, - // otherwise create the render from the string - if (typeof this.prototype.template === "function") { - var temp = this.prototype.template; - this.renderer = function() { - return can.view.frag(temp.apply(null, arguments)); - }; - } else { - this.renderer = can.view.stache(this.prototype.template); - } - } - - // Register this component to be created when its `tag` is found. - can.view.tag(this.prototype.tag, function(el, options) { - new self(el, options); - }); - } - - } - }, { - // ## Prototype - /** - * @prototype - */ - // ### setup - // When a new component instance is created, setup bindings, render the template, etc. - setup: function(el, componentTagData) { - - // Setup values passed to component - var initialViewModelData = {}, - component = this, - // If a template is not provided, we fall back to - // dynamic scoping regardless of settings. - lexicalContent = ((typeof this.leakScope === "undefined" ? - false : - !this.leakScope) && - !!this.template), - - // the object added to the scope - viewModel, - frag, - // an array of teardown stuff that should happen when the element is removed - teardownFunctions = [], - callTeardownFunctions = function() { - for (var i = 0, len = teardownFunctions.length; i < len; i++) { - teardownFunctions[i](); - } - }, - $el = can.$(el), - setupBindings = !can.data($el, "preventDataBindings"); - - // ## Scope - - // Add viewModel prototype properties marked with an "@" to the `initialViewModelData` object - can.each(this.constructor.attributeScopeMappings, function(val, prop) { - initialViewModelData[prop] = el.getAttribute(can.hyphenate(val)); - }); - - if (setupBindings) { - teardownFunctions.push(bindings.behaviors.viewModel(el, componentTagData, function(initialViewModelData) { - - // Make %root available on the viewModel. - initialViewModelData["%root"] = componentTagData.scope.attr("%root"); - - // Create the component's viewModel. - var protoViewModel = component.scope || component.viewModel; - if (component.constructor.Map) { - // If `Map` property is set on the constructor use it to wrap the `initialViewModelData` - viewModel = new component.constructor.Map(initialViewModelData); - } else if (protoViewModel instanceof can.Map) { - // If `component.viewModel` is instance of `can.Map` assign it to the `viewModel` - viewModel = protoViewModel; - } else if (can.isFunction(protoViewModel)) { - // If `component.viewModel` is a function, call the function and - var scopeResult = protoViewModel.call(component, initialViewModelData, componentTagData.scope, el); - - if (scopeResult instanceof can.Map) { - // If the function returns a can.Map, use that as the viewModel - viewModel = scopeResult; - } else if (scopeResult.prototype instanceof can.Map) { - // If `scopeResult` is of a `can.Map` type, use it to wrap the `initialViewModelData` - viewModel = new scopeResult(initialViewModelData); - } else { - // Otherwise extend `can.Map` with the `scopeResult` and initialize it with the `initialViewModelData` - viewModel = new(can.Map.extend(scopeResult))(initialViewModelData); - } - } - - var oldSerialize = viewModel.serialize; - viewModel.serialize = function() { - var result = oldSerialize.apply(this, arguments); - delete result["%root"]; - return result; - }; - - return viewModel; - }, initialViewModelData)); - } - - // Set `viewModel` to `this.viewModel` and set it to the element's `data` object as a `viewModel` property - this.scope = this.viewModel = viewModel; - can.data($el, "scope", this.viewModel); - can.data($el, "viewModel", this.viewModel); - can.data($el, "preventDataBindings", true); - - // Create a real Scope object out of the viewModel property - // The scope used to render the component's template. - // However, if there is no template, the "light" dom is rendered with this anyway. - var shadowScope; - if (lexicalContent) { - shadowScope = can.view.Scope.refsScope().add(this.viewModel, { - viewModel: true - }); - } else { - // if this component has a template, - // render the template with it's own Refs scope - // otherwise, just add this component's viewModel. - shadowScope = (this.constructor.renderer ? - componentTagData.scope.add(new can.view.Scope.Refs()) : - componentTagData.scope) - .add(this.viewModel, { - viewModel: true - }); - } - var options = { - helpers: {} - }, - addHelper = function(name, fn) { - options.helpers[name] = function() { - return fn.apply(viewModel, arguments); - }; - }; - - // ## Helpers - - // Setup helpers to callback with `this` as the component - can.each(this.helpers || {}, function(val, prop) { - if (can.isFunction(val)) { - addHelper(prop, val); - } - }); - - // Setup simple helpers - can.each(this.simpleHelpers || {}, function(val, prop) { - //!steal-remove-start - if (options.helpers[prop]) { - can.dev.warn('Component ' + component.tag + - ' already has a helper called ' + prop); - } - //!steal-remove-end - - // Convert the helper - addHelper(prop, can.view.simpleHelper(val)); - }); - - // ## `events` control - - // Create a control to listen to events - this._control = new this.constructor.Control(el, { - // Pass the viewModel to the control so we can listen to it's changes from the controller. - scope: this.viewModel, - viewModel: this.viewModel, - destroy: callTeardownFunctions - }); - - // ## Rendering - - // Keep a nodeList so we can kill any directly nested nodeLists within this component - var nodeList = can.view.nodeLists.register([], undefined, componentTagData.parentNodeList || true, false); - nodeList.expression = "<" + this.tag + ">"; - teardownFunctions.push(function() { - can.view.nodeLists.unregister(nodeList); - }); - - // If this component has a template (that we've already converted to a renderer) - if (this.constructor.renderer) { - // If `options.tags` doesn't exist set it to an empty object. - if (!options.tags) { - options.tags = {}; - } - - // We need be alerted to when a element is rendered so we can put the original contents of the widget in its place - options.tags.content = function contentHookup(el, contentTagData) { - // First check if there was content within the custom tag - // otherwise, render what was within , the default code. - // `componentTagData.subtemplate` is the content inside this component - var subtemplate = componentTagData.subtemplate || contentTagData.subtemplate, - renderingLightContent = subtemplate === componentTagData.subtemplate; - - if (subtemplate) { - - // `contentTagData.options` is a viewModel of helpers where `` was found, so - // the right helpers should already be available. - // However, `_tags.content` is going to point to this current content callback. We need to - // remove that so it will walk up the chain - - delete options.tags.content; - - // By default, light dom scoping is - // dynamic. This means that any `{{foo}}` - // bindings inside the "light dom" content of - // the component will have access to the - // internal viewModel. This can be overridden to be - // lexical with the leakScope option. - var lightTemplateData; - if (renderingLightContent) { - if (lexicalContent) { - // render with the same scope the component was found within. - lightTemplateData = componentTagData; - } else { - // render with the component's viewModel mixed in, however - // we still want the outer refs to be used, NOT the component's refs - // {{some value }} - // To fix this, we - // walk down the scope to the component's ref, clone scopes from that point up - // use that as the new scope. - lightTemplateData = { - scope: contentTagData.scope.cloneFromRef(), - options: contentTagData.options - }; - } - - } else { - // we are rendering default content so this content should - // use the same scope as the tag was found within. - lightTemplateData = contentTagData; - } - - if (contentTagData.parentNodeList) { - var frag = subtemplate(lightTemplateData.scope, lightTemplateData.options, contentTagData.parentNodeList); - elements.replace([el], frag); - } else { - can.view.live.replace([el], subtemplate(lightTemplateData.scope, lightTemplateData.options)); - } - - // Restore the content tag so it could potentially be used again (as in lists) - options.tags.content = contentHookup; - } - }; - // Render the component's template - frag = this.constructor.renderer(shadowScope, componentTagData.options.add(options), nodeList); - } else { - // Otherwise render the contents between the element - if (componentTagData.templateType === "legacy") { - frag = can.view.frag(componentTagData.subtemplate ? componentTagData.subtemplate(shadowScope, componentTagData.options.add(options)) : ""); - } else { - // we need to be the parent ... or we need to - frag = componentTagData.subtemplate ? - componentTagData.subtemplate(shadowScope, componentTagData.options.add(options), nodeList) : - document.createDocumentFragment(); - } - - } - // Append the resulting document fragment to the element - can.appendChild(el, frag, can.document); - - // update the nodeList with the new children so the mapping gets applied - can.view.nodeLists.update(nodeList, can.childNodes(el)); - } - }); - -// This was moved from the legacy view/scanner.js to here in prep for 3.0.0 -can.view.tag("content", function(el, tagData) { - return tagData.scope; -}); - -/** - * @description Read and write a component element's viewModel. - * - * @function can.viewModel - * @parent can.util - * @signature `can.viewModel(el[, attr[, value]])` - * @param {HTMLElement|NodeList} el can.Component element to get viewModel of. - * @param {String} [attr] Attribute name to access. - * @param {*} [val] Value to write to the viewModel attribute. - * - * @return {*} If only one argument, returns the viewModel itself. If two - * arguments are given, returns the attribute value. If three arguments - * are given, returns the element itself after assigning the value (for - * chaining). - * - * @body - * - * `can.viewModel` can be used to directly access a [can.Component]'s - * viewModel. Depending on how it's called, it can be used to get the - * entire viewModel object, read a specific property from it, or write a - * property. The property read and write features can be seen as a - * shorthand for code such as `$("my-thing").viewModel().attr("foo", val);` - * - * If using jQuery, this function is accessible as a jQuery plugin, - * with one fewer argument to the call. For example, - * `$("my-element").viewModel("name", "Whatnot");` - * - */ -// Define the `can.viewModel` function that can be used to retrieve the -// `viewModel` from the element - - -var $ = can.$; - -// If `$` has an `fn` object create the -// `scope` plugin that returns the scope object. -if ($.fn) { - $.fn.scope = $.fn.viewModel = function() { - // Just use `can.scope` as the base for this function instead - // of repeating ourselves. - return can.viewModel.apply(can, [this].concat(can.makeArray(arguments))); - }; -} - -module.exports = Component; +module.exports = can.Component = require('can-component'); diff --git a/component/component.md b/component/component.md deleted file mode 100644 index 77511bcc7b5..00000000000 --- a/component/component.md +++ /dev/null @@ -1,407 +0,0 @@ -@constructor can.Component -@download can/component -@test can/component/test.html -@parent canjs -@release 2.0 -@link ../docco/component/component.html docco - - -@description Create widgets that use a template, a view-model -and custom tags. - -@signature `< TAG BINDINGS... >` - - Create an instance of a component on a particular tag in a [can.stache] template. - In 2.3, use the [can.view.bindings bindings] syntaxes to setup bindings. - - @release 2.3 - - @param {String} TAG An HTML tag name that matches the [can.Component::tag tag] - property of the component. - - @param {can.view.bindings} BINDINGS Use the following binding syntaxes - to connect the component's [can.Component::viewModel] to the template's [can.view.Scope scope]: - - - [can.view.bindings.toChild]=[can.stache.key] - one way binding to child - - [can.view.bindings.toParent]=[can.stache.key] - one way binding to parent - - [can.view.bindings.twoWay]=[can.stache.key] - two way binding child to parent - - Example: - - ``` - - ``` - -@signature `< TAG [ATTR-NAME="{KEY}|ATTR-VALUE"] >` - - Create an instance of a component on a particular - tag in a [can.stache] template. This form of two way bindings is deprecated as of 2.3. It looked like: - - ``` - - ``` - - @release 2.1 - - Please check earlier versions of the documentation for more information. - -@signature `< TAG [ATTR-NAME=KEY|ATTR-VALUE] >` - - Create an instance of a component on a particular - tag in a [can.stache] template. Use of [can.stache] is deprecated as of - 2.3. Please check earlier versions of the documentation for more information. - - - -@body - - -## Use - -To create a `can.Component`, you must first [can.Component.extend extend] `can.Component` -with the methods and properties of how your component behaves: - - can.Component.extend({ - tag: "hello-world", - template: can.stache("{{#if visible}}{{message}}{{else}}Click me{{/if}}"), - viewModel: { - visible: false, - message: "Hello There!" - }, - events: { - click: function(){ - this.viewModel.attr("visible", !this.viewModel.attr("visible") ); - } - } - }); - -This element says "Click me" until a user clicks it and then -says "Hello There!". To create a a instance of this component on the page, -add `` to a stache template, render -the template and insert the result in the page like: - - var template = can.stache(""); - $(document.body).append( template() ); - -Check this out here: - -@demo can/component/examples/click_me.html - - -Typically, you do not append a single component at a time. Instead, -you'll render a template with many custom tags like: - - - - - - - - - - - -### Creating a can.Component - -Use [can.Component.extend] to create a `can.Component` constructor function -that will automatically get initialized whenever the component's tag is -found. - -Note that inheriting from components works differently than other CanJS APIs. You -can't call `.extend` on a particular component to create a "subclass" of that component. - -Instead, components work more like HTML elements. To reuse functionality from a base component, build on top of it with parent -components that wrap other components in their template and pass any needed viewModel properties via attributes. - -### Tag - -A component's [can.Component::tag tag] is the element node name that -the component will be created on. - - -The following matches `` elements. - - can.Component.extend({ - tag: "hello-world" - }); - -### Template - -A component's [can.Component::template template] is rendered as -the element's innerHTML. - -The following component: - - can.Component.extend({ - tag: "hello-world", - template: can.stache("

Hello World

") - }); - -Changes `` elements into: - -

Hello World

- -Use the `` tag to position the custom element's source HTML. - -The following component: - - can.Component.extend({ - tag: "hello-world", - template: can.stache("

") - }); - -Changes `Hi There` into: - -

Hi There

- -### viewModel - -A component's [can.Component::viewModel viewModel] defines a can.Map that -is used to render the component's template. The maps properties -are typically set by attribute [can.view.bindings bindings] on the custom element. -By default, every attribute's value is looked up in the parent viewModel -of the custom element and added to the viewModel object. - -The following component: - - can.Component.extend({ - tag: "hello-world", - template: can.stache("

{{message}}

") - }); - -Changes the following rendered template: - - var template = can.stache(""); - template({ - greeting: "Salutations" - }) - -Into: - -

Salutations

- -Default values can be provided. The following component: - - can.Component.extend({ - tag: "hello-world", - template: can.stache("

{{message}}

"), - viewModel: { - message: "Hi" - } - }); - -Changes the following rendered template: - - var template = can.stache(""); - template({}) - -Into: - -

Hi

- -If you want to set the string value of the attribute on viewModel, -set an attribute without any binding syntax. - -The following template, with the previous `"hello-world"` component: - - var template = can.stache(""); - template({}) - -Renders to: - -

Howdy

- -### Events - -A component's [can.Component::events events] object is used to listen to events (that are not -listened to with [can.view.bindings view bindings]). The following component -adds "!" to the message every time `` is clicked: - - can.Component.extend({ - tag: "hello-world", - template: can.stache("

{{message}}

"), - events: { - "click" : function(){ - var currentMessage = this.viewModel.attr("message"); - this.viewModel.attr("message", currentMessage+ "!") - } - } - }); - -Components have the ability to bind to special [can.events.inserted inserted] and [can.events.removed removed] events -that are called when a component's tag has been inserted into or removed from the page. - -### Helpers - -A component's [can.Component::helpers helpers] object provides [can.stache.helper stache helper] functions -that are available within the component's template. The following component -only renders friendly messages: - - can.Component.extend({ - tag: "hello-world", - template: can.stache("{{#isFriendly message}}"+ - "

{{message}}

"+ - "{{/isFriendly}}"), - helpers: { - isFriendly: function(message, options){ - if( /hi|hello|howdy/.test(message) ) { - return options.fn(); - } else { - return options.inverse(); - } - } - } - }); - - -## Examples - -Check out the following examples built with `can.Component`. - -### Tabs - -The following demos a tabs widget. Click "Add Vegetables" -to add a new tab. - -@demo can/component/examples/tabs.html - -An instance of the tabs widget is created by creating `` and `` -elements like: - - - {{#each foodTypes}} - {{content}} - {{/each}} - - -To add another panel, all we have to do is add data to `foodTypes` like: - - foodTypes.push({ - title: "Vegetables", - content: "Carrots, peas, kale" - }) - -The secret is that the `` element listens to when it is inserted -and adds its data to the tabs' list of panels with: - - this.element.parent().viewModel().addPanel( this.viewModel ); - -### TreeCombo - -The following tree combo lets people walk through a hierarchy and select locations. - -@demo can/component/examples/treecombo.html - -The secret to this widget is the viewModel's `breadcrumb` property, which is an array -of items the user has navigated through, and `selectableItems`, which represents the children of the -last item in the breadcrub. These are defined on the viewModel like: - - - breadcrumb: [], - selectableItems: function(){ - var breadcrumb = this.attr("breadcrumb"); - - // if there's an item in the breadcrumb - if(breadcrumb.attr('length')){ - - // return the last item's children - return breadcrumb.attr(""+(breadcrumb.length-1)+'.children'); - } else{ - - // return the top list of items - return this.attr('items'); - } - } - -When the "+" icon is clicked next to each item, the viewModel's `showChildren` method is called, which -adds that item to the breadcrumb like: - - showChildren: function( item, ev ) { - ev.stopPropagation(); - this.attr('breadcrumb').push(item) - }, - -### Paginate - -The following example shows 3 -widget-like components: a grid, next / prev buttons, and a page count indicator. And, -it shows an application component that puts them all together. - -@demo can/component/examples/paginate.html - -This demo uses a `Paginate` can.Map to assist with maintaining a paginated state: - - var Paginate = can.Map.extend({ - ... - }); - -The `app` component, using the [can.Map.define define plugin], creates an instance of the `Paginate` model -and a `websitesPromise` that represents a request for the Websites -that should be displayed. - - viewModel: { - define: { - paginate: { - value: function() { - return new Paginate({ - limit: 5 - }); - } - }, - websitesPromise: { - get: function() { - var params = { - limit: this.attr('paginate.limit'), - offset: this.attr('paginate.offset') - }, - websitesPromise = Website.findAll(params), - self = this; - - websitesPromise.then(function(websites) { - self.attr('paginate.count', websites.count); - }); - - return websitesPromise; - } - } - } - } - -The `app` control passes paginate, paginate's values, and websitesPromise to -its sub-components: - - - - {{#each items}} - - {{name}} - {{url}} - - {{/each}} - - - - - -## IE 8 Support - -While CanJS does support Internet Explorer 8 out of the box, if you decide -to use `can.Component` then you will need to include [HTML5 Shiv](https://github.com/aFarkas/html5shiv) -in order for your custom tags to work properly. - -For namespaced tag names (e.g. ``) and hyphenated tag names (e.g. ``) to work properly, you will -need to use version 3.7.2 or later. - -## Videos - -Watch this video for an overview of can.Component, why you should use it, and a hello world example: - - - -This video provides a more in depth overview of the API and goes over several examples of can.Components: - - - -Note: the videos above reference the `scope` property, which was replaced by the [can.Component::viewModel viewModel] property in 2.2. diff --git a/component/component_bindings_test.js b/component/component_bindings_test.js deleted file mode 100644 index 327685808fc..00000000000 --- a/component/component_bindings_test.js +++ /dev/null @@ -1,1720 +0,0 @@ -/* jshint -W003 */ -require("can/map/define/define"); -require("can/component/component"); -require("can/view/stache/stache"); -require("can/route/route"); -require("steal-qunit"); - -var innerHTML = function(node){ - if("innerHTML" in node) { - return node.innerHTML; - } -}; - -function makeTest(name, doc) { - var oldDoc; - QUnit.module(name, { - setup: function () { - oldDoc = can.document; - can.document = doc; - - if(doc) { - this.fixture = doc.createElement("div"); - doc.body.appendChild(this.fixture); - this.$fixture = can.$(this.fixture); - } else { - this.fixture = can.$("#qunit-fixture")[0]; - this.$fixture = can.$("#qunit-fixture"); - } - }, - teardown: function(){ - can.document = oldDoc; - - if(doc) { - doc.body.removeChild(this.fixture); - } - } - }); - - var Paginate = can.Map.extend({ - count: Infinity, - offset: 0, - limit: 100, - // Prevent negative counts - setCount: function (newCount, success, error) { - return newCount < 0 ? 0 : newCount; - }, - // Prevent negative offsets - setOffset: function (newOffset) { - return newOffset < 0 ? - 0 : - Math.min(newOffset, !isNaN(this.count - 1) ? - this.count - 1 : - Infinity); - }, - // move next - next: function () { - this.attr('offset', this.offset + this.limit); - }, - prev: function () { - this.attr('offset', this.offset - this.limit); - }, - canNext: function () { - return this.attr('offset') < this.attr('count') - - this.attr('limit'); - }, - canPrev: function () { - return this.attr('offset') > 0; - }, - page: function (newVal) { - if (newVal === undefined) { - return Math.floor(this.attr('offset') / this.attr('limit')) + 1; - } else { - this.attr('offset', (parseInt(newVal) - 1) * this.attr('limit')); - } - }, - pageCount: function () { - return this.attr('count') ? - Math.ceil(this.attr('count') / this.attr('limit')) : null; - } - }); - - test("basic tabs", function () { - - // new Tabs() .. - can.Component.extend({ - tag: "tabs", - template: can.stache("
    " + - "{{#panels}}" + - "
  • {{title}}
  • " + - "{{/panels}}" + - "
" + - ""), - viewModel: { - panels: [], - addPanel: function (panel) { - - if (this.attr("panels") - .length === 0) { - this.makeActive(panel); - } - this.attr("panels") - .push(panel); - }, - removePanel: function (panel) { - var panels = this.attr("panels"); - can.batch.start(); - panels.splice(panels.indexOf(panel), 1); - if (panel === this.attr("active")) { - if (panels.length) { - this.makeActive(panels[0]); - } else { - this.removeAttr("active"); - } - } - can.batch.stop(); - }, - makeActive: function (panel) { - this.attr("active", panel); - this.attr("panels") - .each(function (panel) { - panel.attr("active", false); - }); - panel.attr("active", true); - - }, - // this is viewModel, not stache - // consider removing viewModel as arg - isActive: function (panel) { - return this.attr('active') === panel; - } - } - }); - can.Component.extend({ - // make sure works - template: can.stache("{{#if active}}{{/if}}"), - tag: "panel", - viewModel: { - active: false, - title: "@" - }, - events: { - " inserted": function () { - can.viewModel(this.element[0].parentNode) - .addPanel(this.viewModel); - }, - " removed": function () { - if (!can.viewModel(this.element[0].parentNode)) { - console.log("bruke"); - } - can.viewModel(this.element[0].parentNode) - .removePanel(this.viewModel); - } - } - }); - - var template = can.stache("{{#each foodTypes}}{{content}}{{/each}}"); - - var foodTypes = new can.List([{ - title: "Fruits", - content: "oranges, apples" - }, { - title: "Breads", - content: "pasta, cereal" - }, { - title: "Sweets", - content: "ice cream, candy" - }]); - - var frag = template({ - foodTypes: foodTypes - }); - can.append(this.$fixture, frag); - - var testArea = this.fixture, - lis = testArea.getElementsByTagName("li"); - - equal(lis.length, 3, "three lis added"); - - foodTypes.each(function (type, i) { - equal(innerHTML(lis[i]), type.attr("title"), "li " + i + " has the right content"); - }); - - foodTypes.push({ - title: "Vegies", - content: "carrots, kale" - }); - - lis = testArea.getElementsByTagName("li"); - - //lis = testArea.getElementsByTagName("li"); - equal(lis.length, 4, "li added"); - - - foodTypes.each(function (type, i) { - equal( innerHTML(lis[i]), type.attr("title"), "li " + i + " has the right content"); - }); - - equal(testArea.getElementsByTagName("panel") - .length, 4, "panel added"); - - foodTypes.shift(); - - lis = testArea.getElementsByTagName("li"); - - equal(lis.length, 3, "removed li after shifting a foodType"); - - foodTypes.each(function (type, i) { - equal( innerHTML(lis[i]), type.attr("title"), "li " + i + " has the right content"); - }); - - // test changing the active element - var panels = testArea.getElementsByTagName("panel"); - - equal(lis[0].className, "active", "the first element is active"); - equal(innerHTML( panels[0] ), "pasta, cereal", "the first content is shown"); - equal(innerHTML( panels[1] ), "", "the second content is removed"); - - can.trigger(lis[1], "click"); - lis = testArea.getElementsByTagName("li"); - - equal(lis[1].className, "active", "the second element is active"); - equal(lis[0].className, "", "the first element is not active"); - - equal( innerHTML( panels[0]), "", "the second content is removed"); - equal( innerHTML( panels[1]), "ice cream, candy", "the second content is shown"); - - }); - - - test("lexical scoping", function() { - can.Component.extend({ - tag: "hello-world", - leakScope: false, - template: can.stache("{{greeting}} World{{exclamation}}"), - viewModel: { - greeting: "Hello" - } - }); - var template = can.stache("{{greeting}}"); - - - var frag = template({ - greeting: "World", - exclamation: "!" - }); - - var hello = frag.firstChild; - - equal(can.trim( innerHTML(hello) ), "Hello World"); - - can.Component.extend({ - tag: "hello-world-no-template", - leakScope: false, - viewModel: {greeting: "Hello"} - }); - template = can.stache("{{greeting}}"); - - frag = template({ - greeting: "World", - exclamation: "!" - }); - - hello = frag.firstChild; - - equal(can.trim( innerHTML(hello) ), "Hello", - "If no template is provided to can.Component, treat bindings as dynamic."); - }); - - test("dynamic scoping", function() { - - can.Component.extend({ - tag: "hello-world", - leakScope: true, - template: can.stache("{{greeting}} World{{exclamation}}"), - viewModel: {greeting: "Hello"} - }); - - var template = can.stache("{{greeting}}"); - var frag = template({ - greeting: "World", - exclamation: "!" - }); - - var hello = frag.firstChild; - - equal( can.trim( innerHTML(hello) ) , "Hello Hello!"); - - }); - - test("treecombo", function () { - - can.Component.extend({ - tag: "treecombo", - template: can.stache("" + - "
    " + - "" + - "{{#selectableItems}}" + - "
  • " + - "" + - "{{title}}" + - "{{#if children.length}}" + - "" + - "{{/if}}" + - "
  • " + - "{{/selectableItems}}" + - "
    " + - "
"), - viewModel: { - items: [], - breadcrumb: [], - selected: [], - selectableItems: function () { - var breadcrumb = this.attr("breadcrumb"); - - // if there's an item in the breadcrumb - if (breadcrumb.attr('length')) { - - // return the last item's children - return breadcrumb.attr("" + (breadcrumb.length - 1) + '.children'); - } else { - - // return the top list of items - return this.attr('items'); - } - }, - showChildren: function (item, el, ev) { - ev.stopPropagation(); - this.attr('breadcrumb') - .push(item); - }, - emptyBreadcrumb: function () { - this.attr("breadcrumb") - .attr([], true); - }, - updateBreadcrumb: function (item) { - var breadcrumb = this.attr("breadcrumb"), - index = breadcrumb.indexOf(item); - breadcrumb.splice(index + 1, breadcrumb.length - index - 1); - }, - toggle: function (item) { - var selected = this.attr('selected'), - index = selected.indexOf(item); - if (index === -1) { - selected.push(item); - } else { - selected.splice(index, 1); - } - } - }, - helpers: { - isSelected: function (options) { - if (this.attr("selected") - .indexOf(options.context) > -1) { - return options.fn(); - } else { - return options.inverse(); - } - } - } - }); - - var template = can.stache(""); - - var base = new can.Map({}); - - var frag = template(base); - var root = doc.createElement("div"); - root.appendChild(frag); - - var items = [{ - id: 1, - title: "Midwest", - children: [{ - id: 5, - title: "Illinois", - children: [{ - id: 23423, - title: "Chicago" - }, { - id: 4563, - title: "Springfield" - }, { - id: 4564, - title: "Naperville" - }] - }, { - id: 6, - title: "Wisconsin", - children: [{ - id: 232423, - title: "Milwaulkee" - }, { - id: 45463, - title: "Green Bay" - }, { - id: 45464, - title: "Madison" - }] - }] - }, { - id: 2, - title: "East Coast", - children: [{ - id: 25, - title: "New York", - children: [{ - id: 3413, - title: "New York" - }, { - id: 4613, - title: "Rochester" - }, { - id: 4516, - title: "Syracuse" - }] - }, { - id: 6, - title: "Pennsylvania", - children: [{ - id: 2362423, - title: "Philadelphia" - }, { - id: 454663, - title: "Harrisburg" - }, { - id: 454664, - title: "Scranton" - }] - }] - }]; - - stop(); - - setTimeout(function () { - - base.attr('locations', items); - - var itemsList = base.attr('locations'); - - // check that the DOM is right - var treecombo = root.firstChild, - breadcrumb = treecombo.firstChild, - breadcrumbLIs = function(){ - return breadcrumb.getElementsByTagName('li'); - }, - options = treecombo.lastChild, - optionsLis = function(){ - return options.getElementsByTagName('li'); - }; - - equal(breadcrumbLIs().length, 1, "Only the default title is shown"); - - equal( innerHTML( breadcrumbLIs()[0] ) , "Locations", "The correct title from the attribute is shown"); - - equal( itemsList.length, optionsLis().length, "first level items are displayed"); - - // Test toggling selected, first by clicking - can.trigger(optionsLis()[0], "click"); - - equal(optionsLis()[0].className, "active", "toggling something not selected adds active"); - - ok(optionsLis()[0].getElementsByTagName('input')[0].checked, "toggling something not selected checks checkbox"); - equal(can.viewModel(treecombo, "selected") - .length, 1, "there is one selected item"); - equal(can.viewModel(treecombo, "selected.0"), itemsList.attr("0"), "the midwest is in selected"); - - // adjust the state and everything should update - can.viewModel(treecombo, "selected") - .pop(); - equal(optionsLis()[0].className, "", "toggling something not selected adds active"); - - // Test going in a location - can.trigger(optionsLis()[0].getElementsByTagName('button')[0], "click"); - equal(breadcrumbLIs().length, 2, "Only the default title is shown"); - equal(innerHTML(breadcrumbLIs()[1]), "Midwest", "The breadcrumb has an item in it"); - ok(/Illinois/.test( innerHTML(optionsLis()[0])), "A child of the top breadcrumb is displayed"); - - // Test going in a location without children - can.trigger(optionsLis()[0].getElementsByTagName('button')[0], "click"); - ok(/Chicago/.test( innerHTML(optionsLis()[0] ) ), "A child of the top breadcrumb is displayed"); - ok(!optionsLis()[0].getElementsByTagName('button') - .length, "no show children button"); - - // Test poping off breadcrumb - can.trigger(breadcrumbLIs()[1], "click"); - equal(innerHTML(breadcrumbLIs()[1]), "Midwest", "The breadcrumb has an item in it"); - ok(/Illinois/.test( innerHTML( optionsLis()[0])), "A child of the top breadcrumb is displayed"); - - // Test removing everything - can.trigger(breadcrumbLIs()[0], "click"); - equal(breadcrumbLIs().length, 1, "Only the default title is shown"); - equal( innerHTML(breadcrumbLIs()[0]), "Locations", "The correct title from the attribute is shown"); - - start(); - - }, 100); - - }); - - test("deferred grid", function () { - - // This test simulates a grid that reads a `deferreddata` property for - // items and displays them. - // If `deferreddata` is a deferred, it waits for those items to resolve. - // The grid also has a `waiting` property that is true while the deferred is being resolved. - - can.Component.extend({ - tag: "grid", - viewModel: { - items: [], - waiting: true - }, - template: can.stache("
"), - events: { - init: function () { - this.update(); - }, - "{viewModel} deferreddata": "update", - update: function () { - var deferred = this.viewModel.attr('deferreddata'), - viewModel = this.viewModel; - - if (can.isDeferred(deferred)) { - this.viewModel.attr("waiting", true); - deferred.then(function (items) { - viewModel.attr('items') - .attr(items, true); - }); - } else { - viewModel.attr('items') - .attr(deferred, true); - } - }, - "{items} change": function () { - this.viewModel.attr("waiting", false); - } - } - }); - - // The context object has a `set` property and a - // deferredData property that reads from it and returns a new deferred. - var SimulatedScope = can.Map.extend({ - set: 0, - deferredData: function () { - - var deferred = new can.Deferred(); - var set = this.attr('set'); - if (set === 0) { - setTimeout(function () { - deferred.resolve([{ - first: "Justin", - last: "Meyer" - }]); - }, 100); - } else if (set === 1) { - setTimeout(function () { - deferred.resolve([{ - first: "Brian", - last: "Moschel" - }]); - }, 100); - } - return deferred; - } - }); - var viewModel = new SimulatedScope(); - - var template = can.stache("" + - "{{#each items}}" + - "" + - "{{first}}" + - "{{last}}" + - "" + - "{{/each}}" + - ""); - - can.append(this.$fixture, template({ - viewModel: viewModel - })); - - var gridScope = can.viewModel(this.fixture.firstChild); - - equal(gridScope.attr("waiting"), true, "The grid is initially waiting on the deferreddata to resolve"); - - stop(); - var self = this; - - var waitingHandler = function() { - gridScope.unbind('waiting', waitingHandler); - - setTimeout(function () { - var tds = self.fixture.getElementsByTagName("td"); - equal(tds.length, 2, "there are 2 tds"); - - gridScope.bind("waiting", function (ev, newVal) { - if (newVal === false) { - setTimeout(function () { - equal(innerHTML(tds[0]), "Brian", "td changed to brian"); - start(); - }, 10); - - } - }); - - // update set to change the deferred. - viewModel.attr("set", 1); - - }, 10); - }; - - gridScope.bind('waiting', waitingHandler); - }); - - test("nextprev", function () { - - can.Component.extend({ - tag: "next-prev", - template: can.stache( - 'Prev' + - 'Next') - }); - - var paginator = new Paginate({ - limit: 20, - offset: 0, - count: 100 - }); - var template = can.stache(""); - - var frag = template({ - paginator: paginator - }); - var nextPrev = frag.firstChild; - - var prev = nextPrev.firstChild, - next = nextPrev.lastChild; - - ok(!/enabled/.test( prev.className ), "prev is not enabled"); - ok(/enabled/.test( next.className ), "next is enabled"); - - can.trigger(next, "click"); - ok(/enabled/.test( prev.className ), "prev is enabled"); - }); - - test("page-count", function () { - - can.Component.extend({ - tag: "page-count", - template: can.stache('Page {{page}}.') - }); - - var paginator = new Paginate({ - limit: 20, - offset: 0, - count: 100 - }); - - var template = can.stache(""); - - var frag = template( new can.Map({ - paginator: paginator - }) ); - - var span = frag.firstChild.getElementsByTagName("span")[0]; - - equal(span.firstChild.nodeValue, "1"); - paginator.next(); - equal(span.firstChild.nodeValue, "2"); - paginator.next(); - equal(span.firstChild.nodeValue, "3"); - - }); - - test("hello-world and whitespace around custom elements", function () { - - can.Component.extend({ - tag: "hello-world", - template: can.stache("{{#if visible}}{{message}}{{else}}Click me{{/if}}"), - viewModel: { - visible: false, - message: "Hello There!" - }, - events: { - click: function () { - this.viewModel.attr("visible", true); - } - } - }); - - var template = can.stache(" "); - var frag = template({}); - - var helloWorld = frag.childNodes.item(1); - - can.trigger(can.$(helloWorld), "click"); - - equal( innerHTML(helloWorld) , "Hello There!"); - - }); - - test("self closing content tags", function () { - - can.Component.extend({ - "tag": "my-greeting", - template: can.stache("

"), - viewModel: { - title: "can.Component" - } - }); - - var template = can.stache("{{site}} - {{title}}"); - - var frag = template({ - site: "CanJS" - }); - - equal(frag.firstChild.getElementsByTagName("span") - .length, 1, "there is an h1"); - }); - - test("can.viewModel utility", function() { - can.Component({ - tag: "my-taggy-tag", - template: can.stache("

hello

"), - viewModel: { - foo: "bar" - } - }); - - var frag = can.stache("")(); - - - var el = can.$(frag.firstChild); - - equal(can.viewModel(el), can.data(el, "viewModel"), "one argument grabs the viewModel object"); - equal(can.viewModel(el, "foo"), "bar", "two arguments fetches a value"); - can.viewModel(el, "foo", "baz"); - equal(can.viewModel(el, "foo"), "baz", "Three arguments sets the value"); - if (window.$ && $.fn) { - el = $(frag.firstChild); - equal(el.viewModel(), can.data(el, "viewModel"), "jQuery helper grabs the viewModel object"); - equal(el.viewModel("foo"), "baz", "jQuery helper with one argument fetches a property"); - equal(el.viewModel("foo", "bar").get(0), el.get(0), "jQuery helper returns the element"); - equal(el.viewModel("foo"), "bar", "jQuery helper with two arguments sets the property"); - } - }); - - test("can.viewModel backwards compatible with can.scope", function() { - equal(can.viewModel, can.scope, "can helper"); - if (window.$ && $.fn) { - equal($.scope, $.viewModel, "jQuery helper"); - } - }); - - test("can.viewModel creates one if it doesn't exist", function(){ - var frag = can.stache("
")(); - - var el = can.$(frag.firstChild); - var viewModel = can.viewModel(el); - ok(!!viewModel, "viewModel created where it didn't exist."); - equal(viewModel, can.data(el, "viewModel"), "viewModel is in the data."); - }); - - test('setting passed variables - two way binding', function () { - can.Component.extend({ - tag: "my-toggler", - template: can.stache("{{#if visible}}{{/if}}"), - viewModel: { - visible: true, - show: function () { - this.attr('visible', true); - }, - hide: function () { - this.attr("visible", false); - } - } - }); - - can.Component.extend({ - tag: "my-app", - viewModel: { - visible: true, - show: function () { - this.attr('visible', true); - } - } - }); - var template = can.stache("" + - '{{^visible}}{{/visible}}' + - '' + - 'content' + - '' + - '' + - ''); - - var frag = template({}); - - var myApp = frag.firstChild, - buttons = myApp.getElementsByTagName("button"); - - equal( buttons.length, 1, "there is one button"); - equal( innerHTML(buttons[0]) , "hide", "the button's text is hide"); - - can.trigger(buttons[0], "click"); - buttons = myApp.getElementsByTagName("button"); - - equal(buttons.length, 1, "there is one button"); - equal(innerHTML(buttons[0]), "show", "the button's text is show"); - - can.trigger(buttons[0], "click"); - buttons = myApp.getElementsByTagName("button"); - - equal(buttons.length, 1, "there is one button"); - equal(innerHTML(buttons[0]), "hide", "the button's text is hide"); - }); - - test("helpers reference the correct instance (#515)", function () { - expect(2); - can.Component({ - tag: 'my-text', - template: can.stache('

{{valueHelper}}

'), - helpers: { - valueHelper: function () { - return this.attr('value'); - } - } - }); - - var template = can.stache(''); - - var frag = template({}); - - equal(frag.firstChild.firstChild.firstChild.nodeValue, 'value1'); - equal(frag.lastChild.firstChild.firstChild.nodeValue, 'value2'); - }); - - test('access hypenated attributes via camelCase or hypenated', function () { - can.Component({ - tag: 'hyphen', - viewModel: { - }, - template: can.stache('

{{valueHelper}}

'), - helpers: { - valueHelper: function () { - return this.attr('camelCase'); - } - } - }); - - var template = can.stache(''); - var frag = template({}); - - - equal(frag.firstChild.firstChild.firstChild.nodeValue, 'value1'); - - }); - - test("a map as viewModel", function () { - - var me = new can.Map({ - name: "Justin" - }); - - can.Component.extend({ - tag: 'my-viewmodel', - template: can.stache("{{name}}}"), - viewModel: me - }); - - var template = can.stache(''); - equal(template().firstChild.firstChild.nodeValue, "Justin"); - - }); - - test("content in a list", function () { - var template = can.stache('{{name}}'); - - can.Component.extend({ - tag: "my-list", - template: can.stache("{{#each items}}
  • {{/each}}"), - viewModel: { - items: new can.List([{ - name: "one" - }, { - name: "two" - }]) - } - }); - - var lis = template() - .firstChild.getElementsByTagName("li"); - - equal(innerHTML(lis[0]), "one", "first li has correct content"); - equal(innerHTML(lis[1]), "two", "second li has correct content"); - - }); - - test("don't update computes unnecessarily", function () { - var sourceAge = 30, - timesComputeIsCalled = 0; - - var age = can.compute(function (newVal) { - timesComputeIsCalled++; - if (timesComputeIsCalled === 1) { - ok(true, "reading initial value to set as years"); - } else if (timesComputeIsCalled === 2) { - equal(newVal, 31, "updating value to 31"); - } else if (timesComputeIsCalled === 3) { - ok(true, "called back another time after set to get the value"); - } else { - ok(false, "You've called the callback " + timesComputeIsCalled + " times"); - } - - if (arguments.length) { - sourceAge = newVal; - } else { - return sourceAge; - } - }); - - can.Component.extend({ - tag: "age-er" - }); - - var template = can.stache(""); - - template({ - age: age - }); - - age(31); - - }); - - test("component does not respect can.compute passed via attributes (#540)", function () { - - var data = { - compute: can.compute(30) - }; - - can.Component.extend({ - tag: "my-component", - template: can.stache("{{blocks}}") - }); - - var template = can.stache(""); - - var frag = template(data); - - equal( innerHTML(frag.firstChild.firstChild), "30"); - - }); - - test("defined view models (#563)", function () { - - var HelloWorldModel = can.Map.extend({ - visible: true, - toggle: function () { - this.attr("visible", !this.attr("visible")); - } - }); - - can.Component.extend({ - tag: "my-helloworld", - template: can.stache("

    {{#if visible}}visible{{else}}invisible{{/if}}

    "), - viewModel: HelloWorldModel - }); - - var template = can.stache(""); - - var frag = template({}); - - equal( innerHTML(frag.firstChild.firstChild), "visible"); - }); - - test("viewModel not rebound correctly (#550)", function () { - - var nameChanges = 0; - - can.Component.extend({ - tag: "viewmodel-rebinder", - events: { - "{name} change": function () { - nameChanges++; - } - } - }); - - var template = can.stache(""); - - var frag = template(); - var viewModel = can.viewModel(can.$(frag.firstChild)); - - var n1 = can.compute(), - n2 = can.compute(); - - viewModel.attr("name", n1); - - n1("updated"); - - viewModel.attr("name", n2); - - n2("updated"); - - - equal(nameChanges, 2); - }); - - test("content extension stack overflow error", function () { - - can.Component({ - tag: 'outer-tag', - template: can.stache('inner-tag CONTENT ') - }); - - can.Component({ - tag: 'inner-tag', - template: can.stache('inner-tag TEMPLATE ') - }); - - // currently causes Maximum call stack size exceeded - var template = can.stache("outer-tag CONTENT"); - - // RESULT = inner-tag TEMPLATE inner-tag CONTENT outer-tag CONTENT - - var frag = template(); - - equal( innerHTML(frag.firstChild.firstChild), 'inner-tag TEMPLATE inner-tag CONTENT outer-tag CONTENT'); - - }); - - test("inserted event fires twice if component inside live binding block", function () { - - var inited = 0, - inserted = 0; - - can.Component({ - tag: 'child-tag', - - viewModel: { - init: function () { - inited++; - } - }, - events: { - ' inserted': function () { - inserted++; - } - } - }); - - can.Component({ - tag: 'parent-tag', - - template: can.stache('{{#shown}}{{/shown}}'), - - viewModel: { - shown: false - }, - events: { - ' inserted': function () { - this.viewModel.attr('shown', true); - } - } - }); - - var frag = can.stache("")({}); - - can.append(this.$fixture, frag); - - equal(inited, 1); - equal(inserted, 1); - - }); - - - test("@ keeps properties live now", function () { - - can.Component.extend({ - tag: "attr-fun", - template: can.stache("

    {{fullName}}

    "), - viewModel: { - fullName: function () { - return this.attr("firstName") + " " + this.attr("lastName"); - } - } - }); - - var frag = can.stache("")(); - - var attrFun = frag.firstChild; - - this.fixture.appendChild(attrFun); - - equal( innerHTML(attrFun.firstChild), "Justin Meyer"); - - can.attr.set(attrFun, "first-name", "Brian"); - - stop(); - - setTimeout(function () { - equal(attrFun.firstChild.firstChild.nodeValue, "Brian Meyer"); - start(); - }, 100); - - }); - - test("id and class should work now (#694)", function () { - can.Component.extend({ - tag: "stay-classy", - viewModel: { - notid: "foo", - notclass: 5, - notdataviewid: {} - } - }); - - var data = { - idData: "id-success", - classData: "class-success" - }; - - var frag = can.stache( - "")(data); - - var stayClassy = frag.firstChild; - - can.append(this.$fixture, frag); - - var viewModel = can.viewModel(stayClassy); - - equal(viewModel.attr("id"), "id-success"); - equal(viewModel.attr("class"), "class-success"); - }); - - test("Component can-click method should be not called while component's init", function () { - - var called = false; - - can.Component.extend({ - tag: "child-tag" - }); - - can.Component.extend({ - tag: "parent-tag", - template: can.stache(''), - viewModel: { - method: function () { - called = true; - } - } - }); - - can.stache('')(); - - equal(called, false); - }); - - - test('Same component tag nested', function () { - can.Component({ - 'tag': 'my-tag', - template: can.stache('

    ') - }); - //simplest case - var template = can.stache('
    OutterInner
    '); - //complex case - var template2 = can.stache('
    3210
    '); - //edge case for new logic (same custom tag at same depth as one previously encountered) - var template3 = can.stache('
    FirstSecond
    '); - - - equal( template({}).firstChild.getElementsByTagName('p').length, 2, 'proper number of p tags'); - - equal( template2({}).firstChild.getElementsByTagName('p').length, 4, 'proper number of p tags'); - - equal( template3({}).firstChild.getElementsByTagName('p').length, 2, 'proper number of p tags'); - - }); - - test("Component events bind to window", function(){ - window.tempMap = new can.Map(); - - can.Component.extend({ - tag: "window-events", - events: { - "{tempMap} prop": function(){ - ok(true, "called templated event"); - } - } - }); - - var template = can.stache(''); - - template(); - - window.tempMap.attr("prop","value"); - - // IE 6-8 throws an error when deleting globals created via assignment: - // http://perfectionkills.com/understanding-delete/#ie_bugs - window.tempMap = undefined; - try{ - delete window.tempMap; - } catch(e) {} - - }); - - test("can.Construct are passed normally", function(){ - var Constructed = can.Construct.extend({foo:"bar"},{}); - - can.Component.extend({ - tag: "con-struct", - template: can.stache("{{con.foo}}") - }); - - var stached = can.stache(""); - - var res = stached({ - Constructed: Constructed - }); - equal(innerHTML(res.firstChild), "bar"); - - - }); - - - test("passing id works now", function(){ - - can.Component.extend({ - tag: 'my-thing', - template: can.stache('hello') - }); - var stache = can.stache(""); - var frag = stache(new can.Map({productId: 123})); - equal( can.viewModel(frag.firstChild).attr("id"), 123); - }); - - - test("stache conditionally nested components calls inserted once (#967)", function(){ - expect(1); - - can.Component.extend({ - tag: "can-parent-stache", - viewModel: { - shown: true - }, - template: can.stache("{{#if shown}}{{/if}}") - }); - can.Component.extend({ - tag: "can-child", - events: { - inserted: function(){ - this.viewModel.attr('bar', 'foo'); - ok(true, "called inserted once"); - } - } - }); - - var template = can.stache(""); - - can.append(this.$fixture, template()); - }); - - test("hyphen-less tag names", function () { - var template = can.stache(''); - can.Component.extend({ - tag: "foobar", - template: can.stache("
    {{name}}
    "), - viewModel: { - name: "Brian" - } - }); - - equal(template().lastChild.firstChild.firstChild.nodeValue, "Brian"); - - }); - - test('nested component within an #if is not live bound(#1025)', function() { - can.Component.extend({ - tag: 'parent-component', - template: can.stache('{{#if shown}}{{/if}}'), - viewModel: { - shown: false - } - }); - - can.Component.extend({ - tag: 'child-component', - template: can.stache('Hello world.') - }); - - var template = can.stache(''); - var frag = template({}); - - equal( innerHTML(frag.firstChild), '', 'child component is not inserted'); - can.viewModel(frag.firstChild).attr('shown', true); - - equal( innerHTML(frag.firstChild.firstChild), 'Hello world.', 'child component is inserted'); - can.viewModel(frag.firstChild).attr('shown', false); - - equal( innerHTML(frag.firstChild), '', 'child component is removed'); - }); - - test('component does not update viewModel on id, class, and data-view-id attribute changes (#1079)', function(){ - - can.Component.extend({ - tag:'x-app' - }); - - var frag=can.stache('')({}); - - var el = frag.firstChild; - var viewModel = can.viewModel(el); - - // element must be inserted, otherwise attributes event will not be fired - can.append(this.$fixture,frag); - - // update the class - can.addClass(can.$(el),"foo"); - - stop(); - setTimeout(function(){ - equal(viewModel.attr('class'),undefined, "the viewModel is not updated when the class attribute changes"); - start(); - },20); - - }); - - test('viewModel objects with Constructor functions as properties do not get converted (#1261)', 1, function(){ - stop(); - - var Test = can.Map.extend({ - test: 'Yeah' - }); - - can.Component.extend({ - tag:'my-app', - viewModel: { - MyConstruct: Test - }, - events: { - '{MyConstruct} something': function() { - ok(true, 'Event got triggered'); - start(); - } - } - }); - - var frag = can.stache('')(); - - // element must be inserted, otherwise attributes event will not be fired - can.append(this.$fixture,frag); - - can.trigger(Test, 'something'); - }); - - test('removing bound viewModel properties on destroy #1415', function(){ - var state = new can.Map({ - product: { - id: 1, - name: "Tom" - } - }); - - can.Component.extend({ - tag: 'destroyable-component', - events: { - destroy: function(){ - - this.viewModel.attr('product', null); - } - } - }); - - var frag = can.stache('')(state); - - // element must be inserted, otherwise attributes event will not be fired - can.append(this.$fixture,frag); - - can.remove( can.$(this.fixture.firstChild) ); - - ok(state.attr('product') == null, 'product was removed'); - }); - - test('changing viewModel property rebinds {viewModel.<...>} events (#1529)', 2, function(){ - can.Component.extend({ - tag: 'rebind-viewmodel', - events: { - inserted: function(){ - this.viewModel.attr('item', {}); - }, - '{scope.item} change': function() { - ok(true, 'Change event on scope'); - }, - '{viewModel.item} change': function() { - ok(true, 'Change event on viewModel'); - } - } - }); - var frag = can.stache('')(); - var rebind = frag.firstChild; - can.append(this.$fixture, rebind); - - can.viewModel(can.$(rebind)).attr('item.name', 'CDN'); - }); - - - - test('Component two way binding loop (#1579)', function() { - var changeCount = 0; - - can.Component.extend({ - tag: 'product-swatch-color', - viewModel: { - tag: 'product-swatch-color' - } - }); - - - can.Component.extend({ - tag: 'product-swatch', - template: can.stache(''), - viewModel: can.Map.extend({ - tag: "product-swatch", - define: { - variations: { - set: function(variations) { - if(changeCount > 500) { - return; - } - changeCount++; - return new can.List(variations.attr()); - } - } - } - }) - }); - - var frag = can.stache('')(), - productSwatch = frag.firstChild; - - can.batch.start(); - can.viewModel( can.$(productSwatch) ).attr('variations', new can.List()); - can.batch.stop(); - - - ok(changeCount < 500, "more than 500 events"); - }); - - test('DOM trees not releasing when referencing can.Map inside can.Map in template (#1593)', function() { - var baseTemplate = can.stache('{{#if show}}{{/if}}'), - show = can.compute(true), - state = new can.Map({ - inner: 1 - }); - - var removeCount = 0; - - can.Component.extend({ - tag: 'my-inside', - events: { - removed: function() { - removeCount++; - } - } - }); - - can.Component.extend({ - tag: 'my-outside', - template: can.stache('{{#if state.inner}}{{/if}}') - }); - - can.append( this.$fixture, baseTemplate({ - show: show, - state: state - }) ); - - show(false); - state.removeAttr('inner'); - - equal(removeCount, 1, 'internal removed once'); - - show(true); - state.attr('inner', 2); - - state.removeAttr('inner'); - - equal(removeCount, 2, 'internal removed twice'); - - }); - - test("references scopes are available to bindings nested in components (#2029)", function(){ - - var template = can.stache(''+ - ''); - - can.Component.extend({ - tag : "wrap-er" - }); - can.Component.extend({ - tag : "export-er", - events : { - "init" : function() { - var self = this.viewModel; - stop(); - setTimeout(function() { - self.attr("value", 100); - var wrapper = frag.lastChild, - simpleExample = wrapper.firstChild, - textNode = simpleExample.firstChild; - equal(textNode.nodeValue, "100", "updated value with reference"); - start(); - }, 10); - - } - } - }); - - can.Component.extend({ - tag : "simple-example", - template : can.stache("{{key}}"), - viewModel : {} - }); - var frag = template({}); - - }); - - test('two-way binding syntax PRIOR to v2.3 shall NOT let a child property initialize an undefined parent property (#2020)', function(){ - var renderer = can.stache(''); - - can.Component.extend({ - tag : 'pa-rent', - template: can.stache('') - }); - - can.Component.extend({ - tag : 'chi-ld', - viewModel: { - childProp: 'bar' - } - }); - - var frag = renderer({}); - - var parentVM = can.viewModel(frag.firstChild); - var childVM = can.viewModel(frag.firstChild.firstChild); - - equal(parentVM.attr('parentProp'), undefined, 'parentProp is undefined'); - equal(childVM.attr('childProp'), 'bar', 'childProp is bar'); - - parentVM.attr('parentProp', 'foo'); - - equal(parentVM.attr('parentProp'), 'foo', 'parentProp is foo'); - equal(childVM.attr('childProp'), 'foo', 'childProp is foo'); - - childVM.attr('childProp', 'baz'); - - equal(parentVM.attr('parentProp'), 'baz', 'parentProp is baz'); - equal(childVM.attr('childProp'), 'baz', 'childProp is baz'); - }); - - test('two-way binding syntax INTRODUCED in v2.3 ALLOWS a child property to initialize an undefined parent property', function(){ - var renderer = can.stache(''); - - can.Component.extend({ - tag : 'pa-rent', - template: can.stache('') - }); - - can.Component.extend({ - tag : 'chi-ld', - viewModel: { - childProp: 'bar' - } - }); - - var frag = renderer({}); - - var parentVM = can.viewModel(frag.firstChild); - var childVM = can.viewModel(frag.firstChild.firstChild); - - equal(parentVM.attr('parentProp'), 'bar', 'parentProp is bar'); - equal(childVM.attr('childProp'), 'bar', 'childProp is bar'); - - parentVM.attr('parentProp', 'foo'); - - equal(parentVM.attr('parentProp'), 'foo', 'parentProp is foo'); - equal(childVM.attr('childProp'), 'foo', 'childProp is foo'); - - childVM.attr('childProp', 'baz'); - - equal(parentVM.attr('parentProp'), 'baz', 'parentProp is baz'); - equal(childVM.attr('childProp'), 'baz', 'childProp is baz'); - }); - - test("conditional attributes (#2077)", function(){ - can.Component.extend({ - tag: 'some-comp', - viewModel: {} - }); - var template = can.stache(""); - - var map = new can.Map({ - preview: true, - nextPage: 2, - swapName: "preview" - }); - var frag = template(map); - - var vm = can.viewModel(frag.firstChild); - - var threads = [ - function(){ - - equal(vm.attr("next"), 2, "has binidng"); - equal(vm.attr("swap"), true, "swap - has binding"); - equal(vm.attr("checked"), "", "attr - has binding"); - map.attr("preview", false); - }, - function(){ - equal(vm.attr("swap"), false, "swap - updated binidng"); - - ok(vm.attr("checked") === null, "attr - value set to null"); - - map.attr("nextPage", 3); - equal(vm.attr("next"), 2, "not updating after binding is torn down"); - map.attr("preview", true); - - }, - function(){ - equal(vm.attr("next"), 3, "re-initialized with binding"); - equal(vm.attr("swap"), true, "swap - updated binidng"); - equal(vm.attr("checked"), "", "attr - has binding set again"); - map.attr("swapName", "nextPage"); - }, - function(){ - equal(vm.attr("swap"), 3, "swap - updated binding key"); - map.attr("nextPage",4); - equal(vm.attr("swap"), 4, "swap - updated binding"); - } - ]; - stop(); - var index = 0; - var next = function(){ - if(index < threads.length) { - threads[index](); - index++; - setTimeout(next, 10); - } else { - start(); - } - }; - setTimeout(next,10); - }); - - test(" (#2151)", function(){ - - can.Component.extend({ - tag : 'list-items', - template : can.stache("
      "+ - "{{#items}}"+ - "{{#if render}}"+ - "
    • "+ - "{{/if}}"+ - "{{/items}}"+ - "
    "), - viewModel : { - define : { - items : { - value : function() { - return new can.List([{ - id : 1, - context : 'Item 1', - render : false - }, { - id : 2, - context : 'Item 2', - render : false - }]); - } - } - } - } - }); - - can.Component.extend({ - tag : 'list-item', - template : can.stache("{{item.context}}") - }); - - var template = can.stache(""); - - var frag = template(); - - can.batch.start(); - can.viewModel(frag.firstChild).attr('items').each(function(item, index) { - item.attr('render', true); - }); - can.batch.stop(); - - var lis = frag.firstChild.getElementsByTagName("li"); - ok( innerHTML(lis[0]).indexOf("Item 1") >= 0, "Item 1 written out"); - ok( innerHTML(lis[1]).indexOf("Item 2") >= 0, "Item 2 written out"); - - }); -} - -makeTest("can/component new bindings dom", document); diff --git a/component/component_test.js b/component/component_test.js index 256ba302dc8..088db4baab7 100644 --- a/component/component_test.js +++ b/component/component_test.js @@ -1,1913 +1 @@ -require("can/util/vdom/document/document"); -require("can/util/vdom/build_fragment/build_fragment"); -require("can/map/define/define"); -require("can/component/component"); -require("can/view/stache/stache"); -require("can/route/route"); -require("steal-qunit"); -require("can/component/component_bindings_test.js"); - -var simpleDocument = can.simpleDocument; - -var innerHTML = function(node){ - if("innerHTML" in node) { - return node.innerHTML; - } -}; - - -function makeTest(name, doc) { - var oldDoc; - QUnit.module(name, { - setup: function () { - oldDoc = can.document; - can.document = doc; - - if(doc) { - this.fixture = doc.createElement("div"); - doc.body.appendChild(this.fixture); - this.$fixture = can.$(this.fixture); - } else { - this.fixture = can.$("#qunit-fixture")[0]; - this.$fixture = can.$("#qunit-fixture"); - } - }, - teardown: function(){ - can.document = oldDoc; - - if(doc) { - doc.body.removeChild(this.fixture); - } - } - }); - - var Paginate = can.Map.extend({ - count: Infinity, - offset: 0, - limit: 100, - // Prevent negative counts - setCount: function (newCount, success, error) { - return newCount < 0 ? 0 : newCount; - }, - // Prevent negative offsets - setOffset: function (newOffset) { - return newOffset < 0 ? - 0 : - Math.min(newOffset, !isNaN(this.count - 1) ? - this.count - 1 : - Infinity); - }, - // move next - next: function () { - this.attr('offset', this.offset + this.limit); - }, - prev: function () { - this.attr('offset', this.offset - this.limit); - }, - canNext: function () { - return this.attr('offset') < this.attr('count') - - this.attr('limit'); - }, - canPrev: function () { - return this.attr('offset') > 0; - }, - page: function (newVal) { - if (newVal === undefined) { - return Math.floor(this.attr('offset') / this.attr('limit')) + 1; - } else { - this.attr('offset', (parseInt(newVal) - 1) * this.attr('limit')); - } - }, - pageCount: function () { - return this.attr('count') ? - Math.ceil(this.attr('count') / this.attr('limit')) : null; - } - }); - - test("basic tabs", function () { - - // new Tabs() .. - can.Component.extend({ - tag: "tabs", - template: can.stache("
      " + - "{{#panels}}" + - "
    • {{title}}
    • " + - "{{/panels}}" + - "
    " + - ""), - viewModel: { - panels: [], - addPanel: function (panel) { - - if (this.attr("panels") - .length === 0) { - this.makeActive(panel); - } - this.attr("panels") - .push(panel); - }, - removePanel: function (panel) { - var panels = this.attr("panels"); - can.batch.start(); - panels.splice(panels.indexOf(panel), 1); - if (panel === this.attr("active")) { - if (panels.length) { - this.makeActive(panels[0]); - } else { - this.removeAttr("active"); - } - } - can.batch.stop(); - }, - makeActive: function (panel) { - this.attr("active", panel); - this.attr("panels") - .each(function (panel) { - panel.attr("active", false); - }); - panel.attr("active", true); - - }, - // this is viewModel, not stache - // consider removing viewModel as arg - isActive: function (panel) { - return this.attr('active') === panel; - } - } - }); - can.Component.extend({ - // make sure works - template: can.stache("{{#if active}}{{/if}}"), - tag: "panel", - viewModel: { - active: false, - title: "@" - }, - events: { - " inserted": function () { - can.viewModel(this.element[0].parentNode) - .addPanel(this.viewModel); - }, - " removed": function () { - if (!can.viewModel(this.element[0].parentNode)) { - console.log("bruke"); - } - can.viewModel(this.element[0].parentNode) - .removePanel(this.viewModel); - } - } - }); - - var template = can.stache("{{#each foodTypes}}{{content}}{{/each}}"); - - var foodTypes = new can.List([{ - title: "Fruits", - content: "oranges, apples" - }, { - title: "Breads", - content: "pasta, cereal" - }, { - title: "Sweets", - content: "ice cream, candy" - }]); - - var frag = template({ - foodTypes: foodTypes - }); - can.append(this.$fixture, frag); - - var testArea = this.fixture, - lis = testArea.getElementsByTagName("li"); - - equal(lis.length, 3, "three lis added"); - - foodTypes.each(function (type, i) { - equal(innerHTML(lis[i]), type.attr("title"), "li " + i + " has the right content"); - }); - - foodTypes.push({ - title: "Vegies", - content: "carrots, kale" - }); - - lis = testArea.getElementsByTagName("li"); - - //lis = testArea.getElementsByTagName("li"); - equal(lis.length, 4, "li added"); - - - foodTypes.each(function (type, i) { - equal( innerHTML(lis[i]), type.attr("title"), "li " + i + " has the right content"); - }); - - equal(testArea.getElementsByTagName("panel") - .length, 4, "panel added"); - - foodTypes.shift(); - - lis = testArea.getElementsByTagName("li"); - - equal(lis.length, 3, "removed li after shifting a foodType"); - - foodTypes.each(function (type, i) { - equal( innerHTML(lis[i]), type.attr("title"), "li " + i + " has the right content"); - }); - - // test changing the active element - var panels = testArea.getElementsByTagName("panel"); - - equal(lis[0].className, "active", "the first element is active"); - equal(innerHTML( panels[0] ), "pasta, cereal", "the first content is shown"); - equal(innerHTML( panels[1] ), "", "the second content is removed"); - - can.trigger(lis[1], "click"); - lis = testArea.getElementsByTagName("li"); - - equal(lis[1].className, "active", "the second element is active"); - equal(lis[0].className, "", "the first element is not active"); - - equal( innerHTML( panels[0]), "", "the second content is removed"); - equal( innerHTML( panels[1]), "ice cream, candy", "the second content is shown"); - - }); - - - test("lexical scoping", function() { - can.Component.extend({ - tag: "hello-world", - leakScope: false, - template: can.stache("{{greeting}} World{{exclamation}}"), - viewModel: { - greeting: "Hello" - } - }); - var template = can.stache("{{greeting}}"); - - - var frag = template({ - greeting: "World", - exclamation: "!" - }); - - var hello = frag.firstChild; - - equal(can.trim( innerHTML(hello) ), "Hello World"); - - can.Component.extend({ - tag: "hello-world-no-template", - leakScope: false, - viewModel: {greeting: "Hello"} - }); - template = can.stache("{{greeting}}"); - - frag = template({ - greeting: "World", - exclamation: "!" - }); - - hello = frag.firstChild; - - equal(can.trim( innerHTML(hello) ), "Hello", - "If no template is provided to can.Component, treat bindings as dynamic."); - }); - - test("dynamic scoping", function() { - - can.Component.extend({ - tag: "hello-world", - leakScope: true, - template: can.stache("{{greeting}} World{{exclamation}}"), - viewModel: {greeting: "Hello"} - }); - - var template = can.stache("{{greeting}}"); - var frag = template({ - greeting: "World", - exclamation: "!" - }); - - var hello = frag.firstChild; - - equal( can.trim( innerHTML(hello) ) , "Hello Hello!"); - - }); - - test("treecombo", function () { - - can.Component.extend({ - tag: "treecombo", - template: can.stache("" + - "
      " + - "" + - "{{#selectableItems}}" + - "
    • " + - "" + - "{{title}}" + - "{{#if children.length}}" + - "" + - "{{/if}}" + - "
    • " + - "{{/selectableItems}}" + - "
      " + - "
    "), - viewModel: { - items: [], - breadcrumb: [], - selected: [], - selectableItems: function () { - var breadcrumb = this.attr("breadcrumb"); - - // if there's an item in the breadcrumb - if (breadcrumb.attr('length')) { - - // return the last item's children - return breadcrumb.attr("" + (breadcrumb.length - 1) + '.children'); - } else { - - // return the top list of items - return this.attr('items'); - } - }, - showChildren: function (item, el, ev) { - ev.stopPropagation(); - this.attr('breadcrumb') - .push(item); - }, - emptyBreadcrumb: function () { - this.attr("breadcrumb") - .attr([], true); - }, - updateBreadcrumb: function (item) { - var breadcrumb = this.attr("breadcrumb"), - index = breadcrumb.indexOf(item); - breadcrumb.splice(index + 1, breadcrumb.length - index - 1); - }, - toggle: function (item) { - var selected = this.attr('selected'), - index = selected.indexOf(item); - if (index === -1) { - selected.push(item); - } else { - selected.splice(index, 1); - } - } - }, - helpers: { - isSelected: function (options) { - if (this.attr("selected") - .indexOf(options.context) > -1) { - return options.fn(); - } else { - return options.inverse(); - } - } - } - }); - - var template = can.stache(""); - - var base = new can.Map({}); - - var frag = template(base); - var root = doc.createElement("div"); - root.appendChild(frag); - - var items = [{ - id: 1, - title: "Midwest", - children: [{ - id: 5, - title: "Illinois", - children: [{ - id: 23423, - title: "Chicago" - }, { - id: 4563, - title: "Springfield" - }, { - id: 4564, - title: "Naperville" - }] - }, { - id: 6, - title: "Wisconsin", - children: [{ - id: 232423, - title: "Milwaulkee" - }, { - id: 45463, - title: "Green Bay" - }, { - id: 45464, - title: "Madison" - }] - }] - }, { - id: 2, - title: "East Coast", - children: [{ - id: 25, - title: "New York", - children: [{ - id: 3413, - title: "New York" - }, { - id: 4613, - title: "Rochester" - }, { - id: 4516, - title: "Syracuse" - }] - }, { - id: 6, - title: "Pennsylvania", - children: [{ - id: 2362423, - title: "Philadelphia" - }, { - id: 454663, - title: "Harrisburg" - }, { - id: 454664, - title: "Scranton" - }] - }] - }]; - - stop(); - - setTimeout(function () { - - base.attr('locations', items); - - var itemsList = base.attr('locations'); - - // check that the DOM is right - var treecombo = root.firstChild, - breadcrumb = treecombo.firstChild, - breadcrumbLIs = function(){ - return breadcrumb.getElementsByTagName('li'); - }, - options = treecombo.lastChild, - optionsLis = function(){ - return options.getElementsByTagName('li'); - }; - - equal(breadcrumbLIs().length, 1, "Only the default title is shown"); - - equal( innerHTML( breadcrumbLIs()[0] ) , "Locations", "The correct title from the attribute is shown"); - - equal( itemsList.length, optionsLis().length, "first level items are displayed"); - - // Test toggling selected, first by clicking - can.trigger(optionsLis()[0], "click"); - - equal(optionsLis()[0].className, "active", "toggling something not selected adds active"); - - ok(optionsLis()[0].getElementsByTagName('input')[0].checked, "toggling something not selected checks checkbox"); - equal(can.viewModel(treecombo, "selected") - .length, 1, "there is one selected item"); - equal(can.viewModel(treecombo, "selected.0"), itemsList.attr("0"), "the midwest is in selected"); - - // adjust the state and everything should update - can.viewModel(treecombo, "selected") - .pop(); - equal(optionsLis()[0].className, "", "toggling something not selected adds active"); - - // Test going in a location - can.trigger(optionsLis()[0].getElementsByTagName('button')[0], "click"); - equal(breadcrumbLIs().length, 2, "Only the default title is shown"); - equal(innerHTML(breadcrumbLIs()[1]), "Midwest", "The breadcrumb has an item in it"); - ok(/Illinois/.test( innerHTML(optionsLis()[0])), "A child of the top breadcrumb is displayed"); - - // Test going in a location without children - can.trigger(optionsLis()[0].getElementsByTagName('button')[0], "click"); - ok(/Chicago/.test( innerHTML(optionsLis()[0] ) ), "A child of the top breadcrumb is displayed"); - ok(!optionsLis()[0].getElementsByTagName('button') - .length, "no show children button"); - - // Test poping off breadcrumb - can.trigger(breadcrumbLIs()[1], "click"); - equal(innerHTML(breadcrumbLIs()[1]), "Midwest", "The breadcrumb has an item in it"); - ok(/Illinois/.test( innerHTML( optionsLis()[0])), "A child of the top breadcrumb is displayed"); - - // Test removing everything - can.trigger(breadcrumbLIs()[0], "click"); - equal(breadcrumbLIs().length, 1, "Only the default title is shown"); - equal( innerHTML(breadcrumbLIs()[0]), "Locations", "The correct title from the attribute is shown"); - - start(); - - }, 100); - - }); - - test("deferred grid", function () { - - // This test simulates a grid that reads a `deferreddata` property for - // items and displays them. - // If `deferreddata` is a deferred, it waits for those items to resolve. - // The grid also has a `waiting` property that is true while the deferred is being resolved. - - can.Component.extend({ - tag: "grid", - viewModel: { - items: [], - waiting: true - }, - template: can.stache("
    "), - events: { - init: function () { - this.update(); - }, - "{viewModel} deferreddata": "update", - update: function () { - var deferred = this.viewModel.attr('deferreddata'), - viewModel = this.viewModel; - - if (can.isDeferred(deferred)) { - this.viewModel.attr("waiting", true); - deferred.then(function (items) { - viewModel.attr('items') - .attr(items, true); - }); - } else { - viewModel.attr('items') - .attr(deferred, true); - } - }, - "{items} change": function () { - this.viewModel.attr("waiting", false); - } - } - }); - - // The context object has a `set` property and a - // deferredData property that reads from it and returns a new deferred. - var SimulatedScope = can.Map.extend({ - set: 0, - deferredData: function () { - - var deferred = new can.Deferred(); - var set = this.attr('set'); - if (set === 0) { - setTimeout(function () { - deferred.resolve([{ - first: "Justin", - last: "Meyer" - }]); - }, 100); - } else if (set === 1) { - setTimeout(function () { - deferred.resolve([{ - first: "Brian", - last: "Moschel" - }]); - }, 100); - } - return deferred; - } - }); - var viewModel = new SimulatedScope(); - - var template = can.stache("" + - "{{#each items}}" + - "" + - "{{first}}" + - "{{last}}" + - "" + - "{{/each}}" + - ""); - - can.append(this.$fixture, template({ - viewModel: viewModel - })); - - var gridScope = can.viewModel(this.fixture.firstChild); - - equal(gridScope.attr("waiting"), true, "The grid is initially waiting on the deferreddata to resolve"); - - stop(); - var self = this; - - var waitingHandler = function() { - gridScope.unbind('waiting', waitingHandler); - - setTimeout(function () { - var tds = self.fixture.getElementsByTagName("td"); - equal(tds.length, 2, "there are 2 tds"); - - gridScope.bind("waiting", function (ev, newVal) { - if (newVal === false) { - setTimeout(function () { - equal(innerHTML(tds[0]), "Brian", "td changed to brian"); - start(); - }, 10); - - } - }); - - // update set to change the deferred. - viewModel.attr("set", 1); - - }, 10); - }; - - gridScope.bind('waiting', waitingHandler); - }); - - test("nextprev", function () { - - can.Component.extend({ - tag: "next-prev", - template: can.stache( - 'Prev' + - 'Next') - }); - - var paginator = new Paginate({ - limit: 20, - offset: 0, - count: 100 - }); - var template = can.stache(""); - - var frag = template({ - paginator: paginator - }); - var nextPrev = frag.firstChild; - - var prev = nextPrev.firstChild, - next = nextPrev.lastChild; - - ok(!/enabled/.test( prev.className ), "prev is not enabled"); - ok(/enabled/.test( next.className ), "next is enabled"); - - can.trigger(next, "click"); - ok(/enabled/.test( prev.className ), "prev is enabled"); - }); - - test("page-count", function () { - - can.Component.extend({ - tag: "page-count", - template: can.stache('Page {{page}}.') - }); - - var paginator = new Paginate({ - limit: 20, - offset: 0, - count: 100 - }); - - var template = can.stache(""); - - var frag = template( new can.Map({ - paginator: paginator - }) ); - - var span = frag.firstChild.getElementsByTagName("span")[0]; - - equal(span.firstChild.nodeValue, "1"); - paginator.next(); - equal(span.firstChild.nodeValue, "2"); - paginator.next(); - equal(span.firstChild.nodeValue, "3"); - - }); - - test("hello-world and whitespace around custom elements", function () { - - can.Component.extend({ - tag: "hello-world", - template: can.stache("{{#if visible}}{{message}}{{else}}Click me{{/if}}"), - viewModel: { - visible: false, - message: "Hello There!" - }, - events: { - click: function () { - this.viewModel.attr("visible", true); - } - } - }); - - var template = can.stache(" "); - var frag = template({}); - - var helloWorld = frag.childNodes.item(1); - - can.trigger(can.$(helloWorld), "click"); - - equal( innerHTML(helloWorld) , "Hello There!"); - - }); - - test("self closing content tags", function () { - - can.Component.extend({ - "tag": "my-greeting", - template: can.stache("

    "), - viewModel: { - title: "can.Component" - } - }); - - var template = can.stache("{{site}} - {{title}}"); - - var frag = template({ - site: "CanJS" - }); - - equal(frag.firstChild.getElementsByTagName("span") - .length, 1, "there is an h1"); - }); - - test("can.viewModel utility", function() { - can.Component({ - tag: "my-taggy-tag", - template: can.stache("

    hello

    "), - viewModel: { - foo: "bar" - } - }); - - var frag = can.stache("")(); - - - var el = can.$(frag.firstChild); - - equal(can.viewModel(el), can.data(el, "viewModel"), "one argument grabs the viewModel object"); - equal(can.viewModel(el, "foo"), "bar", "two arguments fetches a value"); - can.viewModel(el, "foo", "baz"); - equal(can.viewModel(el, "foo"), "baz", "Three arguments sets the value"); - if (window.$ && $.fn) { - el = $(frag.firstChild); - equal(el.viewModel(), can.data(el, "viewModel"), "jQuery helper grabs the viewModel object"); - equal(el.viewModel("foo"), "baz", "jQuery helper with one argument fetches a property"); - equal(el.viewModel("foo", "bar").get(0), el.get(0), "jQuery helper returns the element"); - equal(el.viewModel("foo"), "bar", "jQuery helper with two arguments sets the property"); - } - }); - - test("can.viewModel backwards compatible with can.scope", function() { - equal(can.viewModel, can.scope, "can helper"); - if (window.$ && $.fn) { - equal($.scope, $.viewModel, "jQuery helper"); - } - }); - - test("can.viewModel creates one if it doesn't exist", function(){ - var frag = can.stache("
    ")(); - - var el = can.$(frag.firstChild); - var viewModel = can.viewModel(el); - ok(!!viewModel, "viewModel created where it didn't exist."); - equal(viewModel, can.data(el, "viewModel"), "viewModel is in the data."); - }); - - test('setting passed variables - two way binding', function () { - can.Component.extend({ - tag: "my-toggler", - template: can.stache("{{#if visible}}{{/if}}"), - viewModel: { - visible: true, - show: function () { - this.attr('visible', true); - }, - hide: function () { - this.attr("visible", false); - } - } - }); - - can.Component.extend({ - tag: "my-app", - viewModel: { - visible: true, - show: function () { - this.attr('visible', true); - } - } - }); - var template = can.stache("" + - '{{^visible}}{{/visible}}' + - '' + - 'content' + - '' + - '' + - ''); - - var frag = template({}); - - var myApp = frag.firstChild, - buttons = myApp.getElementsByTagName("button"); - - equal( buttons.length, 1, "there is one button"); - equal( innerHTML(buttons[0]) , "hide", "the button's text is hide"); - - can.trigger(buttons[0], "click"); - buttons = myApp.getElementsByTagName("button"); - - equal(buttons.length, 1, "there is one button"); - equal(innerHTML(buttons[0]), "show", "the button's text is show"); - - can.trigger(buttons[0], "click"); - buttons = myApp.getElementsByTagName("button"); - - equal(buttons.length, 1, "there is one button"); - equal(innerHTML(buttons[0]), "hide", "the button's text is hide"); - }); - - test("helpers reference the correct instance (#515)", function () { - expect(2); - can.Component({ - tag: 'my-text', - template: can.stache('

    {{valueHelper}}

    '), - helpers: { - valueHelper: function () { - return this.attr('value'); - } - } - }); - - var template = can.stache(''); - - var frag = template({}); - - equal(frag.firstChild.firstChild.firstChild.nodeValue, 'value1'); - equal(frag.lastChild.firstChild.firstChild.nodeValue, 'value2'); - }); - - test('access hypenated attributes via camelCase or hypenated', function () { - can.Component({ - tag: 'hyphen', - viewModel: { - }, - template: can.stache('

    {{valueHelper}}

    '), - helpers: { - valueHelper: function () { - return this.attr('camelCase'); - } - } - }); - - var template = can.stache(''); - var frag = template({}); - - - equal(frag.firstChild.firstChild.firstChild.nodeValue, 'value1'); - - }); - - test("a map as viewModel", function () { - - var me = new can.Map({ - name: "Justin" - }); - - can.Component.extend({ - tag: 'my-viewmodel', - template: can.stache("{{name}}}"), - viewModel: me - }); - - var template = can.stache(''); - equal(template().firstChild.firstChild.nodeValue, "Justin"); - - }); - - test("content in a list", function () { - var template = can.stache('{{name}}'); - - can.Component.extend({ - tag: "my-list", - template: can.stache("{{#each items}}
  • {{/each}}"), - viewModel: { - items: new can.List([{ - name: "one" - }, { - name: "two" - }]) - } - }); - - var lis = template() - .firstChild.getElementsByTagName("li"); - - equal(innerHTML(lis[0]), "one", "first li has correct content"); - equal(innerHTML(lis[1]), "two", "second li has correct content"); - - }); - - test("don't update computes unnecessarily", function () { - var sourceAge = 30, - timesComputeIsCalled = 0; - - var age = can.compute(function (newVal) { - timesComputeIsCalled++; - if (timesComputeIsCalled === 1) { - ok(true, "reading initial value to set as years"); - } else if (timesComputeIsCalled === 2) { - equal(newVal, 31, "updating value to 31"); - } else if (timesComputeIsCalled === 3) { - ok(true, "called back another time after set to get the value"); - } else { - ok(false, "You've called the callback " + timesComputeIsCalled + " times"); - } - - if (arguments.length) { - sourceAge = newVal; - } else { - return sourceAge; - } - }); - - can.Component.extend({ - tag: "age-er" - }); - - var template = can.stache(""); - - template({ - age: age - }); - - age(31); - - }); - - test("component does not respect can.compute passed via attributes (#540)", function () { - - var data = { - compute: can.compute(30) - }; - - can.Component.extend({ - tag: "my-component", - template: can.stache("{{blocks}}") - }); - - var template = can.stache(""); - - var frag = template(data); - - equal( innerHTML(frag.firstChild.firstChild), "30"); - - }); - - test("defined view models (#563)", function () { - - var HelloWorldModel = can.Map.extend({ - visible: true, - toggle: function () { - this.attr("visible", !this.attr("visible")); - } - }); - - can.Component.extend({ - tag: "my-helloworld", - template: can.stache("

    {{#if visible}}visible{{else}}invisible{{/if}}

    "), - viewModel: HelloWorldModel - }); - - var template = can.stache(""); - - var frag = template({}); - - equal( innerHTML(frag.firstChild.firstChild), "visible"); - }); - - test("viewModel not rebound correctly (#550)", function () { - - var nameChanges = 0; - - can.Component.extend({ - tag: "viewmodel-rebinder", - events: { - "{name} change": function () { - nameChanges++; - } - } - }); - - var template = can.stache(""); - - var frag = template(); - var viewModel = can.viewModel(can.$(frag.firstChild)); - - var n1 = can.compute(), - n2 = can.compute(); - - viewModel.attr("name", n1); - - n1("updated"); - - viewModel.attr("name", n2); - - n2("updated"); - - - equal(nameChanges, 2); - }); - - test("content extension stack overflow error", function () { - - can.Component({ - tag: 'outer-tag', - template: can.stache('inner-tag CONTENT ') - }); - - can.Component({ - tag: 'inner-tag', - template: can.stache('inner-tag TEMPLATE ') - }); - - // currently causes Maximum call stack size exceeded - var template = can.stache("outer-tag CONTENT"); - - // RESULT = inner-tag TEMPLATE inner-tag CONTENT outer-tag CONTENT - - var frag = template(); - - equal( innerHTML(frag.firstChild.firstChild), 'inner-tag TEMPLATE inner-tag CONTENT outer-tag CONTENT'); - - }); - - test("inserted event fires twice if component inside live binding block", function () { - - var inited = 0, - inserted = 0; - - can.Component({ - tag: 'child-tag', - - viewModel: { - init: function () { - inited++; - } - }, - events: { - ' inserted': function () { - inserted++; - } - } - }); - - can.Component({ - tag: 'parent-tag', - - template: can.stache('{{#shown}}{{/shown}}'), - - viewModel: { - shown: false - }, - events: { - ' inserted': function () { - this.viewModel.attr('shown', true); - } - } - }); - - var frag = can.stache("")({}); - - can.append(this.$fixture, frag); - - equal(inited, 1); - equal(inserted, 1); - - }); - - - test("@ keeps properties live now", function () { - - can.Component.extend({ - tag: "attr-fun", - template: can.stache("

    {{fullName}}

    "), - viewModel: { - fullName: function () { - return this.attr("firstName") + " " + this.attr("lastName"); - } - } - }); - - var frag = can.stache("")(); - - var attrFun = frag.firstChild; - - this.fixture.appendChild(attrFun); - - equal( innerHTML(attrFun.firstChild), "Justin Meyer"); - - can.attr.set(attrFun, "first-name", "Brian"); - - stop(); - - setTimeout(function () { - equal(attrFun.firstChild.firstChild.nodeValue, "Brian Meyer"); - start(); - }, 100); - - }); - - test("id, class, and dataViewId should be ignored (#694)", function () { - can.Component.extend({ - tag: "stay-classy", - viewModel: { - notid: "foo", - notclass: 5, - notdataviewid: {} - } - }); - - var data = { - idFromData: "id-success", - classFromData: "class-success", - dviFromData: "dvi-success" - }; - var frag = can.stache( - "")(data); - - var stayClassy = frag.firstChild; - - can.append(this.$fixture, frag); - - var viewModel = can.viewModel(stayClassy); - - equal(viewModel.attr("id"), undefined); - equal(viewModel.attr("notid"), "id-success"); - equal(viewModel.attr("class"), undefined); - equal(viewModel.attr("notclass"), "class-success"); - equal(viewModel.attr("dataViewId"), undefined); - equal(viewModel.attr("notdataviewid"), "dvi-success"); - }); - - test("Component can-click method should be not called while component's init", function () { - - var called = false; - - can.Component.extend({ - tag: "child-tag" - }); - - can.Component.extend({ - tag: "parent-tag", - template: can.stache(''), - viewModel: { - method: function () { - called = true; - } - } - }); - - can.stache('')(); - - equal(called, false); - }); - - - test('Same component tag nested', function () { - can.Component({ - 'tag': 'my-tag', - template: can.stache('

    ') - }); - //simplest case - var template = can.stache('
    OutterInner
    '); - //complex case - var template2 = can.stache('
    3210
    '); - //edge case for new logic (same custom tag at same depth as one previously encountered) - var template3 = can.stache('
    FirstSecond
    '); - - - equal( template({}).firstChild.getElementsByTagName('p').length, 2, 'proper number of p tags'); - - equal( template2({}).firstChild.getElementsByTagName('p').length, 4, 'proper number of p tags'); - - equal( template3({}).firstChild.getElementsByTagName('p').length, 2, 'proper number of p tags'); - - }); - - test("Component events bind to window", function(){ - window.tempMap = new can.Map(); - - can.Component.extend({ - tag: "window-events", - events: { - "{tempMap} prop": function(){ - ok(true, "called templated event"); - } - } - }); - - var template = can.stache(''); - - template(); - - window.tempMap.attr("prop","value"); - - // IE 6-8 throws an error when deleting globals created via assignment: - // http://perfectionkills.com/understanding-delete/#ie_bugs - window.tempMap = undefined; - try{ - delete window.tempMap; - } catch(e) {} - - }); - - test("can.Construct are passed normally", function(){ - var Constructed = can.Construct.extend({foo:"bar"},{}); - - can.Component.extend({ - tag: "con-struct", - template: can.stache("{{con.foo}}") - }); - - var stached = can.stache(""); - - var res = stached({ - Constructed: Constructed - }); - equal(innerHTML(res.firstChild), "bar"); - - - }); - - //!steal-remove-start - if (can.dev) { - test("passing unsupported attributes gives a warning", function(){ - - var oldlog = can.dev.warn; - can.dev.warn = function (text) { - ok(text, "got a message"); - can.dev.warn = oldlog; - }; - can.Component.extend({ - tag: 'my-thing', - template: can.stache('hello') - }); - var stache = can.stache(""); - stache(new can.Map({productId: 123})); - }); - } - //!steal-remove-end - - test("stache conditionally nested components calls inserted once (#967)", function(){ - expect(1); - - can.Component.extend({ - tag: "can-parent-stache", - viewModel: { - shown: true - }, - template: can.stache("{{#if shown}}{{/if}}") - }); - can.Component.extend({ - tag: "can-child", - events: { - inserted: function(){ - this.viewModel.attr('bar', 'foo'); - ok(true, "called inserted once"); - } - } - }); - - var template = can.stache(""); - - can.append(this.$fixture, template()); - - }); - - test("hyphen-less tag names", function () { - var template = can.stache(''); - can.Component.extend({ - tag: "foobar", - template: can.stache("
    {{name}}
    "), - viewModel: { - name: "Brian" - } - }); - - equal(template().lastChild.firstChild.firstChild.nodeValue, "Brian"); - - }); - - test('nested component within an #if is not live bound(#1025)', function() { - can.Component.extend({ - tag: 'parent-component', - template: can.stache('{{#if shown}}{{/if}}'), - viewModel: { - shown: false - } - }); - - can.Component.extend({ - tag: 'child-component', - template: can.stache('Hello world.') - }); - - var template = can.stache(''); - var frag = template({}); - - equal( innerHTML(frag.firstChild), '', 'child component is not inserted'); - can.viewModel(frag.firstChild).attr('shown', true); - - equal( innerHTML(frag.firstChild.firstChild), 'Hello world.', 'child component is inserted'); - can.viewModel(frag.firstChild).attr('shown', false); - - equal( innerHTML(frag.firstChild), '', 'child component is removed'); - }); - - test('component does not update viewModel on id, class, and data-view-id attribute changes (#1079)', function(){ - - can.Component.extend({ - tag:'x-app' - }); - - var frag=can.stache('')({}); - - var el = frag.firstChild; - var viewModel = can.viewModel(el); - - // element must be inserted, otherwise attributes event will not be fired - can.append(this.$fixture,frag); - - // update the class - can.addClass(can.$(el),"foo"); - - stop(); - setTimeout(function(){ - equal(viewModel.attr('class'),undefined, "the viewModel is not updated when the class attribute changes"); - start(); - },20); - - }); - - test('viewModel objects with Constructor functions as properties do not get converted (#1261)', 1, function(){ - stop(); - - var Test = can.Map.extend({ - test: 'Yeah' - }); - - can.Component.extend({ - tag:'my-app', - viewModel: { - MyConstruct: Test - }, - events: { - '{MyConstruct} something': function() { - ok(true, 'Event got triggered'); - start(); - } - } - }); - - var frag = can.stache('')(); - - // element must be inserted, otherwise attributes event will not be fired - can.append(this.$fixture,frag); - - can.trigger(Test, 'something'); - }); - - test('removing bound viewModel properties on destroy #1415', function(){ - var state = new can.Map({ - product: { - id: 1, - name: "Tom" - } - }); - - can.Component.extend({ - tag: 'destroyable-component', - events: { - destroy: function(){ - this.viewModel.attr('product', null); - } - } - }); - - var frag = can.stache('')(state); - - // element must be inserted, otherwise attributes event will not be fired - can.append(this.$fixture,frag); - - can.remove( can.$(this.fixture.firstChild) ); - - ok(state.attr('product') == null, 'product was removed'); - }); - - test('changing viewModel property rebinds {viewModel.<...>} events (#1529)', 2, function(){ - can.Component.extend({ - tag: 'rebind-viewmodel', - events: { - inserted: function(){ - this.viewModel.attr('item', {}); - }, - '{scope.item} change': function() { - ok(true, 'Change event on scope'); - }, - '{viewModel.item} change': function() { - ok(true, 'Change event on viewModel'); - } - } - }); - var frag = can.stache('')(); - var rebind = frag.firstChild; - can.append(this.$fixture, rebind); - - can.viewModel(can.$(rebind)).attr('item.name', 'CDN'); - }); - - - - test('Component two way binding loop (#1579)', function() { - var changeCount = 0; - - can.Component.extend({ - tag: 'product-swatch-color' - }); - - - can.Component.extend({ - tag: 'product-swatch', - template: can.stache(''), - viewModel: can.Map.extend({ - define: { - variations: { - set: function(variations) { - if(changeCount > 500) { - return; - } - changeCount++; - return new can.List(variations.attr()); - } - } - } - }) - }); - - var frag = can.stache('')(), - productSwatch = frag.firstChild; - - can.batch.start(); - can.viewModel( can.$(productSwatch) ).attr('variations', new can.List()); - can.batch.stop(); - - - ok(changeCount < 500, "more than 500 events"); - }); - - test('DOM trees not releasing when referencing can.Map inside can.Map in template (#1593)', function() { - var baseTemplate = can.stache('{{#if show}}{{/if}}'), - show = can.compute(true), - state = new can.Map({ - inner: 1 - }); - - var removeCount = 0; - - can.Component.extend({ - tag: 'my-inside', - events: { - removed: function() { - removeCount++; - } - } - }); - - can.Component.extend({ - tag: 'my-outside', - template: can.stache('{{#if state.inner}}{{/if}}') - }); - - can.append( this.$fixture, baseTemplate({ - show: show, - state: state - }) ); - - show(false); - state.removeAttr('inner'); - - equal(removeCount, 1, 'internal removed once'); - - show(true); - state.attr('inner', 2); - - state.removeAttr('inner'); - - equal(removeCount, 2, 'internal removed twice'); - - }); - - test("component viewModels are initialized with binding values", function(){ - can.Component.extend({ - tag: 'init-viewmodel', - viewModel: { - init: function(props){ - equal(props.prop, "value", "got initialized with a value"); - } - } - }); - can.stache("")({ - passedProp: "value" - }); - }); - - test("wrong context inside tags (#2092)", function(){ - - can.Component.extend({ - tag: 'some-context', - viewModel: { - value: "WRONG", - items: [{value: "A", name: "X"}, {value: "B", name: "Y"}] - }, - template: can.stache("{{#each items}}{{name}}{{/each}}") - }); - - var templateA = can.stache("{{value}}"); - - var frag = templateA({}); - - var spans = frag.firstChild.getElementsByTagName("span"); - - equal(spans[0].firstChild.nodeValue, "A", "context is right"); - equal(spans[1].firstChild.nodeValue, "B", "context is right"); - - var templateB = can.stache(""); - - frag = templateB({}); - - spans = frag.firstChild.getElementsByTagName("span"); - - equal(spans[0].firstChild.nodeValue, "X", "context is right X"); - equal(spans[1].firstChild.nodeValue, "Y", "context is right"); - - }); - - test("%root property should not be serialized inside prototype of can.Component constructor (#2080)", function () { - var viewModel = can.Map.extend({}); - - can.Component.extend({ - tag: "foo", - viewModel: viewModel, - init: function () { - ok(!this.viewModel.serialize()['%root'], "serialized viewModel contains '%root' property"); - } - }); - - var template = can.stache(""); - - can.append(this.$fixture, template()); - }); - - test("%root property is available in a viewModel", function () { - var viewModel = can.Map.extend({}); - - can.Component.extend({ - tag: "foo", - viewModel: viewModel, - init: function () { - ok(this.viewModel.attr('%root'), "viewModel contains '%root' property"); - } - }); - - var template = can.stache(""); - - can.append(this.$fixture, template()); - }); - - test('Type in a component’s viewModel doesn’t work correctly with lists (#2250)', function() { - var Item = can.Map.extend({ - define: { - prop: { - value: true - } - } - }); - - var Collection = can.List.extend({ - Map: Item - }, {}); - - can.Component.extend({ - tag: "test-component", - viewModel: { - define: { - collection: { - Type: Collection - }, - vmProp: { - get: function() { - return this.attr('collection.0.prop'); - } - } - } - } - }); - - var frag = can.stache('')({ - collection: [{}] - }); - var vmPropVal = can.viewModel(can.$(frag.firstChild)).attr('vmProp'); - ok(vmPropVal, 'value is from defined prop'); - }); - - // PUT NEW TESTS ABOVE THIS LINE -} - -makeTest("can/component dom", document); -if(window.jQuery && window.steal) { - makeTest("can/component vdom", simpleDocument); -} - - - -QUnit.module("can/component stache"); - - -asyncTest('stache integration', function(){ - - can.Component.extend({ - tag: 'my-tagged', - template: '{{p1}},{{p2.val}},{{p3}},{{p4}}' - }); - - var stache = can.stache(""); - - var data = new can.Map({ - v1: "value1", - v2: {val: "value2"}, - v3: "value3", - value3: "value 3", - VALUE3: "VALUE 3" - }); - - var stacheFrag = stache(data), - stacheResult = stacheFrag.childNodes[0].innerHTML.split(","); - - equal(stacheResult[0], "v1", "stache uses attribute values"); - equal(stacheResult[1], "value2", "stache single {} cross binds value"); - equal(stacheResult[2], "value3", "stache {{}} cross binds attribute"); - - data.attr("v1","VALUE1"); - data.attr("v2",new can.Map({val: "VALUE 2"})); - data.attr("v3","VALUE3"); - can.attr.set( stacheFrag.childNodes[0],"p4","value4"); - - stacheResult = stacheFrag.childNodes[0].innerHTML.split(","); - - equal(stacheResult[0], "v1", "stache uses attribute values so it should not change"); - equal(stacheResult[1], "VALUE 2", "stache single {} cross binds value and updates immediately"); - - equal(stacheResult[2], "value3", "stache {{}} cross binds attribute changes so it wont be updated immediately"); - - setTimeout(function(){ - stacheResult = stacheFrag.childNodes[0].innerHTML.split(","); - equal(stacheResult[2], "VALUE3", "stache {{}} cross binds attribute"); - - equal(stacheResult[3], "value4", "stache sees new attributes"); - - start(); - },20); - -}); - -test("attach events on init", function(){ - expect(2); - can.Component.extend({ - tag: 'app-foo', - template: can.stache('
    click me
    '), - events: { - init: function(){ - this.on("div", 'click', 'doSomethingfromInit'); - }, - inserted: function(){ - this.on("div", 'click', 'doSomethingfromInserted'); - }, - doSomethingfromInserted: function(){ - ok(true, "bound in inserted"); - }, - doSomethingfromInit: function(){ - ok(true, "bound in init"); - } - } - }); - can.append( can.$("#qunit-fixture"), can.stache("")({})); - can.trigger(can.$('#qunit-fixture div'), 'click'); -}); - -test("attach events on init", function(){ - expect(2); - can.Component.extend({ - tag: 'app-foo', - template: can.stache('
    click me
    '), - events: { - init: function(){ - this.on("div", 'click', 'doSomethingfromInit'); - }, - inserted: function(){ - this.on("div", 'click', 'doSomethingfromInserted'); - }, - doSomethingfromInserted: function(){ - ok(true, "bound in inserted"); - }, - doSomethingfromInit: function(){ - ok(true, "bound in init"); - } - } - }); - can.append( can.$("#qunit-fixture"), can.stache("")({})); - can.trigger(can.$('#qunit-fixture div'), 'click'); -}); - -if(can.isFunction(Object.keys)) { - test(' node list cleans up properly as direct child (#1625, #1627)', 2, function() { - var size = Object.keys(can.view.nodeLists.nodeMap).length; - var items = []; - var viewModel = new can.Map({ - show: false - }); - var toggle = function() { - viewModel.attr('show', !viewModel.attr('show')); - }; - - for (var i = 0; i < 100; i++) { - items.push({ - // Random 5 character String - name: Math.random().toString(36) - .replace(/[^a-z]+/g, '').substr(0, 5) - }); - } - - can.Component.extend({ - tag: 'grandparent-component', - template: can.stache('{{#if show}}{{/if}}'), - scope: viewModel - }); - - can.Component.extend({ - tag: 'parent-component', - template: can.stache('{{#items}}\n:)\n{{/items}}'), - scope: { - items: items - } - }); - - can.Component.extend({ - tag: 'child-component', - template: can.stache('
    \n\n
    ') - }); - - can.append(can.$("#qunit-fixture"), can.stache('')()); - - toggle(); - equal(Object.keys(can.view.nodeLists.nodeMap).length - size, 0, - 'No new items added to nodeMap'); - - toggle(); - equal(Object.keys(can.view.nodeLists.nodeMap).length - size, 0, - 'No new items added to nodeMap'); - - can.remove(can.$("#qunit-fixture>*")); - }); - - asyncTest(' node list cleans up properly, directly nested (#1625, #1627)', function() { - - can.Component.extend({ - tag: 'parent-component', - template: can.stache('{{#items}}{{parentAttrInContent}}{{/items}}'), - scope: { - items: [{parentAttrInContent: 0},{parentAttrInContent: 1} ] - } - }); - - can.Component.extend({ - tag: 'child-component', - template: can.stache('
    {{#if bar}}{{/if}}
    '), - scope: { - bar: true - } - }); - - can.append(can.$("#qunit-fixture"), can.stache('')()); - - var old = can.unbindAndTeardown; - var count = 0; - can.unbindAndTeardown = function(name) { - if(name === 'parentAttrInContent') { - count++; - } - return old.call(this, arguments); - }; - - can.remove(can.$("#qunit-fixture>*")); - - // Dispatches async - setTimeout(function() { - equal(count, 2, '2 items unbound. Previously, this would unbind 4 times because of switching to fast path'); - can.unbindAndTeardown = old; - - start(); - }, 20); - }); - - test("components control destroy method is called", function(){ - expect(0); - can.Component.extend({ - tag: 'comp-control-destroy-test', - template: can.stache('
    click me
    '), - events: { - "{document} click" : function () { - ok(true, "click registered"); - } - } - }); - can.append(can.$("#qunit-fixture"), can.stache("")({})); - can.remove(can.$("#qunit-fixture>*")); - can.trigger(can.$(document), 'click'); - }); - - test(" tag doesn't leak memory", function(){ - - can.Component.extend({ - tag: 'my-viewer', - template: can.stache('
    ') - }); - - var template = can.stache('{{#show}}{{value}}{{/show}}'); - - var map = new can.Map({show: true, value: "hi"}); - - can.append(can.$("#qunit-fixture"), template(map)); - - map.attr("show", false); - map.attr("show", true); - map.attr("show", false); - map.attr("show", true); - map.attr("show", false); - map.attr("show", true); - map.attr("show", false); - stop(); - setTimeout(function(){ - equal(map._bindings,1, "only one binding"); - can.remove(can.$("#qunit-fixture>*")); - start(); - },10); - - }); - - // PUT NEW TESTS THAT NEED TO TEST AGAINST STACHE JUST ABOVE THIS LINE -} - -test('component simpleHelpers', function() { - can.Component.extend({ - tag: 'simple-helper', - template: can.stache('Result: {{add first second}}'), - scope: { - first: 4, - second: 3 - }, - simpleHelpers: { - add: function(a, b) { - return a + b; - } - } - }); - - var frag = can.stache('')(); - equal(frag.childNodes[0].innerHTML, 'Result: 7'); -}); - -test("component destroy should teardown event handlers", function () { - var count = 0, - map = new can.Map({value: 1}); - - can.Component.extend({ - tag: "page-element", - viewModel: { map: map }, - events: { - '{scope.map} value': function(){ - count++; - } - } - }); - can.append(can.$("#qunit-fixture"), can.stache("")()); - can.remove(can.$("#qunit-fixture>*")); - map.attr("value", 2); - - equal(count, 0, "Event handler should NOT be called since the element was removed."); - - can.remove(can.$("#qunit-fixture>*")); -}); - -// DONT PUT TESTS HERE!!! Look up for where to put new tests. +require('can-component/test/component-test'); diff --git a/component/events.md b/component/events.md deleted file mode 100644 index fdad81affd8..00000000000 --- a/component/events.md +++ /dev/null @@ -1,61 +0,0 @@ -@property {Object} can.Component.prototype.events -@parent can.Component.prototype - -Listen to events on elements and observables. - -@option {Object.} An object of event names and methods -that handle the event. For example: - - can.Component({ - events: { - ".next click": function(){ - this.viewModel.next() - } - }, - viewModel: { - next: function(){ - this.attr("offset", this.offset + this.limit); - } - } - }) - - -A component's events object is used as the prototype of a [can.Control]. The control gets created on the component's -element. The component's viewModel is available within event handlers as `this.viewModel`. - - -@body - -## Use - -[can.Component]'s events object allows you to provide low-level [can.Control]-like abilities to a `can.Component` -while still accessing `can.Component`'s objects and methods like [can.Component::viewModel viewModel]. The following -example listens to clicks on elements with `className="next"` and calls `.next()` on the component's viewModel. - -@demo can/component/examples/paginate_events_next.html - -The events object can also listen to objects or properties on the component's [can.Component::viewModel viewModel]. For instance, instead -of using live-binding, we could listen to when offset changes and update the page manually: - -@demo can/component/examples/paginate_events_next_update_page.html - -Components have the ability to bind to special [can.events.inserted inserted] and [can.events.removed removed] events that are called when a component's tag has been inserted into or removed from the page: - - events: { - "inserted": function(){ - // called when the component's tag is inserted into the DOM - }, - "removed": function(){ - // called when the component's tag is removed from the DOM - } - } - -## High performance template rendering - -While [can.view.bindings] conveniently allows you to call a [can.Component::viewModel viewModel] method from a template like: - - - -This has the effect of binding an event handler directly to this element. Every element that has a `can-click` or similar attribute has an event handler bound to it. For a large grid or list, this could have a performance penalty. - -By contrast, events bound using [can.Component]'s events object use event delegation, which is useful for high performance template rendering. In a large grid or list, event delegation only binds a single event handler rather than one per row. \ No newline at end of file diff --git a/component/examples/1.html b/component/examples/1.html deleted file mode 100644 index 114fb74bda2..00000000000 --- a/component/examples/1.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - diff --git a/component/examples/2.html b/component/examples/2.html deleted file mode 100644 index 02f255f3218..00000000000 --- a/component/examples/2.html +++ /dev/null @@ -1,66 +0,0 @@ - - diff --git a/component/examples/3.html b/component/examples/3.html deleted file mode 100644 index 02537667685..00000000000 --- a/component/examples/3.html +++ /dev/null @@ -1,86 +0,0 @@ -
    - - - diff --git a/component/examples/4.html b/component/examples/4.html deleted file mode 100644 index 4a553bfeabc..00000000000 --- a/component/examples/4.html +++ /dev/null @@ -1,175 +0,0 @@ - - -
    -
    - - - - - - diff --git a/component/examples/accordion.html b/component/examples/accordion.html deleted file mode 100644 index 8eb418700b1..00000000000 --- a/component/examples/accordion.html +++ /dev/null @@ -1,101 +0,0 @@ -
    -
    - -
    - - diff --git a/component/examples/child-component-content-siblings-leak.html b/component/examples/child-component-content-siblings-leak.html deleted file mode 100644 index 3bb6ae28625..00000000000 --- a/component/examples/child-component-content-siblings-leak.html +++ /dev/null @@ -1,48 +0,0 @@ -
    - diff --git a/component/examples/click_me.html b/component/examples/click_me.html deleted file mode 100644 index 888c0cd0554..00000000000 --- a/component/examples/click_me.html +++ /dev/null @@ -1,22 +0,0 @@ - - - \ No newline at end of file diff --git a/component/examples/component-tag.html b/component/examples/component-tag.html deleted file mode 100644 index 6d76e9bc8fe..00000000000 --- a/component/examples/component-tag.html +++ /dev/null @@ -1,32 +0,0 @@ - - - component system plugin example - - - - - - - - diff --git a/component/examples/grid.js b/component/examples/grid.js deleted file mode 100644 index 85bd9f6c79e..00000000000 --- a/component/examples/grid.js +++ /dev/null @@ -1,32 +0,0 @@ -can.Component.extend({ - tag: 'grid', - viewModel: { - items: [] - }, - template: '
    ', - events: { - init: function () { - this.update(); - }, - '{deferreddata} change': 'update', - update: function () { - var deferred = this.viewModel.attr('deferreddata'), - viewModel = this.viewModel; - if (can.isDeferred(deferred)) { - this.element.find('tbody') - .css('opacity', 0.5); - deferred.then(function (items) { - viewModel.attr('items') - .attr(items, true); - }); - } else { - viewModel.attr('items') - .attr(deferred, true); - } - }, - '{items} change': function () { - this.element.find('tbody') - .css('opacity', 1); - } - } -}); diff --git a/component/examples/hello-world.html b/component/examples/hello-world.html deleted file mode 100644 index abb996e5a5e..00000000000 --- a/component/examples/hello-world.html +++ /dev/null @@ -1,26 +0,0 @@ -
    -
    - -
    - diff --git a/component/examples/list.html b/component/examples/list.html deleted file mode 100644 index 1aa17aff50a..00000000000 --- a/component/examples/list.html +++ /dev/null @@ -1,67 +0,0 @@ - -
    - - - - - - - diff --git a/component/examples/my_greeting_full.html b/component/examples/my_greeting_full.html deleted file mode 100644 index 81f76f74444..00000000000 --- a/component/examples/my_greeting_full.html +++ /dev/null @@ -1,25 +0,0 @@ - -
    - - - diff --git a/component/examples/name_editor.html b/component/examples/name_editor.html deleted file mode 100644 index 63c3d37d594..00000000000 --- a/component/examples/name_editor.html +++ /dev/null @@ -1,38 +0,0 @@ - -
    - - - - - diff --git a/component/examples/nested-component-content-leak.html b/component/examples/nested-component-content-leak.html deleted file mode 100644 index aef81e6f808..00000000000 --- a/component/examples/nested-component-content-leak.html +++ /dev/null @@ -1,43 +0,0 @@ -
    - diff --git a/component/examples/nested-component-leak.html b/component/examples/nested-component-leak.html deleted file mode 100644 index 828ce4c1519..00000000000 --- a/component/examples/nested-component-leak.html +++ /dev/null @@ -1,41 +0,0 @@ -
    - diff --git a/component/examples/paginate.html b/component/examples/paginate.html deleted file mode 100644 index 6266dea03a9..00000000000 --- a/component/examples/paginate.html +++ /dev/null @@ -1,233 +0,0 @@ - - -
    - - - diff --git a/component/examples/paginate_events_next.html b/component/examples/paginate_events_next.html deleted file mode 100644 index 55d9bf40f0c..00000000000 --- a/component/examples/paginate_events_next.html +++ /dev/null @@ -1,29 +0,0 @@ - -
    - - diff --git a/component/examples/paginate_events_next_update_page.html b/component/examples/paginate_events_next_update_page.html deleted file mode 100644 index 7020ca1fd83..00000000000 --- a/component/examples/paginate_events_next_update_page.html +++ /dev/null @@ -1,32 +0,0 @@ - -
    - - diff --git a/component/examples/paginate_next.html b/component/examples/paginate_next.html deleted file mode 100644 index 1b9d8d15c9b..00000000000 --- a/component/examples/paginate_next.html +++ /dev/null @@ -1,25 +0,0 @@ - -
    - - diff --git a/component/examples/paginate_next_event.html b/component/examples/paginate_next_event.html deleted file mode 100644 index f01bf228e9d..00000000000 --- a/component/examples/paginate_next_event.html +++ /dev/null @@ -1,62 +0,0 @@ - - -
    -
    - - -
    - - \ No newline at end of file diff --git a/component/examples/selected.html b/component/examples/selected.html deleted file mode 100644 index 72278e3fcd4..00000000000 --- a/component/examples/selected.html +++ /dev/null @@ -1,63 +0,0 @@ - - -
    -
    - -
    - - - diff --git a/component/examples/tabs.component b/component/examples/tabs.component deleted file mode 100644 index 63b16cc2d83..00000000000 --- a/component/examples/tabs.component +++ /dev/null @@ -1,98 +0,0 @@ - - - - - import can from "can"; - - export default can.Map.extend({ - // Contains a list of all panel scopes within the - // tabs element. - panels: [], - // When a `` element is inserted into the document, - // it calls this method to add the panel's scope to the - // panels array. - addPanel: function(panel){ - // If this is the first panel, activate it. - if( this.attr("panels").length === 0 ) { - this.makeActive(panel); - } - this.attr("panels").push(panel); - }, - // When a `` element is removed from the document, - // it calls this method to remove the panel's scope from - // the panels array. - removePanel: function(panel){ - var panels = this.attr("panels"); - can.batch.start(); - panels.splice(panels.indexOf(panel),1); - // if the panel was active, make the first item active - if(panel === this.attr("active")){ - if(panels.length){ - this.makeActive(panels[0]); - } else { - this.removeAttr("active") - } - } - can.batch.stop() - }, - makeActive: function(panel){ - this.attr("active",panel); - this.attr("panels").each(function(panel){ - panel.attr("active", false) - }); - panel.attr("active",true); - - }, - // this is scope, not stache - // consider removing scope as arg - isActive: function( panel ) { - return this.attr('active') == panel; - } - }); - - - export default {} - - - export default {} - - diff --git a/component/examples/tabs.html b/component/examples/tabs.html deleted file mode 100644 index c28e524482b..00000000000 --- a/component/examples/tabs.html +++ /dev/null @@ -1,144 +0,0 @@ - -
    -
    - -
    - diff --git a/component/examples/treecombo.html b/component/examples/treecombo.html deleted file mode 100644 index 24301c0706e..00000000000 --- a/component/examples/treecombo.html +++ /dev/null @@ -1,176 +0,0 @@ - -
    - - - diff --git a/component/extend.md b/component/extend.md deleted file mode 100644 index 08e29137bfe..00000000000 --- a/component/extend.md +++ /dev/null @@ -1,37 +0,0 @@ -@function can.Component.extend -@parent can.Component.static - -Extends the [can.Component] constructor function. - -@signature `can.Component.extend(proto)` - -Extends the [can.Component] constructor function with prototype -properties and methods. - -@param {{}} proto An object set as the prototype of the -constructor function. You will typically provide the following values -on the prototype object. - -@option {can.Component.prototype.tag} tag Defines the -tag on which instances of the component constructor function will be -created. - -@option {can.Component.prototype.events} [events] Defines events on -dom elements or observable objects the component listens to. - - -@option {can.Component.prototype.helpers} [helpers] Specifies stache helpers -used to render the component's template. - -@option {can.Component.prototype.viewModel} [viewModel] Specifies an object -that is is used to render the component's template. - -@option {can.Component.prototype.tempate} [template] Specifies the template -rendered within the custom element. - -@body - - -Note that inheriting from components works differently than other CanJS APIs. You can't call `.extend` on a particular component to create a "subclass" of that component. - -Instead, components work more like HTML elements. To reuse functionality from a base component, build on top of it with parent components that wrap other components in their template and pass any needed viewModel properties via attributes. diff --git a/component/helpers.md b/component/helpers.md deleted file mode 100644 index 230c98b9c6f..00000000000 --- a/component/helpers.md +++ /dev/null @@ -1,21 +0,0 @@ -@property {Object.} can.Component.prototype.helpers -@parent can.Component.prototype - -Helper functions used with the component's template. - -@option {Object.} - -An object of [can.stache] helper names and methods. The helpers are only -available within the component's template and source html. Helpers -are always called back with `this` as the [can.Component::viewModel viewModel]. - -@body - -## Use - -[can.Component]'s helper object lets you provide helper functions that are localized to -the component's [can.Component::template template]. The following example -uses an `isSelected` helper to render content for selected items. Click -one of the following libraries to toggle them within the `selected` array. - -@demo can/component/examples/selected.html diff --git a/component/leakscope.md b/component/leakscope.md deleted file mode 100644 index b0f2ae37c51..00000000000 --- a/component/leakscope.md +++ /dev/null @@ -1,85 +0,0 @@ -@property {Boolean} can.Component.prototype.leakScope -@parent can.Component.prototype - -@description Allow reading the outer scope values from a component's template and -a component's viewModel values in the user content. - -@option {Boolean} `false` limits reading to: - -- the component's viewModel from the component's template, and -- the outer scope values from the user content. - -`true` adds the ability to read: - -- the outer [can.view.Scope scope] values from the component's template, and -- the component's [can.Component.prototype.viewModel viewModel] values from the user content. - -The default value is `true`. This may reverse in 3.0. - -@body - -## Use - -A component's [can.Component::leakScope leakScope] option controls if a -component's template can access the component's outer scope and the -user content can read the component's view model. - -Lets define what __outer scope__, __component's template__ and __user content__ mean. - -If I have a `` component in a template like: - -``` -{{#data}} - {{subject}} -{{/data}} -``` - -The __outer scope__ of `` has `data` as its context. The __user content__ of -`` is the template between its tags. In this case, the __user content__ -is `{{subject}}`. - -Finally, if `` is defined like: - -``` -can.Component.extend({ - tag: "hello-world", - template: can.stache("{{greeting}} {{exclamation}}") -}) -``` - -`{{greeting}} {{exclamation}}` represents the __component's template__. - -## Example - -If the following component is defined: - - can.Component.extend({ - tag: "hello-world", - leakScope: true, // the default value - template: can.stache("{{greeting}} {{exclamation}}"), - viewModel: { subject: "LEAK", exclamation: "!" } - }) - -And used like so: - - {{subject}} - -With the following data in the outer scope: - - { greeting: "Hello", subject: "World"} - -Will render the following if `leakScope` is true: - - Hello LEAK! - -But if `leakScope` is false: - - Hello World - -Because when the scope isn't leaked, the __component's template__ -does not see `exclamation`. The __user content__ does not see the -viewModel's `subject` and uses the outer scope's `subject` which is `"World"`. - -Using the `leakScope: false` option is useful for hiding and protecting -internal details of `can.Component`, potentially preventing accidental -clashes. diff --git a/component/scope.md b/component/scope.md deleted file mode 100644 index cc247c6c456..00000000000 --- a/component/scope.md +++ /dev/null @@ -1,5 +0,0 @@ -@property {*} can.Component.prototype.scope -@parent can.Component.prototype - -@deprecated {2.2} In 2.2 `scope` has been renamed to [can.Component::viewModel] to avoid confusion with [can.view.Scope]. `scope` is still available for backwards compatibility. - diff --git a/component/tag.md b/component/tag.md deleted file mode 100644 index f07983805a5..00000000000 --- a/component/tag.md +++ /dev/null @@ -1,11 +0,0 @@ -@property {String} can.Component.prototype.tag -@parent can.Component.prototype - -Specifies the HTML tag (or node-name) the [can.Component] will be created on. - -@option {String} The tag name the [can.Component] -will be created on. Tag names are typically lower cased and -hypenated like: `foo-bar`. Component's register their -tag with [can.view.tag]. - - diff --git a/component/template.md b/component/template.md deleted file mode 100644 index 31491cd0aa6..00000000000 --- a/component/template.md +++ /dev/null @@ -1,177 +0,0 @@ -@property {can.view.renderer|String} [can.Component.prototype.template] -@parent can.Component.prototype - -Provides a template to render directly within the component's tag. The template is rendered with the -component's [can.Component::viewModel viewModel]. `` elements within the template are replaced by -the source elements within the component's tag. - -@option {can.view.renderer} A [can.view.renderer] returned by [can.stache] or -[can.view]. For example: - - can.Component({ - tag: "my-tabs", - template: can.stache("
      {{#panels}}
    • {{title}}
    • ...") - }) - - -@option {String} The string contents of a [can.stache] template. For example: - - can.Component({ - tag: "my-tabs", - template: "
        {{#panels}}
      • {{title}}
      • ..." - }) - -Note: Using mustache is deprecated. Please switch to [can.stache]. - - -@body - - -## Use - -The template specified by the `template` property works similar to -the [http://www.w3.org/TR/shadow-dom/ W3C Shadow DOM proposal]. It represents the contents -of a custom element, while being able to reposition the user provided __source__ elements -with the `` tag. - -There are three things to understand about a [can.Component]'s template: - - - It is inserted into the component's tag. - - It is rendered with access to the component instance's viewModel. - - `` tags within the template act as insertion points for the source elements. - -The following example demonstrates all three features: - -@demo can/component/examples/my_greeting_full.html - -The following explains how each part works: - -__can.Component:__ - - can.Component({ - tag: "my-greeting", - template: can.stache("

        "), - viewModel: { - title: "can.Component" - } - }) - -This registers a component for elements like ``. Its template -will place an `

        ` element directly within `` and put -the original contents of `` within the `

        `. The component's -[can.Component::viewModel viewModel] adds a title value. - -__Source template:__ - -
        - - {{site}} - {{title}} - -
        - -The source template is the template that -uses ``. In the demo, this is defined within a ` -
        - - - - - - - - - - diff --git a/component/view-model.md b/component/view-model.md deleted file mode 100644 index ad0e154e32d..00000000000 --- a/component/view-model.md +++ /dev/null @@ -1,285 +0,0 @@ -@property {Object|can.Map|function} can.Component.prototype.viewModel -@parent can.Component.prototype - -@description - -Provides or describes a [can.Map] constructor function or `can.Map` instance that will be -used to retrieve values found in the component's [can.Component::template template]. The map -instance is initialized with values specified by the component element's attributes. - -__Note:__ This page documents behavior of components in [can.stache]. [can.stache] behaves -slightly differently. If you want the behavior of components with [can.stache], -please look at versions of this page prior to 2.3. In 2.3, use [can.view.bindings] [can.view.bindings.toChild], -[can.view.bindings.toParent] and [can.view.bindings.twoWay] to setup viewModel -bindings. - -@option {Object} A plain JavaScript object that is used to define the prototype methods and properties of -[can.Construct constructor function] that extends [can.Map]. For example: - - can.Component.extend({ - tag: "my-paginate", - viewModel: { - offset: 0, - limit: 20, - next: function(){ - this.attr("offset", this.offset + this.limit); - } - } - }); - -@option {can.Map} A `can.Map` constructor function will be used to create an instance of the observable -`can.Map` placed at the head of the template's viewModel. For example: - - var Paginate = can.Map.extend({ - offset: 0, - limit: 20, - next: function(){ - this.attr("offset", this.offset + this.limit); - } - }) - can.Component.extend({ - tag: "my-paginate", - viewModel: Paginate - }) - - -@option {function} Returns the instance or constructor function of the object that will be added -to the viewModel. - -@param {Object} attrs An object of values specified by the custom element's attributes. For example, -a template rendered like: - - can.stache("")({ - name: "Justin" - }) - -Creates an instance of following control: - - can.Component.extend({ - tag: "my-element", - viewModel: function(attrs){ - attrs.title //-> "Justin"; - return new can.Map(attrs); - } - }) - -And calls the viewModel function with `attrs` like `{title: "Justin"}`. - -@param {can.view.viewModel} parentScope - -The viewModel the custom tag was found within. By default, any attribute's values will -be looked up within the current viewModel, but if you want to add values without needing -the user to provide an attribute, you can set this up here. For example: - - can.Component.extend({ - tag: "my-element", - viewModel: function(attrs, parentScope){ - return new can.Map({title: parentScope.attr('name')}); - } - }); - -Notice how the attribute's value is looked up in `my-element`'s parent viewModel. - -@param {HTMLElement} element The element the [can.Component] is going to be placed on. If you want -to add custom attribute handling, you can do that here. For example: - - can.Component.extend({ - tag: "my-element", - viewModel: function(attrs, parentScope, el){ - return new can.Map({title: el.getAttribute('title')}); - } - }); - -@return {can.Map|Object} Specifies one of the following: - - - The data used to render the component's template. - - The prototype of a `can.Map` that will be used to render the component's template. - -@option {can.Map} If an instance of `can.Map` is returned, that instance is placed -on top of the viewModel and used to render the component's template. - -@option {Object} If a plain JavaScript object is returned, that is used as a prototype -definition used to extend `can.Map`. A new instance of the extended Map is created. - -@body - -## Use - -[can.Component]'s viewModel property is used to define an __object__, typically an instance -of a [can.Map], that will be used to render the component's -template. This is most easily understood with an example. The following -component shows the current page number based off a `limit` and `offset` value: - - can.Component.extend({ - tag: "my-paginate", - viewModel: { - offset: 0, - limit: 20, - page: function(){ - return Math.floor(this.attr('offset') / this.attr('limit')) + 1; - } - }, - template: can.stache("Page {{page}}.") - }) - -If this component HTML was inserted into the page like: - - var template = can.stache("") - $("body").append(template()) - -It would result in: - - Page 1 - -This is because the provided viewModel object is used to extend a can.Map like: - - CustomMap = can.Map.extend({ - offset: 0, - limit: 20, - page: function(){ - return Math.floor(this.attr('offset') / this.attr('limit')) + 1; - } - }) - -Any primitives found on a `can.Map`'s prototype (ex: `offset: 0`) are used as -default values. - -Next, a new instance of CustomMap is created with the attribute data within `` -(in this case there is none) like: - - componentData = new CustomMap(attrs); - -And finally, that data is added to the [can.view.Scope parentScope] of the component, used to -render the component's template, and inserted into the element: - - var newviewModel = parentScope.add(componentData), - result = can.stache("Page {{page}}.")(newviewModel); - $(element).html(result); - -## Values passed from attributes - -Values can be "passed" into the viewModel of a component, similar to passing arguments into a function. Using -[can.view.bindings], the following binding types can be setup: - -- [can.view.bindings.toChild] - Update the component's viewModel when the parent scope value changes. -- [can.view.bindings.toParent] - Update the parent scope when the component's viewModel changes. -- [can.view.bindings.twoWay] - Update the parent scope or the component's viewModel when the other changes. - -As mentioned in the deprecation warning above, using [can.stache], values are passed into components like this: - - - -The above would create an offset and limit property on the component that are initialized to whatever index and size are, NOT two-way bind (between component and parent viewModels) -the offset and limit properties to the index and size. - -The following component requires an `offset` and `limit`: - - can.Component.extend({ - tag: "my-paginate", - viewModel: { - page: function(){ - return Math.floor(this.attr('offset') / this.attr('limit')) + 1; - } - }, - template: can.stache("Page {{page}}.") - }); - -If ``'s source html is rendered like: - - var template = can.stache(""); - - var pageInfo = new can.Map({ - index: 0, - size: 20 - }); - - $("body").append( template( pageInfo ) ); - -... `pageInfo`'s index and size are set as the component's offset and -limit attributes. If we were to change the value of `pageInfo`'s -index like: - - pageInfo.attr("index",20) - -... the component's offset value will change and its template will update to: - - Page 1 - -### Using attribute values - -You can also pass a literal string value of the attribute. To do this in [can.stache], -simply pass any value not wrapped in single brackets, and the viewModel property will -be initialized to this string value: - - - -The above will create a title property in the component's viewModel, which has a string `hello`. - -If the tag's `title` attribute is changed, it updates the viewModel property -automatically. This can be seen in the following example: - -@demo can/component/examples/accordion.html - -Clicking the __Change title__ button sets a `` element's `title` attribute like: - - $("#out").on("click", "button", function(){ - $("panel:first").attr("title", "Users") - $(this).remove(); - }); - - -## Calling methods on viewModel from events within the template - -Using html attributes like `can-EVENT-METHOD`, you can directly call a viewModel method -from a template. For example, we can make `` elements include a next -button that calls the viewModel's `next` method like: - - can.Component.extend({ - tag: "my-paginate", - viewModel: { - offset: 0, - limit: 20, - next: function(context, el, ev){ - this.attr("offset", this.offset + this.limit); - }, - page: function(){ - return Math.floor(this.attr('offset') / this.attr('limit')) + 1; - } - }, - template: can.stache("Page {{page}} ") - }) - -viewModel methods get called back with the current context, the element that you are listening to -and the event that triggered the callback. - -@demo can/component/examples/paginate_next.html - -## Publishing events on viewModels - -Maps can publish events on themselves. For instance, the following `` component, -dispatches a `"close"` event when it's close method is called: - -``` -can.Component.extend({ - tag: "player-edit", - template: can.view('player-edit-stache'), - viewModel: { - close: function(){ - this.dispatch("close"); - } - } -}); -``` - -These can be listened to with [can.view.bindings.event] bindings like: - -``` - -``` - -The following demo uses this ability to create a close button that -hides the player editor: - -@demo can/component/examples/paginate_next_event.html diff --git a/compute/async.md b/compute/async.md deleted file mode 100644 index c28e9891ea0..00000000000 --- a/compute/async.md +++ /dev/null @@ -1,44 +0,0 @@ -@function can.compute.async -@parent can.compute -@release 2.1 -@hide - -Create a compute that can set its value after the computed function has been called. - -@signature `can.compute.async(initialValue, computed(currentValue, setValue(newValue) )` - -@param {*} The initial value of the compute. - -@param {can.compute.asyncComputer} computed A function -that returns the current value of the compute and can optionally later call -its `setValue` callback to update the value. - -@return {can.computed} Returns a compute, but a compute that will -possibly not have the correct value unless it is bound to. - -@body - -## Use - -The following compute is a live list of todos for a given -userId. `todos` value would alternate between `null` and a Todo.List as `userId` changes. - - - var userId = can.compute(5) - - var todos = can.compute.async(null, function(oldTodoList, setValue){ - Todo.findAll({ userId: userId() }, function(todos){ - setValue(todos) - }); - return null; - }); - - -The following replaces the list in place: - - var userId = can.compute(5) - - var todos = can.compute.async(new Todo.List(), function(todoList, setValue){ - todoList.replace( Todo.findAll({ userId: userId() }) - return todoList; - }); diff --git a/compute/async_computer.md b/compute/async_computer.md deleted file mode 100644 index 4e69637aa3a..00000000000 --- a/compute/async_computer.md +++ /dev/null @@ -1,19 +0,0 @@ -@typedef {function} can.compute.asyncComputer(currentVal, setVal) -@parent can.compute -@release 2.1 -@hide - -A function that determines a value for an [can.compute.async async compute]. - -@option {function} The function callback to [can.compute.async] that determines -the value of the compute. - -@param {*} [lastSetValue] The last set value of the compute. This should be returned -if you are doing an in-place compute. - -@param {function(*)} [setVal(newVal)] Called to update the value -of the compute at a later time. - -@return {*} If a `setVal` argument is not provided, the return value -is set as the current value of the compute. If `setVal` is provided and -undefined is returned, the current value remains until `setVal` is called. diff --git a/compute/benchmark/benchmark.html b/compute/benchmark/benchmark.html deleted file mode 100644 index 0a77cf755a7..00000000000 --- a/compute/benchmark/benchmark.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/compute/benchmark/compute_benchmark.js b/compute/benchmark/compute_benchmark.js deleted file mode 100644 index e499874d620..00000000000 --- a/compute/benchmark/compute_benchmark.js +++ /dev/null @@ -1,106 +0,0 @@ -require('can/compute/compute'); -require('can/compute/proto_compute'); -require('steal-benchmark'); -require('can/map/map'); - /* jshint ignore:start */ -b.suite("can/compute") - -.add("compute that reads two props", - function () { - can.batch.start(); - num++; - person.attr({ - first: "Bob"+num, - last: "Marley"+num - }); - can.batch.stop(); - },{ - setup: function(){ - var person = new can.Map({ - first: 'Bob', - last: 'Marley' - }); - var c = new can.Compute(function() { - return person.attr('first') + person.attr('last'); - }); - c.bind('change', function() {}); - var num = 0; - } - }); - -/* -module('can.compute'); -test('create/bind/read', function() { - expect(0); - - benchmarks.add( - "can.compute create/bind/read", - function () {}, - function () { - var c = can.compute(0); - c.bind('change', function() {}); - c(1); - c(); - }, - function () {}); -}); - -test('create/bind/read with multiple attributes', function() { - expect(0); - - benchmarks.add( - "can.compute create/bind/read with multiple attributes", - function () { - var person = new can.Map({ - first: 'Bob', - last: 'Marley' - }); - }, - function () { - var c = can.compute(function() { - return person.attr('first') + person.attr('last'); - }); - c.bind('change', function() {}); - c(); - }, - function () {}); -}); - -module('can.Compute'); -test('create/bind/read', function() { - expect(0); - - benchmarks.add( - "can.Compute create/bind/read", - function () {}, - function () { - var c = new can.Compute(0); - c.bind('change', function() {}); - - c.set(1); - c.get(); - }, - function () {}); -}); - -test('create/bind/read with multiple attributes', function() { - expect(0); - - benchmarks.add( - "can.Compute create/bind/read with multiple attributes", - function () { - var person = new can.Map({ - first: 'Bob', - last: 'Marley' - }); - }, - function () { - var c = new can.Compute(function() { - return person.attr('first') + person.attr('last'); - }); - c.bind('change', function() {}); - c.get(); - }, - function () {}); -});*/ -/* jshint ignore:end */ diff --git a/compute/compute.js b/compute/compute.js index 3dde5f7bfa7..1d35c73d314 100644 --- a/compute/compute.js +++ b/compute/compute.js @@ -1,96 +1,3 @@ -/* jshint maxdepth:7*/ +var can = require('../util/can'); -// # can.compute -// -// `can.compute` allows the creation of observable values in different forms. -// This module is now just a facade around [proto_compute.js](proto_compute.html). -// `proto_compute.js` provides `can.Compute` as a constructor function where this file, -// `compute.js` wraps an instance of a `can.Compute` with a function. -// -// Other files: -// - [get_value_and_bind.js](get_value_and_bind.js) provides the low-level utility for observing functions. -// - [read.js](read.html) provides a helper that read properties and values in an observable way. -var can = require('can/util/util'); -require('can/util/bind/bind'); -require('can/util/batch/batch'); -require('can/compute/proto_compute'); -// The `can.compute` generator function. - -can.compute = function (getterSetter, context, eventName, bindOnce) { - // Create an internal `can.Compute`. - var internalCompute = new can.Compute(getterSetter, context, eventName, bindOnce); - // The "compute" function that calls compute instance's get or set function. - var bind = internalCompute.bind; - var unbind = internalCompute.unbind; - var compute = function(val) { - if(arguments.length) { - return internalCompute.set(val); - } - - return internalCompute.get(); - }; - var cid = can.cid(compute, 'compute'); - var handlerKey = '__handler' + cid; - - compute.bind = function(ev, handler) { - var computeHandler = handler && handler[handlerKey]; - if(handler && !computeHandler) { - computeHandler = handler[handlerKey] = function() { - handler.apply(compute, arguments); - }; - } - - return bind.call(internalCompute, ev, computeHandler); - }; - compute.unbind = function(ev, handler) { - var computeHandler = handler && handler[handlerKey]; - if(computeHandler) { - delete handler[handlerKey]; - return internalCompute.unbind(ev, computeHandler); - } - return unbind.apply(internalCompute, arguments); - }; - compute.isComputed = internalCompute.isComputed; - compute.clone = function(ctx) { - if(typeof getterSetter === 'function') { - context = ctx; - } - return can.compute(getterSetter, context, ctx, bindOnce); - }; - - compute.computeInstance = internalCompute; - - return compute; -}; - -// ## Helpers - -// ### truthy -// Wraps a compute with another compute that only changes when -// the wrapped compute's `truthiness` changes. -can.compute.truthy = function (compute) { - return can.compute(function () { - var res = compute(); - if (typeof res === 'function') { - res = res(); - } - return !!res; - }); -}; - -// ### async -// A simple helper that makes an async compute a bit easier. -can.compute.async = function(initialValue, asyncComputer, context){ - return can.compute(initialValue, { - fn: asyncComputer, - context: context - }); -}; - -// ### compatability -// Setting methods that should not be around in 3.0. -can.compute.read = can.Compute.read; -can.compute.set = can.Compute.set; -can.compute.temporarilyBind = can.Compute.temporarilyBind; - -module.exports = exports = can.compute; +module.exports = can.compute = require('can-compute'); diff --git a/compute/compute.md b/compute/compute.md deleted file mode 100644 index ac39b8d0c47..00000000000 --- a/compute/compute.md +++ /dev/null @@ -1,291 +0,0 @@ -@function can.compute -@parent canjs -@release 1.1 -@link ../docco/compute/compute.html docco - -@description Create an observable value. - -@signature `can.compute( getterSetter[, context] )` - -Create a compute that derives its value from [can.Map]s and other [can.computed can.compute]s. - -@param {function(*+,*+)} getterSetter(newVal,oldVal) A function that gets and optionally sets the value of the compute. -When called with no parameters, _getterSetter_ should return the current value of the compute. When -called with a single parameter, _getterSetter_ should arrange things so that the next read of the compute -produces that value. This compute will automatically update its value when any [can.Map observable] -values are read via [can.Map.prototype.attr]. - -@param {Object} [context] The `this` to use when calling the `getterSetter` function. -@return {can.computed} A new compute. - - -@signature `can.compute( initialValue [, settings] )` - -Creates a compute from a value and optionally specifies how to read, update, and -listen to changes in dependent values. This form of can.compute can be used to -create a compute that derives its value from any source. - -@param {*} initialValue The initial value of the compute. If `settings` is -not provided, the compute simply updates its value to whatever the first argument -to the compute is. - - var age = can.compute(30); - age() //-> 30 - age(31) //-> fires a "change" event - -@param {computeSettings} [settings] - -Configures all behaviors of the [can.computed compute]. The following cross -binds an input element to a compute: - - var input = document.getElementById("age") - var value = can.compute("",{ - get: function(){ - return input.value; - }, - set: function(newVal){ - input.value = newVal; - }, - on: function(updated){ - input.addEventListener("change", updated, false); - }, - off: function(updated){ - input.removeEventListener("change", updated, false); - } - }) - - -@return {can.computed} The new compute. - - - -@signature `can.compute( initialValue, setter(newVal,oldVal) )` - -Create a compute that has a setter that can adjust incoming new values. - - var age = can.compute(6,function(newVal, oldVal){ - if(!isNaN(+newVal)){ - return +newVal; - } else { - return oldVal; - } - }) - - - -@param {*} initialValue - -The initial value of the compute. - -@param {function(*,*):*} setter(newVal,oldVal) - -A function that is called when a compute is called with an argument. The function is passed -the first argumented passed to [can.computed compute] and the current value. If -`set` returns a value, it is used to compare to the current value of the compute. Otherwise, -`get` is called to get the current value of the compute and that value is used -to determine if the compute has changed values. - -@return {can.computed} A new compute. - -@signature `can.compute( object, propertyName [, eventName] )` - -Create a compute from an object's property value. This short-cut -signature lets you create a compute on objects that have events -that be listened to with [can.bind]. - - var input = document.getElementById('age') - var age = can.compute(input,"value","change"); - - var me = new can.Map({name: "Justin"}); - var name = can.compute(me,"name") - -@param {Object} object An object that either has a `bind` method or -a has events dispatched on it via [can.trigger]. - -@param {String} propertyName The property value to read on `object`. The -property will be read via `object.attr(propertyName)` or `object[propertyame]`. - -@param {String} [eventName=propertyName] Specifies the event name to listen -to on `object` for `propertyName` updates. - -@return {can.computed} A new compute. - - -@body - -## Use - -`can.compute` lets you make an observable value. Computes are similar -to [can.Map Observes], but they represent a single value rather than -a collection of values. - -`can.compute` returns a [can.computed compute] function that can -be called to read and optionally update the compute's value. - -It's also possible to derive a compute's value from other computes, -[can.Map]s, and [can.List]s. When the derived values -change, the compute's value will be automatically updated. - -Use [can.computed.bind compute.bind] to listen for changes of the -compute's value. - -## Observing a value - -The simplest way to use a compute is to have it store a single value, and to set it when -that value needs to change: - - - var tally = can.compute(12); - tally(); // 12 - - tally.bind("change",function(ev, newVal, oldVal){ - console.log(newVal,oldVal) - }) - - tally(13); - tally(); // 13 - -Any value can be observed. The following creates a compute -that holds an object and then changes it to an array. - - var data = can.compute({name: "Justin"}) - data([{description: "Learn Computes"}]) - - - -## Derived computes - -If you use a compute that derives its -value from properties of a [can.Map] or other [can.compute]s, the compute will listen for changes in those -properties and automatically recalculate itself, emitting a _change_ event if its value -changes. - -The following example shows creating a `fullName` compute -that derives its value from two properties on the `person` observe: - - - var person = new can.Map({ - firstName: 'Alice', - lastName: 'Liddell' - }); - - var fullName = can.compute(function() { - return person.attr('firstName') + ' ' + person.attr('lastName'); - }); - fullName.bind('change', function(ev, newVal, oldVal) { - console.log("This person's full name is now " + newVal + '.'); - }); - - person.attr('firstName', 'Allison'); // The log reads: - //-> "This person's full name is now Allison Liddell." - - -Notice how the definition of the compute uses `[can.Map.prototype.attr attr]` -to read the values of the properties of `person`. This is how the compute knows to listen -for changes. - -## Translator computes - computes that update their derived values - -Sometimes you need a compute to be able to translate one value to another. For example, -consider a widget that displays and allows you to update the progress in percent -of a task. It accepts a compute with values between 0 and 100. But, -our task observe has progress values between 0 and 1 like: - - var task = new can.Map({ - progress: 0.75 - }) - -Use `can.compute( getterSetter )` to create a compute that updates itself -when task's `progress` changes, but can also update progress when -the compute function is called with a value. For example: - - var progressPercent = can.compute(function(percent){ - if(arguments.length){ - task.attr('progress', percent / 100) - } else { - return task.attr('progress') * 100 - } - }) - - progressPercent() // -> 75 - - progressPercent(100) - - task.attr('progress') // -> 1 - - -The following is a similar example that shows converting feet into meters and back: - -``` -var wall = new can.Map({ - material: 'brick', - length: 10 // in feet -}); - -var wallLengthInMeters = can.compute(function(lengthInM) { - if(arguments.length) { - wall.attr('length', lengthInM / 3.28084); - } else { - return wall.attr('length') * 3.28084; - } -}); - -wallLengthInMeters(); // 3.048 - -// When you set the compute... -wallLengthInMeters(5); -wallLengthInMeters(); // 5 -// ...the original Observe changes too. -wall.length; // 16.4042 -``` - -## Events - -When a compute's value is changed, it emits a _change_ event. You can listen for this change -event by using `[can.computed.bind bind]` to bind an event handler to the compute: - -``` -var tally = can.compute(0); -tally.bind('change', function(ev, newVal, oldVal) { - console.log('The tally is now at ' + newVal + '.'); -}); - -tally(tally() + 5); // The log reads: - // 'The tally is now at 5.' -``` - -## Using computes to build Controls - -It's a piece of cake to build a `[can.Control]` off of the value of a compute. And since computes -are observable, it means that the view of that Control will update itself whenever the value -of the compute updates. Here's a simple slider that works off of a compute: - -``` -var project = new can.Map({ - name: 'A Very Important Project', - percentDone: .35 -}); - -SimpleSlider = can.Control.extend({ }, { - init: function() { - this.element.html(can.view(this.options.view, this.options)); - }, - '.handle dragend': function(el, ev) { - var percent = this.calculateSliderPercent(); - // set the compute's value - this.options.percentDone(percent); - }, - '{percentDone} change': function(ev, newVal, oldVal) { - // react to the percentage changing some other way - this.moveSliderTo(newVal); - } - // Implementing calculateSliderPercent and moveSliderTo - // has been left as an exercise for the reader. -}); - -new SimpleSlider('#slider', {percentDone: project.compute('percentDone')}); -``` - -Now that's some delicious cake. More information on Controls can be found under `[can.Control]`. -There is also a full explanation of can.Map's `[can.Map.prototype.compute compute]`, -which is used in the last line of the example above. diff --git a/compute/compute_settings.md b/compute/compute_settings.md deleted file mode 100644 index dc6b402ed62..00000000000 --- a/compute/compute_settings.md +++ /dev/null @@ -1,18 +0,0 @@ -@typedef {{get: function, set: function, on: function, off: function}} computeSettings computeSettings -@release 2.1 -@parent can.compute - -@option {function} get A function that retrieves and returns the current value of the compute. -@option {function(*,*):*} set(newVal,oldVal) A function that is used when setting a new value of the compute. - -A function that is called when a compute is called with an argument. The function is passed -the first argumented passed to [can.computed compute] and the current value. If -`set` returns a value, it is used to compare to the current value of the compute. Otherwise, -`get` is called to get the current value of the compute and that value is used -to determine if the compute has changed values. - -`newVal` is the value being set, while `oldVal` is the previous value in the compute. - -@option {function(function)} on(updated) Called to setup binding to dependency events. Call `updated` when the compute's value needs to be updated. - -@option {function(function)} off Called to teardown binding. diff --git a/compute/compute_test.js b/compute/compute_test.js index 52d75ac89cc..82d319269ea 100644 --- a/compute/compute_test.js +++ b/compute/compute_test.js @@ -1,882 +1 @@ -require('can/compute/compute'); -require('can/map/map'); -require('steal-qunit'); -require('./read_test'); - -QUnit.module('can/compute'); -test('single value compute', function () { - var num = can.compute(1); - num.bind('change', function (ev, newVal, oldVal) { - equal(newVal, 2, 'newVal'); - equal(oldVal, 1, 'oldVal'); - }); - num(2); -}); -test('inner computes values are not bound to', function () { - var num = can.compute(1); - var outer = can.compute(function() { - var inner = can.compute(function() { - return num() + 1; - }); - return 2 * inner(); - }); - - var handler = function () {}; - outer.bind('change', handler); - // We do a timeout because we temporarily bind on num so that we can use its cached value. - stop(); - setTimeout(function () { - equal(num.computeInstance._bindings, 1, 'inner compute only bound once'); - equal(outer.computeInstance._bindings, 1, 'outer compute only bound once'); - start(); - }, 50); -}); - -test('can.compute.truthy', function () { - var result = 0; - var numValue; - var num = can.compute(numValue = 3); - var truthy = can.compute.truthy(num); - var tester = can.compute(function () { - if (truthy()) { - return ++result; - } else { - return ++result; - } - }); - tester.bind('change', function (ev, newVal, oldVal) { - if (num() === 0) { - equal(newVal, 2, '2 is the new val'); - } else if (num() === -1) { - equal(newVal, 3, '3 is the new val'); - } else { - ok(false, 'change should not be called'); - } - }); - equal(tester(), 1, 'on bind, we call tester once'); - num(numValue = 2); - num(numValue = 1); - num(numValue = 0); - num(numValue = -1); -}); -test('a binding compute does not double read', function () { - var sourceAge = 30, - timesComputeIsCalled = 0; - var age = can.compute(function (newVal) { - timesComputeIsCalled++; - if (timesComputeIsCalled === 1) { - ok(true, 'reading age to get value'); - } else if (timesComputeIsCalled === 2) { - equal(newVal, 31, 'the second time should be an update'); - } else if (timesComputeIsCalled === 3) { - ok(true, 'called after set to get the value'); - } else { - ok(false, 'You\'ve called the callback ' + timesComputeIsCalled + ' times'); - } - if (arguments.length) { - sourceAge = newVal; - } else { - return sourceAge; - } - }); - var info = can.compute(function () { - return 'I am ' + age(); - }); - var k = function () {}; - info.bind('change', k); - equal(info(), 'I am 30'); - age(31); - equal(info(), 'I am 31'); -}); -test('cloning a setter compute (#547)', function () { - var name = can.compute('', function (newVal) { - return this.txt + newVal; - }); - var cloned = name.clone({ - txt: '.' - }); - cloned('-'); - equal(cloned(), '.-'); -}); - -test('compute updated method uses get and old value (#732)', function () { - expect(9); - var input = { - value: 1 - }; - var value = can.compute('', { - get: function () { - return input.value; - }, - set: function (newVal) { - input.value = newVal; - }, - on: function (update) { - input.onchange = update; - }, - off: function () { - delete input.onchange; - } - }); - equal(value(), 1, 'original value'); - ok(!input.onchange, 'nothing bound'); - value(2); - equal(value(), 2, 'updated value'); - equal(input.value, 2, 'updated input.value'); - - - - value.bind('change', function (ev, newVal, oldVal) { - equal(newVal, 3, 'newVal'); - equal(oldVal, 2, 'oldVal'); - value.unbind('change', this.Constructor); - }); - ok(input.onchange, 'binding to onchange'); - - - input.value = 3; - input.onchange({}); - - ok(!input.onchange, 'removed binding'); - equal(value(), 3); -}); - -test("a compute updated by source changes within a batch is part of that batch", function(){ - - var computeA = can.compute("a"); - var computeB = can.compute("b"); - - var combined1 = can.compute(function(){ - - return computeA()+" "+computeB(); - - }); - - var combined2 = can.compute(function(){ - - return computeA()+" "+computeB(); - - }); - - var combo = can.compute(function(){ - return combined1()+" "+combined2(); - }); - - var callbacks = 0; - combo.bind("change", function(){ - if(callbacks === 0){ - ok(true, "called change once"); - } else { - ok(false, "called change multiple times"); - } - callbacks++; - }); - - can.batch.start(); - computeA("A"); - computeB("B"); - can.batch.stop(); -}); - -test("compute.async can be like a normal getter", function(){ - var first = can.compute("Justin"), - last = can.compute("Meyer"), - fullName = can.compute.async("", function(){ - return first()+" "+last(); - }); - - equal(fullName(), "Justin Meyer"); -}); - -test("compute.async operate on single value", function(){ - - var a = can.compute(1); - var b = can.compute(2); - - var obj = can.compute.async({}, function( curVal ){ - if(a()) { - curVal.a = a(); - } else { - delete curVal.a; - } - if(b()) { - curVal.b = b(); - } else { - delete curVal.b; - } - return curVal; - }); - - obj.bind("change", function(){}); - - deepEqual( obj(), {a: 1, b: 2}, "object has all properties" ); - - a(0); - - deepEqual( obj(), {b: 2}, "removed a" ); - - b(0); - - deepEqual( obj(), {}, "removed b" ); - -}); - -test("compute.async async changing value", function(){ - - var a = can.compute(1); - var b = can.compute(2); - - var async = can.compute.async(undefined,function( curVal, setVal ){ - - if(a()) { - setTimeout(function(){ - setVal("a"); - },10); - } else if(b()) { - setTimeout(function(){ - setVal("b"); - },10); - } else { - return null; - } - }); - - var changeArgs = [ - {newVal: "a", oldVal: undefined, run: function(){ a(0); } }, - {newVal: "b", oldVal: "a", run: function(){ b(0); }}, - {newVal: null, oldVal: "b", run: function(){ start(); }} - ], - changeNum = 0; - - stop(); - - - async.bind("change", function(ev, newVal, oldVal){ - var data = changeArgs[changeNum++]; - equal( newVal, data.newVal, "newVal is correct" ); - equal( oldVal, data.oldVal, "oldVal is correct" ); - - setTimeout(data.run, 10); - - }); - - - -}); - -test("compute.async read without binding", function(){ - - var source = can.compute(1); - - var async = can.compute.async([],function( curVal, setVal ){ - curVal.push(source()); - return curVal; - }); - - ok(async(), "calling async worked"); - - - -}); - - -// ======================================== -QUnit.module('can/Compute'); - -test('single value compute', function () { - expect(2); - var num = new can.Compute(1); - num.bind('change', function (ev, newVal, oldVal) { - equal(newVal, 2, 'newVal'); - equal(oldVal, 1, 'oldVal'); - }); - num.set(2); -}); - -test('inner computes values are not bound to', function () { - var num = new can.Compute(1), - numBind = num.bind, - numUnbind = num.unbind; - var bindCount = 0; - num.bind = function() { - bindCount++; - return numBind.apply(this, arguments); - }; - num.unbind = function() { - bindCount--; - return numUnbind.apply(this, arguments); - }; - var outer = new can.Compute(function() { - var inner = new can.Compute(function() { - return num.get() + 1; - }); - return 2 * inner.get(); - }); - var handler = function() {}; - outer.bind('change', handler); - // We do a timeout because we temporarily bind on num so that we can use its cached value. - stop(); - setTimeout(function() { - equal(bindCount, 1, 'compute only bound to once'); - start(); - }, 50); -}); - -test('can.Compute.truthy', function() { - var result = 0; - var num = new can.Compute(3); - var truthy = can.Compute.truthy(num); - var tester = new can.Compute(function() { - if(truthy.get()) { - return ++result; - } else { - return ++result; - } - }); - - tester.bind('change', function(ev, newVal, oldVal) { - if (num.get() === 0) { - equal(newVal, 2, '2 is the new val'); - } else if (num.get() === -1) { - equal(newVal, 3, '3 is the new val'); - } else { - ok(false, 'change should not be called'); - } - }); - equal(tester.get(), 1, 'on bind, we call tester once'); - num.set(2); - num.set(1); - num.set(0); - num.set(-1); -}); - -test('a binding compute does not double read', function () { - var sourceAge = 30, - timesComputeIsCalled = 0; - var age = new can.Compute(function (newVal) { - timesComputeIsCalled++; - if (timesComputeIsCalled === 1) { - ok(true, 'reading age to get value'); - } else if (timesComputeIsCalled === 2) { - equal(newVal, 31, 'the second time should be an update'); - } else if (timesComputeIsCalled === 3) { - ok(true, 'called after set to get the value'); - } else { - ok(false, 'You\'ve called the callback ' + timesComputeIsCalled + ' times'); - } - if (arguments.length) { - sourceAge = newVal; - } else { - return sourceAge; - } - }); - - var info = new can.Compute(function () { - return 'I am ' + age.get(); - }); - - var k = function () {}; - info.bind('change', k); - equal(info.get(), 'I am 30'); - age.set(31); - equal(info.get(), 'I am 31'); -}); - -test('cloning a setter compute (#547)', function () { - var name = new can.Compute('', function(newVal) { - return this.txt + newVal; - }); - - var cloned = name.clone({ - txt: '.' - }); - - cloned.set('-'); - equal(cloned.get(), '.-'); -}); - -test('compute updated method uses get and old value (#732)', function () { - expect(9); - - var input = { - value: 1 - }; - - var value = new can.Compute('', { - get: function () { - return input.value; - }, - set: function (newVal) { - input.value = newVal; - }, - on: function (update) { - input.onchange = update; - }, - off: function () { - delete input.onchange; - } - }); - - equal(value.get(), 1, 'original value'); - ok(!input.onchange, 'nothing bound'); - value.set(2); - equal(value.get(), 2, 'updated value'); - equal(input.value, 2, 'updated input.value'); - - value.bind('change', function (ev, newVal, oldVal) { - equal(newVal, 3, 'newVal'); - equal(oldVal, 2, 'oldVal'); - value.unbind('change', this.Constructor); - }); - - ok(input.onchange, 'binding to onchange'); - - input.value = 3; - input.onchange({}); - - ok(!input.onchange, 'removed binding'); - equal(value.get(), 3); -}); - -test('a compute updated by source changes within a batch is part of that batch', function () { - var computeA = new can.Compute('a'); - var computeB = new can.Compute('b'); - - var combined1 = new can.Compute(function() { - return computeA.get() + ' ' + computeB.get(); - }); - - var combined2 = new can.Compute(function() { - return computeA.get() + ' ' + computeB.get(); - }); - - var combo = new can.Compute(function() { - return combined1.get() + ' ' + combined2.get(); - }); - - - var callbacks = 0; - combo.bind('change', function(){ - if(callbacks === 0){ - ok(true, 'called change once'); - } else { - ok(false, 'called change multiple times'); - } - callbacks++; - }); - - can.batch.start(); - computeA.set('A'); - computeB.set('B'); - can.batch.stop(); -}); - -test('compute.async can be like a normal getter', function() { - var first = new can.Compute('Justin'), - last = new can.Compute('Meyer'), - fullName = can.Compute.async('', function(){ - return first.get() + ' ' + last.get(); - }); - - equal(fullName.get(), 'Justin Meyer'); -}); - -test('compute.async operate on single value', function() { - var a = new can.Compute(1); - var b = new can.Compute(2); - - var obj = can.Compute.async({}, function(curVal) { - if(a.get()) { - curVal.a = a.get(); - } else { - delete curVal.a; - } - - if(b.get()) { - curVal.b = b.get(); - } else { - delete curVal.b; - } - - return curVal; - }); - - obj.bind('change', function() {}); - deepEqual(obj.get(), {a: 1, b: 2}, 'object has all properties'); - - a.set(0); - deepEqual(obj.get(), {b: 2}, 'removed a'); - - b.set(0); - deepEqual(obj.get(), {}, 'removed b'); -}); - -test('compute.async async changing value', function() { - var a = new can.Compute(1); - var b = new can.Compute(2); - - var async = can.Compute.async(undefined, function(curVal, setVal) { - if(a.get()) { - setTimeout(function() { - setVal('a'); - }, 10); - } else if(b.get()) { - setTimeout(function() { - setVal('b'); - }, 10); - } else { - return null; - } - }); - - var changeArgs = [ - {newVal: 'a', oldVal: undefined, run: function() { a.set(0); } }, - {newVal: 'b', oldVal: 'a', run: function() { b.set(0); }}, - {newVal: null, oldVal: 'b', run: function() { start(); }} - ], - changeNum = 0; - - stop(); - - async.bind('change', function(ev, newVal, oldVal) { - var data = changeArgs[changeNum++]; - equal( newVal, data.newVal, 'newVal is correct' ); - equal( oldVal, data.oldVal, 'oldVal is correct' ); - - setTimeout(data.run, 10); - }); -}); - -test('compute.async read without binding', function() { - var source = new can.Compute(1); - - var async = can.Compute.async([],function( curVal, setVal ) { - curVal.push(source.get()); - return curVal; - }); - - ok(async.get(), 'calling async worked'); -}); - -test('Compute.async set uses last set or initial value', function() { - - var add = new can.Compute(1); - - var fnCount = 0; - - var async = can.Compute.async(10,function( curVal ) { - switch(fnCount++) { - case 0: - equal(curVal, 10); - break; - case 1: - equal(curVal, 20); - break; - case 2: - equal(curVal, 30, "on bind"); - break; - case 3: - equal(curVal, 30, "on bind"); - break; - } - return curVal+add.get(); - }); - - equal(async.get(), 11, "initial value"); - - async.set(20); - - async.bind("change", function(){}); - - async.set(20); - - async.set(30); -}); - - - -test("setting compute.async with a observable dependency gets a new value and can re-compute", 4, function(){ - // this is needed for define with a set and get. - var compute = can.compute(1); - var add; - - var async = can.compute.async(1, function(curVal){ - add = curVal; - return compute()+add; - }); - - - equal( async(), 2, "can read unbound"); - - async.bind("change", function(ev, newVal, oldVal){ - equal(newVal, 3, "change new val"); - equal(oldVal, 2, "change old val"); - }); - - - async(2); - - equal( async(), 3, "can read unbound"); -}); - -test('compute.async getter has correct when length === 1', function(){ - var m = new can.Map(); - - var getterCompute = can.compute.async(false, function (singleArg) { - equal(this, m, 'getter has the right context'); - }, m); - - getterCompute.bind('change', can.noop); -}); - -test("bug with nested computes and batch ordering (#1519)", function(){ - - var root = can.compute('a'); - - var isA = can.compute(function(){ - return root() ==='a'; - }); - - var isB = can.compute(function(){ - return root() === 'b'; - }); - - var combined = can.compute(function(){ - var valA = isA(), - valB = isB(); - - return valA || valB; - }); - - equal(combined(), true); - - combined.bind('change', function(){ }); - - - - can.batch.start(); - root('b'); - can.batch.stop(); - - equal(combined(), true); - //equal(other(), 2); -}); - -test('compute change handler context is set to the function not can.Compute', function() { - var comp = can.compute(null); - - comp.bind('change', function() { - equal(typeof this, 'function'); - }); - - comp('test'); -}); - -test('Calling .unbind() on un-bound compute does not throw an error', function () { - var count = can.compute(0); - count.unbind('change'); - ok(true, 'No error was thrown'); -}); - - -test("dependent computes update in the right order (2093)", function() { - - var root = can.compute('a'), - childB = can.compute(function() { - return root(); - }), - combine = can.compute(function() { - return root() + childB(); - }); - - combine.bind("change", function(ev, newVal) { - equal(newVal, "bb", "concat changed"); - }); - root('b'); -}); - -test("dependent computes update in the right order with a batch (#2093)", function() { - - // so the problem is that `child` then `combine` happens. - // without a batch, child change fires before `combine`, firing `grandChild`, which - // then triggers `combine`. - - - // the goal should be for - var root = can.compute('a'), - child = can.compute(function() { - return root(); - }), - child2 = can.compute(function(){ - return root(); - }), - grandChild = can.compute(function(){ - return child(); - }), - combine = can.compute(function() { - return child2()+grandChild(); - }); - - /*console.log("root", root.computeInstance._cid, - "child", child.computeInstance._cid, - "grandChild", grandChild.computeInstance._cid, - "combine", combine.computeInstance._cid);*/ - - combine.bind("change", function(ev, newVal) { - equal(newVal, "bb", "concat changed"); - }); - - /*root.bind("change", function(ev, newVal){ - console.log("root change", ev.batchNum) - }); - child.bind("change", function(ev, newVal){ - console.log("child change", ev.batchNum) - }); - grandChild.bind("change", function(ev, newVal){ - console.log("grandChild change", ev.batchNum) - });*/ - - can.batch.start(); - root('b'); - can.batch.stop(); -}); - -test("bug with nested computes and batch ordering (#1519)", function(){ - - var root = can.compute('a'); - - var isA = can.compute(function(){ - return root() ==='a'; - }); - - var isB = can.compute(function(){ - return root() === 'b'; - }); - - var combined = can.compute(function(){ - var valA = isA(), - valB = isB(); - - return valA || valB; - }); - - equal(combined(), true); - - combined.bind('change', function(){ }); - - - - can.batch.start(); - root('b'); - can.batch.stop(); - - equal(combined(), true); - //equal(other(), 2); -}); - -test("binding, unbinding, and rebinding works after a timeout (#2095)", function(){ - var root = can.compute(1), - derived = can.compute(function(){ - return root(); - }); - - var change = function(){}; - derived.bind("change", change); - derived.unbind("change", change); - - stop(); - setTimeout(function(){ - derived.bind("change", function(ev, newVal, oldVal){ - equal(newVal, 2, "updated"); - start(); - }); - root(2); - },10); - -}); - -test("can.__isRecording observes doesn't understand can.__notObserve (#2099)", function(){ - expect(0); - var compute = can.compute(1); - compute.computeInstance.bind = function() { - ok(false); - }; - - var outer = can.compute(function(){ - can.__notObserve(function(){ - compute(); - })(); - }); - - outer.bind("change", function(){}); -}); - -test("handles missing update order items (#2121)",function(){ - var root1 = can.compute("root1"), - child1 = can.compute(function(){ - return root1(); - }), - root2 = can.compute("root2"), - child2 = can.compute(function(){ - return root2(); - }), - gc2 = can.compute(function(){ - return child2(); - }), - res = can.compute(function(){ - return child1() + gc2(); - }); - - res.bind("change", function(ev, newVal){ - equal(newVal, "ROOT1root2"); - }); - - can.batch.start(); - root1("ROOT1"); - can.batch.stop(); - -}); - -test("compute should not fire event when NaN is set multiple times #2128", function() { - var compute = can.compute(NaN); - - compute.bind("change", function() { - ok(false, "change event should not be fired"); - }); - - ok(isNaN(compute())); - compute(NaN); -}); - -test("can.batch.afterPreviousEvents firing too late (#2198)", function(){ - - - var compute1 = can.compute("a"), - compute2 = can.compute("b"); - - var derived = can.compute(function() { - return compute1().toUpperCase(); - }); - - derived.bind("change", function() { - var afterPrevious = false; - - compute2.bind("change", function() { - ok(afterPrevious, "after previous should have fired so we would respond to this event"); - }); - - can.batch.start(); - can.batch.stop(); - - // we should get this callback before we are notified of the change - can.batch.afterPreviousEvents(function() { - afterPrevious = true; - }); - - compute2("c"); - }); - - can.batch.start(); - compute1("x"); - can.batch.stop(); -}); +require('can-compute/can-compute_test'); diff --git a/compute/computed.md b/compute/computed.md deleted file mode 100644 index f03491a3d0f..00000000000 --- a/compute/computed.md +++ /dev/null @@ -1,13 +0,0 @@ -@function can.computed compute -@parent can.compute - -@signature `compute( [newVal] )` - -@param {*} [newVal] If `compute` is called with an argument, the first argument is used -to set the compute to a new value. This may trigger a -`"change"` event that can be listened for with [can.computed.bind]. - -If the compute is called without any arguments (`compute()`), it simply returns -the current value of the compute. - -@return {*} The current value of the compute. diff --git a/compute/get_value_and_bind.js b/compute/get_value_and_bind.js deleted file mode 100644 index 5a1abfac0f9..00000000000 --- a/compute/get_value_and_bind.js +++ /dev/null @@ -1,305 +0,0 @@ -// # can/compute/get_value_and_bind -// -// This module: -// -// Exports a function that calls an arbitrary function and binds to any observables that -// function reads. When any of those observables change, a callback function is called. -// -// And ... -// -// Adds two main methods to can: -// -// - can.__observe - All other observes call this method to be visible to computed functions. -// - can.__notObserve - Returns a function that can not be observed. -var can = require('can/util/util'); - -function ObservedInfo(func, context, compute){ - this.newObserved = {}; - this.oldObserved = null; - this.func = func; - this.context = context; - this.compute = compute; - this.onDependencyChange = can.proxy(this.onDependencyChange, this); - this.depth = null; - this.childDepths = {}; - this.ignore = 0; - this.inBatch = false; - this.ready = false; - compute.observedInfo = this; - this.setReady = can.proxy(this._setReady, this); -} - -// ### observedInfoStack -// -// This is the stack of all `observedInfo` objects that are the result of -// recursive `getValueAndBind` calls. -// `getValueAndBind` can indirectly call itself anytime a compute reads another -// compute. -// -// An `observedInfo` entry looks like: -// -// { -// observed: { -// "map1|first": {obj: map, event: "first"}, -// "map1|last" : {obj: map, event: "last"} -// }, -// names: "map1|firstmap1|last" -// } -// -// Where: -// - `observed` is a map of `"cid|event"` to the observable and event. -// We use keys like `"cid|event"` to quickly identify if we have already observed this observable. -// - `names` is all the keys so we can quickly tell if two observedInfo objects are the same. -var observedInfoStack = []; - -can.simpleExtend(ObservedInfo.prototype,{ - getPrimaryDepth: function() { - return this.compute._primaryDepth; - }, - _setReady: function(){ - this.ready = true; - }, - getDepth: function(){ - if(this.depth !== null) { - return this.depth; - } else { - return (this.depth = this._getDepth()); - } - }, - _getDepth: function(){ - var max = 0, - childDepths = this.childDepths; - for(var cid in childDepths) { - if(childDepths[cid] > max) { - max = childDepths[cid]; - } - } - return max + 1; - }, - addEdge: function(objEv){ - objEv.obj.bind(objEv.event, this.onDependencyChange); - if(objEv.obj.observedInfo) { - this.childDepths[objEv.obj._cid] = objEv.obj.observedInfo.getDepth(); - this.depth = null; - } - }, - removeEdge: function(objEv){ - objEv.obj.unbind(objEv.event, this.onDependencyChange); - if(objEv.obj.observedInfo) { - delete this.childDepths[objEv.obj._cid]; - this.depth = null; - } - }, - dependencyChange: function(ev){ - if(this.bound && this.ready) { - if(ev.batchNum !== undefined) { - // Only need to register once per batchNum - if(ev.batchNum !== this.batchNum) { - ObservedInfo.registerUpdate(this); - this.batchNum = ev.batchNum; - } - } else { - this.updateCompute(ev.batchNum); - } - } - }, - onDependencyChange: function(ev, newVal, oldVal){ - this.dependencyChange(ev, newVal, oldVal); - }, - updateCompute: function(batchNum){ - // It's possible this became unbound since it was registered to update - // Only actually update if something didn't come in and unbind it. (#2188). - if(this.bound) { - // Keep the old value. - var oldValue = this.value; - // Get the new value and register this event handler to any new observables. - this.getValueAndBind(); - // Update the compute with the new value. - this.compute.updater(this.value, oldValue, batchNum); - } - }, - // ## getValueAndBind - // Calls `func` with "this" as `context` and binds to any observables that - // `func` reads. When any of those observables change, `onchanged` is called. - // `oldObservedInfo` is A map of observable / event pairs this function used to be listening to. - // Returns the `newInfo` set of listeners and the value `func` returned. - getValueAndBind: function() { - this.bound = true; - this.oldObserved = this.newObserved || {}; - this.ignore = 0; - this.newObserved = {}; - this.ready = false; - - // Add this function call's observedInfo to the stack, - // runs the function, pops off the observedInfo, and returns it. - - observedInfoStack.push(this); - this.value = this.func.call(this.context); - observedInfoStack.pop(); - this.updateBindings(); - can.batch.afterPreviousEvents(this.setReady); - }, - // ### updateBindings - // Unbinds everything in `oldObserved`. - updateBindings: function(){ - var newObserved = this.newObserved, - oldObserved = this.oldObserved, - name, - obEv; - - for (name in newObserved) { - obEv = newObserved[name]; - if(!oldObserved[name]) { - this.addEdge(obEv); - } else { - oldObserved[name] = null; - } - } - for (name in oldObserved) { - obEv = oldObserved[name]; - if(obEv) { - this.removeEdge(obEv); - } - } - }, - teardown: function(){ - // track this because events can be in the queue. - this.bound = false; - for (var name in this.newObserved) { - var ob = this.newObserved[name]; - this.removeEdge(ob); - } - this.newObserved = {}; - } -}); - - - -var updateOrder = [], - curPrimaryDepth = Infinity, - maxPrimaryDepth = 0; - -// could get a registerUpdate from a 5 while a 1 is going on because the 5 listens to the 1 -ObservedInfo.registerUpdate = function(observeInfo, batchNum){ - var depth = observeInfo.getDepth()-1; - var primaryDepth = observeInfo.getPrimaryDepth(); - - curPrimaryDepth = Math.min(primaryDepth, curPrimaryDepth); - maxPrimaryDepth = Math.max(primaryDepth, maxPrimaryDepth); - - var primary = updateOrder[primaryDepth] || - (updateOrder[primaryDepth] = { - observeInfos: [], - current: Infinity, - max: 0 - }); - var objs = primary.observeInfos[depth] || (primary.observeInfos[depth] = []); - - objs.push(observeInfo); - - primary.current = Math.min(depth, primary.current); - primary.max = Math.max(depth, primary.max); -}; -ObservedInfo.batchEnd = function(batchNum){ - var cur; - - while(true) { - if(curPrimaryDepth <= maxPrimaryDepth) { - var primary = updateOrder[curPrimaryDepth]; - - if(primary && primary.current <= primary.max) { - var last = primary.observeInfos[primary.current]; - if(last && (cur = last.pop())) { - cur.updateCompute(batchNum); - } else { - primary.current++; - } - } else { - curPrimaryDepth++; - } - } else { - updateOrder = []; - curPrimaryDepth = Infinity; - maxPrimaryDepth = 0; - return; - } - } -}; - - -// ## can.__observe -// Indicates that an observable is being read. -// Updates the top of the stack with the observable being read. -can.__observe = function (obj, event) { - var top = observedInfoStack[observedInfoStack.length-1]; - if (top && !top.ignore) { - var evStr = event + "", - name = obj._cid + '|' + evStr; - - if(top.traps) { - top.traps.push({obj: obj, event: evStr, name: name}); - } - else if(!top.newObserved[name]) { - top.newObserved[name] = { - obj: obj, - event: evStr - }; - } - } -}; - -can.__reading = can.__observe; - -can.__trapObserves = function(){ - if (observedInfoStack.length) { - var top = observedInfoStack[observedInfoStack.length-1]; - var traps = top.traps = []; - return function(){ - top.traps = null; - return traps; - }; - } else { - return function(){return [];}; - } -}; -can.__observes = function(observes){ - // a bit more optimized so we don't have to repeat everything in can.__observe - var top = observedInfoStack[observedInfoStack.length-1]; - if (top) { - for(var i =0, len = observes.length; i < len; i++) { - var trap = observes[i], - name = trap.name; - - if(!top.newObserved[name]) { - top.newObserved[name] = trap; - } - } - } -}; - -// ### can.__isRecordingObserves -// Returns if some function is in the process of recording observes. -can.__isRecordingObserves = function(){ - var len = observedInfoStack.length; - return len && (observedInfoStack[len-1].ignore === 0); -}; - -// ### can.__notObserve -// Protects a function from being observed. -can.__notObserve = function(fn){ - return function(){ - if (observedInfoStack.length) { - var top = observedInfoStack[observedInfoStack.length-1]; - top.ignore++; - var res = fn.apply(this, arguments); - top.ignore--; - return res; - } else { - return fn.apply(this, arguments); - } - }; -}; - -can.batch._onDispatchedEvents = ObservedInfo.batchEnd; - -module.exports = exports = ObservedInfo; diff --git a/compute/proto_benchmark.html b/compute/proto_benchmark.html deleted file mode 100644 index 746b19bb50d..00000000000 --- a/compute/proto_benchmark.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - diff --git a/compute/proto_compute.js b/compute/proto_compute.js deleted file mode 100644 index 9d2a7b293e9..00000000000 --- a/compute/proto_compute.js +++ /dev/null @@ -1,479 +0,0 @@ -// # can/compute/proto_compute (aka can.Compute) -// -// Allows the creation of observablue values. This -// is a prototype based version of [can.compute](compute.html). -// -// can.Computes come in different flavors: -// -// - [Getter / Setter functional computes](#setup-getter-setter-functional-computes). -// - [Property computes](#setup-property-computes). -// - [Setter computes](#setup-setter-computes). -// - [Async computes](#setup-async-computes). -// - [Settings computes](#setup-settings-computes). -// - [Simple value computes](#setup-simple-value-computes). -// -// -// can.Computes have public `.get`, `.set`, `.on`, and `.off` methods that call -// internal methods that are configured differently depending on what flavor of -// compute is being created. Those methods are: -// -// - `_on(updater)` - Called the first time the compute is bound. This should bind to -// any source observables. When any of the source observables have changed, it should call -// `updater(newVal, oldVal, batchNum)`. -// -// - `_off(updater)` - Called when the compute has no more event handlers. This should unbind to any source observables. -// - `_get` - Called to get the current value of the compute. -// - `_set` - Called to set the value of the compute. -// -// -// -// Other internal flags and values: -// - `value` - the cached value -// - `_setUpdates` - if calling `_set` will have updated the cached value itself so `_get` does not need to be called. -// - `_canObserve` - if this compute can be observed. -// - `hasDependencies` - if this compute has source observable values. -var can = require('can/util/util'); -var read = require('can/compute/read'); -var ObservedInfo = require('can/compute/get_value_and_bind'); -require('can/util/bind/bind'); -require('can/util/batch/batch'); - -// ## can.Compute -// Checks the arguments and calls different setup methods. -can.Compute = function(getterSetter, context, eventName, bindOnce) { - can.cid(this, 'compute'); - - var args = []; - - for(var i = 0, arglen = arguments.length; i < arglen; i++) { - args[i] = arguments[i]; - } - - var contextType = typeof args[1]; - - if (typeof args[0] === 'function') { - // Getter/Setter functional computes. - // `new can.Compute(function(){ ... })` - this._setupGetterSetterFn(args[0], args[1], args[2], args[3]); - } else if (args[1]) { - if (contextType === 'string') { - // Property computes. - // `new can.Compute(object, propertyName[, eventName])` - this._setupProperty(args[0], args[1], args[2]); - } else if(contextType === 'function') { - // Setter computes. - // `new can.Compute(initialValue, function(newValue){ ... })` - this._setupSetter(args[0], args[1], args[2]); - } else { - - if(args[1] && args[1].fn) { - // Async computes. - this._setupAsyncCompute(args[0], args[1]); - } else { - // Settings computes. - //`new can.Compute(initialValue, {on, off, get, set})` - this._setupSettings(args[0], args[1]); - } - - } - } else { - // Simple value computes. - // `new can.Compute(initialValue)` - this._setupSimpleValue(args[0]); - } - - this._args = args; - this._primaryDepth = 0; - - this.isComputed = true; - -}; - -can.simpleExtend(can.Compute.prototype, { - setPrimaryDepth: function(depth) { - this._primaryDepth = depth; - }, - - // ## Setup getter / setter functional computes - // Uses the function as both a getter and setter. - _setupGetterSetterFn: function(getterSetter, context, eventName) { - this._set = context ? can.proxy(getterSetter, context) : getterSetter; - this._get = context ? can.proxy(getterSetter, context) : getterSetter; - this._canObserve = eventName === false ? false : true; - // The helper provides the on and off methods that use `getValueAndBind`. - var handlers = setupComputeHandlers(this, getterSetter, context || this); - - can.simpleExtend(this, handlers); - }, - // ## Setup property computes - // Listen to a property changing on an object. - _setupProperty: function(target, propertyName, eventName) { - var isObserve = can.isMapLike( target ), - self = this, - handler; - - // If a `can.Map`, setup to read and write to that property. - if(isObserve) { - // We should pass the batchNum if there is one. - handler = function(ev, newVal,oldVal) { - self.updater(newVal, oldVal, ev.batchNum); - }; - this.hasDependencies = true; - this._get = function() { - return target.attr(propertyName); - }; - this._set = function(val) { - target.attr(propertyName, val); - }; - } else { - // This is objects that can be bound to with can.bind. - handler = function () { - self.updater(self._get(), self.value); - }; - this._get = function() { - return can.getObject(propertyName, [target]); - }; - this._set = function(value) { - // allow setting properties n levels deep, if separated with dot syntax - var properties = propertyName.split("."), - leafPropertyName = properties.pop(), - targetProperty = can.getObject(properties.join('.'), [target]); - targetProperty[leafPropertyName] = value; - }; - } - this._on = function(update) { - can.bind.call(target, eventName || propertyName, handler); - // Set the cached value - this.value = this._get(); - }; - this._off = function() { - return can.unbind.call( target, eventName || propertyName, handler); - }; - }, - // ## Setup Setter Computes - // Only a setter function is specified. - _setupSetter: function(initialValue, setter, eventName) { - this.value = initialValue; - this._set = setter; - can.simpleExtend(this, eventName); - }, - // ## Setup settings computes - // Use whatever `on`, `off`, `get`, `set` the users provided - // as the internal methods. - _setupSettings: function(initialValue, settings) { - - this.value = initialValue; - - this._set = settings.set || this._set; - this._get = settings.get || this._get; - - // This allows updater to be called without any arguments. - // selfUpdater flag can be set by things that want to call updater themselves. - if(!settings.__selfUpdater) { - var self = this, - oldUpdater = this.updater; - this.updater = function() { - oldUpdater.call(self, self._get(), self.value); - }; - } - - - this._on = settings.on ? settings.on : this._on; - this._off = settings.off ? settings.off : this._off; - }, - // ## Setup async computes - // This is a special, non-documented form of a compute - // rhat can asynchronously update its value. - _setupAsyncCompute: function(initialValue, settings){ - var self = this; - this.value = initialValue; - - // This compute will call update with the new value itself. - this._setUpdates = true; - - // An "async" compute has a `lastSetValue` that represents - // the last value `compute.set` was called with. - // The following creates `lastSetValue` as a can.Compute so when - // `lastSetValue` is changed, the `getter` can see that change - // and automatically update itself. - this.lastSetValue = new can.Compute(initialValue); - - // Wires up setting this compute to set `lastSetValue`. - // If the new value matches the last setValue, do nothing. - this._set = function(newVal){ - if(newVal === self.lastSetValue.get()) { - return this.value; - } - - return self.lastSetValue.set(newVal); - }; - - // Wire up the get to pass the lastNewValue - this._get = function() { - return getter.call(settings.context, self.lastSetValue.get() ); - }; - - // This is the async getter function. Depending on how many arguments the function takes, - // we setup bindings differently. - var getter = settings.fn, - bindings; - - - - if(getter.length === 0) { - // If it takes no arguments, it should behave just like a Getter compute. - bindings = setupComputeHandlers(this, getter, settings.context); - } else if(getter.length === 1) { - // If it has a single argument, pass it the last setValue. - bindings = setupComputeHandlers(this, function() { - return getter.call(settings.context, self.lastSetValue.get() ); - }, settings); - - } else { - // If the function takes 2 arguments, the second argument is a function - // that should update the value of the compute (`setValue`). To make this we need - // the "normal" updater function because we are about to overwrite it. - var oldUpdater = this.updater, - setValue = function(newVal) { - oldUpdater.call(self, newVal, self.value); - }; - - // Because `setupComputeHandlers` calls `updater` internally with its - // readInfo.value as `oldValue` and that might not be up to date, - // we overwrite updater to always use self.value. - this.updater = function(newVal) { - oldUpdater.call(self, newVal, self.value); - }; - - - bindings = setupComputeHandlers(this, function() { - // Call getter, and get new value - var res = getter.call(settings.context, self.lastSetValue.get(), setValue); - // If undefined is returned, don't update the value. - return res !== undefined ? res : this.value; - }, this); - } - - can.simpleExtend(this, bindings); - }, - // ## Setup simple value computes - // Uses the default `_get`, `_set` behaviors. - _setupSimpleValue: function(initialValue) { - this.value = initialValue; - }, - // ## _bindsetup - // When a compute is first bound, call the internal `this._on` method. - // `can.__notObserve` makes sure if `_on` is listening to any observables, - // they will not be observed by any outer compute. - _bindsetup: can.__notObserve(function () { - this.bound = true; - this._on(this.updater); - }), - // ## _bindteardown - // When a compute has no other bindings, call the internal `this._off` method. - _bindteardown: function () { - this._off(this.updater); - this.bound = false; - }, - // ## bind and unbind - // A bind and unbind that calls `_bindsetup` and `_bindteardown`. - bind: can.bindAndSetup, - unbind: can.unbindAndTeardown, - - // ## clone - // Copies this compute, but for a different context. - // This is mostly used for computes on a map's prototype. - clone: function(context) { - if(context && typeof this._args[0] === 'function') { - this._args[1] = context; - } else if(context) { - this._args[2] = context; - } - - return new can.Compute(this._args[0], this._args[1], this._args[2], this._args[3]); - }, - // ## _on and _off - // Default _on and _off do nothing. - _on: can.k, - _off: can.k, - // ## get - // Returns the cached value if `bound`, otherwise, returns - // the _get value. - get: function() { - // If an external compute is tracking observables and - // this compute can be listened to by "function" based computes .... - if(can.__isRecordingObserves() && this._canObserve !== false) { - - // ... tell the tracking compute to listen to change on this computed. - can.__observe(this, 'change'); - // ... if we are not bound, we should bind so that - // we don't have to re-read to get the value of this compute. - if (!this.bound) { - can.Compute.temporarilyBind(this); - } - } - // If computed is bound, use the cached value. - if (this.bound) { - return this.value; - } else { - return this._get(); - } - }, - // ## _get - // Returns the cached value. - _get: function() { - return this.value; - }, - // ## set - // Sets the value of the compute. - // Depending on the type of the compute and what `_set` returns, it might need to call `_get` after - // `_set` to get the final value. - set: function(newVal) { - - var old = this.value; - - // Setter may return the value if setter - // is for a value maintained exclusively by this compute. - var setVal = this._set(newVal, old); - - // If the setter updated this.value, just return that. - if(this._setUpdates) { - return this.value; - } - - // If the computed function has dependencies, - // we should call the getter. - if (this.hasDependencies) { - return this._get(); - } - - // Setting may not fire a change event, in which case - // the value must be read - if (setVal === undefined) { - this.value = this._get(); - } else { - this.value = setVal; - } - // Fire the change - updateOnChange(this, this.value, old); - return this.value; - }, - // ## _set - // Updates the cached value. - _set: function(newVal) { - return this.value = newVal; - }, - // ## updater - // Updates the cached value and fires an event if the value has changed. - updater: function(newVal, oldVal, batchNum) { - this.value = newVal; - updateOnChange(this, newVal, oldVal, batchNum); - }, - // ## toFunction - // Returns a proxy form of this compute. - toFunction: function() { - return can.proxy(this._computeFn, this); - }, - _computeFn: function(newVal) { - if(arguments.length) { - return this.set(newVal); - } - - return this.get(); - } -}); - -// ## Helpers - -// ## updateOnChange -// A helper to trigger an event when a value changes -var updateOnChange = function(compute, newValue, oldValue, batchNum){ - - var valueChanged = newValue !== oldValue && !(newValue !== newValue && oldValue !== oldValue); - - // Only trigger event when value has changed - if (valueChanged) { - can.batch.trigger(compute, {type: "change", batchNum: batchNum}, [ - newValue, - oldValue - ]); - } -}; - -// ### setupComputeHandlers -// A helper that creates an `_on` and `_off` function that -// will bind on source observables and update the value of the compute. -var setupComputeHandlers = function(compute, func, context) { - - // The last observeInfo object returned by getValueAndBind. - var readInfo = new ObservedInfo(func, context, compute); - - return { - // Call `onchanged` when any source observables change. - _on: function() { - readInfo.getValueAndBind(); - compute.value = readInfo.value; - compute.hasDependencies = !can.isEmptyObject(readInfo.newObserved); - }, - // Unbind `onchanged` from all source observables. - _off: function() { - readInfo.teardown(); - }, - getDepth: function() { - return readInfo.getDepth(); - } - }; -}; - - -// ### temporarilyBind -// Binds computes for a moment to cache their value and prevent re-calculating it. -can.Compute.temporarilyBind = function (compute) { - var computeInstance = compute.computeInstance || compute; - computeInstance.bind('change', can.k); - if (!computes) { - computes = []; - setTimeout(unbindComputes, 10); - } - computes.push(computeInstance); -}; - -// A list of temporarily bound computes -var computes, - // Unbinds all temporarily bound computes. - unbindComputes = function () { - for (var i = 0, len = computes.length; i < len; i++) { - computes[i].unbind('change', can.k); - } - computes = null; - }; - -// ### async -// A simple helper that makes an async compute a bit easier. -can.Compute.async = function(initialValue, asyncComputer, context){ - return new can.Compute(initialValue, { - fn: asyncComputer, - context: context - }); -}; - - -// ### truthy -// Wraps a compute with another compute that only changes when -// the wrapped compute's `truthiness` changes. -can.Compute.truthy = function(compute) { - return new can.Compute(function() { - var res = compute.get(); - if(typeof res === 'function') { - res = res.get(); - } - return !!res; - }); -}; - -// ### compatability -// Setting methods that should not be around in 3.0. -can.Compute.read = read; -can.Compute.set = read.write; - -module.exports = exports = can.Compute; diff --git a/compute/read.js b/compute/read.js deleted file mode 100644 index 91735da92b2..00000000000 --- a/compute/read.js +++ /dev/null @@ -1,290 +0,0 @@ -var can = require('can/util/util'); -// there are things that you need to evaluate when you get them back as a property read -// for example a compute or a function you might need to call to get the next value to -// actually check -// - isArgument - should be renamed to something like "onLastPropertyReadReturnFunctionInsteadOfCallingIt". -// This is used to make a compute out of that function if necessary. -// - readCompute - can be set to `false` to prevent reading an ending compute. This is used by component to get a -// compute as a delegate. In 3.0, this should be removed and force people to write "{@prop} change" -// - callMethodsOnObservables - this is an overwrite ... so normal methods won't be called, but observable ones will. -// - executeAnonymousFunctions - call a function if it's found, defaults to true -// - proxyMethods - if the last read is a method, return a function so `this` will be correct. -// - args - arguments to call functions with. -// -// Callbacks -// - earlyExit - called if a value could not be found -// - foundObservable - called when an observable value is found -var read = function (parent, reads, options) { - - options = options || {}; - var state = { - foundObservable: false - }; - - // `cur` is the current value. - var cur = readValue(parent, 0, reads, options, state), - type, - // `prev` is the object we are reading from. - prev, - // `foundObs` did we find an observable. - readLength = reads.length, - i = 0; - - - while( i < readLength ) { - prev = cur; - // try to read the property - for(var r=0, readersLength = read.propertyReaders.length; r < readersLength; r++) { - var reader = read.propertyReaders[r]; - if(reader.test(cur)) { - cur = reader.read(cur, reads[i], i, options, state); - break; // there can be only one reading of a property - } - } - i = i+1; - // read the value if it is a compute or function - cur = readValue(cur, i, reads, options, state, prev); - type = typeof cur; - // early exit if need be - if (i < reads.length && (cur === null || type !== 'function' && type !== 'object')) { - if (options.earlyExit) { - options.earlyExit(prev, i - 1, cur); - } - // return undefined so we know this isn't the right value - return { - value: undefined, - parent: prev - }; - } - - } - // if we don't have a value, exit early. - if (cur === undefined) { - if (options.earlyExit) { - options.earlyExit(prev, i - 1); - } - } - return { - value: cur, - parent: prev - }; -}; - - -var isAt = function(index, reads) { - var prevRead = reads[index-1]; - return prevRead && prevRead.at; -}; - -var readValue = function(value, index, reads, options, state, prev){ - // if the previous read is AT false ... we shouldn't be doing this; - var usedValueReader; - do { - - usedValueReader = false; - for(var i =0, len = read.valueReaders.length; i < len; i++){ - if( read.valueReaders[i].test(value, index, reads, options) ) { - value = read.valueReaders[i].read(value, index, reads, options, state, prev); - //usedValueReader = true; - } - } - } while(usedValueReader); - - return value; -}; - -// value readers check the current value -// and get a new value from it -// ideally they would keep calling until -// none of these passed -read.valueReaders = [{ - name: "compute", - // compute value reader - test: function(value, i, reads, options){ - - return value && value.isComputed && !isAt(i, reads); - }, - read: function(value, i, reads, options, state){ - if(options.readCompute === false && i === reads.length ) { - return value; - } - - if (!state.foundObservable && options.foundObservable) { - options.foundObservable(value, i); - state.foundObservable = true; - } - return value instanceof can.Compute ? value.get() : value(); - } -},{ - name: "function", - // if this is a function before the last read and its not a constructor function - test: function(value, i, reads, options){ - var type = typeof value; - // i = reads.length if this is the last iteration of the read for-loop. - return type === 'function' && !value.isComputed && - !(can.Construct && value.prototype instanceof can.Construct) && - !(can.route && value === can.route); - }, - read: function(value, i, reads, options, state, prev){ - if( isAt(i, reads) ) { - return i === reads.length ? can.proxy(value, prev) : value; - } - else if(options.callMethodsOnObservables && can.isMapLike(prev)) { - return value.apply(prev, options.args || []); - } - else if ( options.isArgument && i === reads.length ) { - return options.proxyMethods !== false ? can.proxy(value, prev) : value; - } - return value.apply(prev, options.args || []); - } -}]; - -// propertyReaders actually read a property value -read.propertyReaders = [ - // read a can.Map or can.route - { - name: "map", - test: can.isMapLike, - read: function(value, prop, index, options, state){ - if (!state.foundObservable && options.foundObservable) { - options.foundObservable(value, index); - state.foundObservable = true; - } - var res = value.attr(prop.key); - if(res !== undefined) { - return res; - } else { - return value[prop.key]; - } - } - }, - // read a promise - { - name: "promise", - test: function(value){ - return can.isPromise(value); - }, - read: function(value, prop, index, options, state){ - if (!state.foundObservable && options.foundObservable) { - options.foundObservable(value, index); - state.foundObservable = true; - } - var observeData = value.__observeData; - if(!value.__observeData) { - observeData = value.__observeData = { - isPending: true, - state: "pending", - isResolved: false, - isRejected: false, - value: undefined, - reason: undefined - }; - can.cid(observeData); - // proto based would be faster - can.simpleExtend(observeData, can.event); - value.then(function(value){ - observeData.isPending = false; - observeData.isResolved = true; - observeData.value = value; - observeData.state = "resolved"; - observeData.dispatch("state",["resolved","pending"]); - }, function(reason){ - observeData.isPending = false; - observeData.isRejected = true; - observeData.reason = reason; - observeData.state = "rejected"; - observeData.dispatch("state",["rejected","pending"]); - }); - } - can.__observe(observeData,"state"); - return prop.key in observeData ? observeData[prop.key] : value[prop.key]; - } - }, - - // read a normal object - { - name: "object", - // this is the default - test: function(){return true;}, - read: function(value, prop){ - if(value == null) { - return undefined; - } else { - if(prop.key in value) { - return value[prop.key]; - } - // TODO: remove in 3.0. This is for backwards compat with @key and @index. - else if( prop.at && specialRead[prop.key] && ( ("@"+prop.key) in value)) { - //!steal-remove-start - can.dev.warn("Use %"+prop.key+" in place of @"+prop.key+"."); - - //!steal-remove-end - - prop.at = false; - return value["@"+prop.key]; - } - - } - } - } -]; - -var specialRead = {index: true, key: true, event: true, element: true, viewModel: true}; - -// This should be able to set a property similar to how read works. -read.write = function(parent, key, value, options) { - options = options || {}; - if(can.isMapLike(parent)) { - // HACK! ... check if the attr is a comptue, if it is, set it. - if(!options.isArgument && parent._data && parent._data[key] && parent._data[key].isComputed) { - return parent._data[key](value); - } else { - return parent.attr(key, value); - } - } - - if(parent[key] && parent[key].isComputed) { - return parent[key](value); - } - - if(typeof parent === 'object') { - parent[key] = value; - } -}; - - -read.reads = function(key) { - var keys = []; - var last = 0; - var at = false; - if( key.charAt(0) === "@" ) { - last = 1; - at = true; - } - var keyToAdd = ""; - for(var i = last; i < key.length; i++) { - var character = key.charAt(i); - if(character === "." || character === "@") { - if( key.charAt(i -1) !== "\\" ) { - keys.push({ - key: keyToAdd, - at: at - }); - at = character === "@"; - keyToAdd = ""; - } else { - keyToAdd = keyToAdd.substr(0,keyToAdd.length - 1) + "."; - } - } else { - keyToAdd += character; - } - } - keys.push({ - key: keyToAdd, - at: at - }); - - return keys; -}; - -module.exports = exports = read; diff --git a/compute/read_test.js b/compute/read_test.js deleted file mode 100644 index 1ae41e6d518..00000000000 --- a/compute/read_test.js +++ /dev/null @@ -1,143 +0,0 @@ -require('can/compute/compute'); -require('can/map/map'); -require('steal-qunit'); - -QUnit.module('can/compute/read'); - -test("can.Construct derived classes should be considered objects, not functions (#450)", function() { - var foostructor = can.Map({ text: "bar" }, {}), - obj = { - next_level: { - thing: foostructor, - text: "In the inner context" - } - }, - read; - foostructor.self = foostructor; - - read = can.compute.read(obj, can.compute.read.reads("next_level.thing.self.text") ); - equal(read.value, "bar", "static properties on a can.Construct-based function"); - - read = can.compute.read(obj, can.compute.read.reads("next_level.thing.self"), { isArgument: true }); - ok(read.value === foostructor, "arguments shouldn't be executed"); - - foostructor.self = function() { return foostructor; }; - read = can.compute.read(obj, can.compute.read.reads("next_level.thing.self.text"), { }); - equal(read.value, "bar", "anonymous functions in the middle of a read should be executed if requested"); -}); - -test("compute.read works with a Map wrapped in a compute", function() { - var parent = can.compute(new can.Map({map: {first: "Justin" }})); - - var result = can.compute.read(parent, can.compute.read.reads("map.first")); - equal(result.value, "Justin", "The correct value is found."); -}); - -test('compute.read works with a Map wrapped in a compute', function() { - var parent = new can.Compute(new can.Map({map: {first: 'Justin' }})); - - var result = can.Compute.read(parent, can.compute.read.reads("map.first")); - equal(result.value, 'Justin', 'The correct value is found.'); -}); - -test("compute.read returns constructor functions instead of executing them (#1332)", function() { - var Todo = can.Map.extend({}); - var parent = can.compute(new can.Map({map: { Test: Todo }})); - - var result = can.compute.read(parent, can.compute.read.reads("map.Test")); - equal(result.value, Todo, 'Got the same Todo'); -}); - -test("compute.set with different values", 4, function() { - var comp = can.compute("David"); - var parent = { - name: "David", - comp: comp - }; - var map = new can.Map({ - name: "David" - }); - - map.bind('change', function(ev, attr, how, value) { - equal(value, "Brian", "Got change event on map"); - }); - - can.compute.set(parent, "name", "Matthew"); - equal(parent.name, "Matthew", "Name set"); - - can.compute.set(parent, "comp", "Justin"); - equal(comp(), "Justin", "Name updated"); - - can.compute.set(map, "name", "Brian"); - equal(map.attr("name"), "Brian", "Name updated in map"); -}); - -test("can.Compute.read can read a promise (#179)", function(){ - - var def = new can.Deferred(); - var map = new can.Map(); - - var c = can.compute(function(){ - return can.Compute.read({map: map},can.compute.read.reads("map.data.value")).value; - }); - - var calls = 0; - c.bind("change", function(ev, newVal, oldVal){ - calls++; - equal(calls, 1, "only one call"); - equal(newVal, "Something", "new value"); - equal(oldVal, undefined, "oldVal"); - start(); - }); - - map.attr("data", def); - - setTimeout(function(){ - def.resolve("Something"); - },2); - - stop(); - -}); - -test('can.compute.reads', function(){ - deepEqual( can.compute.read.reads("@foo"), - [{key: "foo", at: true}]); - - deepEqual( can.compute.read.reads("@foo.bar"), - [{key: "foo", at: true}, {key: "bar", at: false}]); - - deepEqual( can.compute.read.reads("@foo\\.bar"), - [{key: "foo.bar", at: true}]); - - deepEqual( can.compute.read.reads("foo.bar@zed"), - [{key: "foo", at: false},{key: "bar", at: false},{key: "zed", at: true}]); - -}); - -test("prototype computes work (#2098)", function(){ - var Map = can.Map.extend({ - plusOne: can.compute(function(){ - return this.attr("value")+1; - }) - }); - var root = new Map({value: 2}), - read; - read = can.compute.read(root, can.compute.read.reads("plusOne") ); - equal(read.value, 3, "static properties on a can.Construct-based function"); -}); - -test("expandos on can.Map can be read (#2199)", function(){ - var map = new can.Map({}); - var expandoMethod = function(){ - return this.expandoProp+"!"; - }; - map.expandoMethod = expandoMethod; - map.expandoProp = "val"; - - var read = can.compute.read(map, can.compute.read.reads("@expandoMethod") ); - equal(read.value(),"val!", "got expando method"); - - read = can.compute.read(map, can.compute.read.reads("expandoProp") ); - equal(read.value,"val", "got expando prop"); -}); diff --git a/compute/test.html b/compute/test.html deleted file mode 100644 index 8b4a8a985fd..00000000000 --- a/compute/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/compute - -
        diff --git a/construct/construct.html b/construct/construct.html deleted file mode 100644 index 0748767ec2c..00000000000 --- a/construct/construct.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - can.Construct Demo - - -

        Some can.Construct demos

        -

        Open the console to see the status messages.

        - - - diff --git a/construct/construct.js b/construct/construct.js index 97e17321acc..54b53a99f18 100644 --- a/construct/construct.js +++ b/construct/construct.js @@ -1,763 +1,3 @@ -// steal-clean -var can = require('can/util/string/string'); -// ## construct.js -// `can.Construct` -// _This is a modified version of -// [John Resig's class](http://ejohn.org/blog/simple-javascript-inheritance/). -// It provides class level inheritance and callbacks._ -// A private flag used to initialize a new class instance without -// initializing it's bindings. -var initializing = 0; +var can = require('../util/can'); -var canGetDescriptor; -try { - Object.getOwnPropertyDescriptor({}); - canGetDescriptor = true; -} catch(e) { - canGetDescriptor = false; -} - -var getDescriptor = function(newProps, name) { - var descriptor = Object.getOwnPropertyDescriptor(newProps, name); - if(descriptor && (descriptor.get || descriptor.set)) { - return descriptor; - } - return null; - }, - inheritGetterSetter = function(newProps, oldProps, addTo) { - addTo = addTo || newProps; - var descriptor; - - for (var name in newProps) { - if( (descriptor = getDescriptor(newProps, name)) ) { - this._defineProperty(addTo, oldProps, name, descriptor); - } else { - can.Construct._overwrite(addTo, oldProps, name, newProps[name]); - } - } - }, - simpleInherit = function (newProps, oldProps, addTo) { - addTo = addTo || newProps; - - for (var name in newProps) { - can.Construct._overwrite(addTo, oldProps, name, newProps[name]); - } - }; - -/** - * @add can.Construct - */ -can.Construct = function () { - if (arguments.length) { - return can.Construct.extend.apply(can.Construct, arguments); - } -}; -/** - * @static - */ -can.extend(can.Construct, { - /** - * @property {Boolean} can.Construct.constructorExtends - * @parent can.Construct.static - * - * @description - * Toggles the behavior of a constructor function called - * without the `new` keyword to extend the constructor function or - * create a new instance. - * - * ``` - * var animal = Animal(); - * // vs - * var animal = new Animal(); - * ``` - * - * @body - * - * If `constructorExtends` is: - * - * - `true` - the constructor extends - * - `false` - a new instance of the constructor is created - * - * This property defaults to false. - * - * Example of constructExtends is true: - * ``` - * var Animal = can.Construct.extend({ - * constructorExtends: true // the constructor extends - * },{ - * sayHi: function() { - * console.log("hai!"); - * } - * }); - * - * var Pony = Animal({ - * gallop: function () { - * console.log("Galloping!!"); - * } - * }); // Pony is now a constructor function extended from Animal - * - * var frank = new Animal(); // frank is a new instance of Animal - * - * var gertrude = new Pony(); // gertrude is a new instance of Pony - * gertrude.sayHi(); // "hai!" - sayHi is "inherited" from Animal - * gertrude.gallop(); // "Galloping!!" - gallop is unique to instances of Pony - *``` - * - * The default behavior is shown in the example below: - * ``` - * var Animal = can.Construct.extend({ - * constructorExtends: false // the constructor does NOT extend - * },{ - * sayHi: function() { - * console.log("hai!"); - * } - * }); - * - * var pony = Animal(); // pony is a new instance of Animal - * var frank = new Animal(); // frank is a new instance of Animal - * - * pony.sayHi() // "hai!" - * frank.sayHi() // "hai!" - *``` - * By default to extend a constructor, you must use [can.Construct.extend extend]. - */ - constructorExtends: true, - /** - * @function can.Construct.newInstance newInstance - * @parent can.Construct.static - * - * @description Returns an instance of `can.Construct`. This method - * can be overridden to return a cached instance. - * - * @signature `can.Construct.newInstance([...args])` - * - * @param {*} [args] arguments that get passed to [can.Construct::setup] and [can.Construct::init]. Note - * that if [can.Construct::setup] returns an array, those arguments will be passed to [can.Construct::init] - * instead. - * @return {class} instance of the class - * - * @body - * Creates a new instance of the constructor function. This method is useful for creating new instances - * with arbitrary parameters. Typically, however, you will simply want to call the constructor with the - * __new__ operator. - * - * ## Example - * - * The following creates a `Person` Construct and overrides `newInstance` to cache all - * instances of Person to prevent duplication. If the properties of a new Person match an existing one it - * will return a reference to the previously created object, otherwise it returns a new object entirely. - * - * ``` - * // define and create the Person constructor - * var Person = can.Construct.extend({ - * init : function(first, middle, last) { - * this.first = first; - * this.middle = middle; - * this.last = last; - * } - * }); - * - * // store a reference to the original newInstance function - * var _newInstance = Person.newInstance; - * - * // override Person's newInstance function - * Person.newInstance = function() { - * // if cache does not exist make it an new object - * this.__cache = this.__cache || {}; - * // id is a stingified version of the passed arguments - * var id = JSON.stringify(arguments); - * - * // look in the cache to see if the object already exists - * var cachedInst = this.__cache[id]; - * if(cachedInst) { - * return cachedInst; - * } - * - * //otherwise call the original newInstance function and return a new instance of Person. - * var newInst = _newInstance.apply(this, arguments); - * this.__cache[id] = newInst; - * return newInst; - * } - * - * // create two instances with the same arguments - * var justin = new Person('Justin', 'Barry', 'Meyer'), - * brian = new Person('Justin', 'Barry', 'Meyer'); - * - * console.log(justin === brian); // true - both are references to the same instance - * ``` - * - */ - newInstance: function () { - // Get a raw instance object (`init` is not called). - var inst = this.instance(), - args; - // Call `setup` if there is a `setup` - if (inst.setup) { - inst.__inSetup = true; - args = inst.setup.apply(inst, arguments); - delete inst.__inSetup; - } - // Call `init` if there is an `init` - // If `setup` returned `args`, use those as the arguments - if (inst.init) { - inst.init.apply(inst, args || arguments); - } - return inst; - }, - // Overwrites an object with methods. Used in the `super` plugin. - // `newProps` - New properties to add. - // `oldProps` - Where the old properties might be (used with `super`). - // `addTo` - What we are adding to. - _inherit: canGetDescriptor ? inheritGetterSetter : simpleInherit, - - // Adds a `defineProperty` with the given name and descriptor - // Will only ever be called if ES5 is supported - _defineProperty: function(what, oldProps, propName, descriptor) { - Object.defineProperty(what, propName, descriptor); - }, - - // used for overwriting a single property. - // this should be used for patching other objects - // the super plugin overwrites this - _overwrite: function (what, oldProps, propName, val) { - what[propName] = val; - }, - // Set `defaults` as the merger of the parent `defaults` and this - // object's `defaults`. If you overwrite this method, make sure to - // include option merging logic. - /** - * @function can.Construct.setup setup - * @parent can.Construct.static - * - * @description Perform initialization logic for a constructor function. - * - * @signature `can.Construct.setup(base, fullName, staticProps, protoProps)` - * - * A static `setup` method provides inheritable setup functionality - * for a Constructor function. The following example - * creates a Group constructor function. Any constructor - * functions that inherit from Group will be added to - * `Group.childGroups`. - * - * - * Group = can.Construct.extend({ - * setup: function(Construct, fullName, staticProps, protoProps){ - * this.childGroups = []; - * if(Construct !== can.Construct){ - * this.childGroups.push(Construct) - * } - * Construct.setup.apply(this, arguments) - * } - * },{}) - * var Flock = Group.extend(...) - * Group.childGroups[0] //-> Flock - * - * @param {constructor} base The base constructor that is being inherited from. - * @param {String} fullName The name of the new constructor. - * @param {Object} staticProps The static properties of the new constructor. - * @param {Object} protoProps The prototype properties of the new constructor. - * - * @body - * The static `setup` method is called immediately after a constructor - * function is created and - * set to inherit from its base constructor. It is useful for setting up - * additional inheritance work. - * Do not confuse this with the prototype `[can.Construct::setup]` method. - * - * ## Example - * - * This `Parent` class adds a reference to its base class to itself, and - * so do all the classes that inherit from it. - * - * ``` - * Parent = can.Construct.extend({ - * setup : function(base, fullName, staticProps, protoProps){ - * this.base = base; - * - * // call base functionality - * can.Construct.setup.apply(this, arguments) - * } - * },{}); - * - * Parent.base; // can.Construct - * - * Child = Parent({}); - * - * Child.base; // Parent - * ``` - */ - setup: function (base, fullName) { - this.defaults = can.extend(true, {}, base.defaults, this.defaults); - }, - // Create's a new `class` instance without initializing by setting the - // `initializing` flag. - instance: function () { - // Prevents running `init`. - initializing = 1; - var inst = new this(); - // Allow running `init`. - initializing = 0; - return inst; - }, - // Extends classes. - /** - * @function can.Construct.extend extend - * @parent can.Construct.static - * - * @signature `can.Construct.extend([name,] [staticProperties,] instanceProperties)` - * - * Extends `can.Construct`, or constructor functions derived from `can.Construct`, - * to create a new constructor function. Example: - * - * var Animal = can.Construct.extend({ - * sayHi: function(){ - * console.log("hi") - * } - * }) - * var animal = new Animal() - * animal.sayHi(); - * - * @param {String} [name] Creates the necessary properties and - * objects that point from the `window` to the created constructor function. The following: - * - * can.Construct.extend("company.project.Constructor",{}) - * - * creates a `company` object on window if it does not find one, a - * `project` object on `company` if it does not find one, and it will set the - * `Constructor` property on the `project` object to point to the constructor function. - * - * Finally, it sets "company.project.Constructor" as [can.Construct.fullName fullName] - * and "Constructor" as [can.Construct.shortName shortName]. - * - * @param {Object} [staticProperties] Properties that are added the constructor - * function directly. For example: - * - * ``` - * var Animal = can.Construct.extend({ - * findAll: function(){ - * return can.ajax({url: "/animals"}) - * } - * },{}); // need to pass an empty instanceProperties object - * - * Animal.findAll().then(function(json){ ... }) - * ``` - * - * The [can.Construct.setup static setup] method can be used to - * specify inheritable behavior when a Constructor function is created. - * - * @param {Object} instanceProperties Properties that belong to - * instances made with the constructor. These properties are added to the - * constructor's `prototype` object. Example: - * - * var Animal = can.Construct.extend({ - * findAll: function() { - * return can.ajax({url: "/animals"}); - * } - * },{ - * init: function(name) { - * this.name = name; - * }, - * sayHi: function() { - * console.log(this.name," says hai!"); - * } - * }) - * var pony = new Animal("Gertrude"); - * pony.sayHi(); // "Gertrude says hai!" - * - * The [can.Construct::init init] and [can.Construct::setup setup] properties - * are used for initialization. - * - * @return {function} The constructor function. - * ``` - * var Animal = can.Construct.extend(...); - * var pony = new Animal(); // Animal is a constructor function - * ``` - * @body - * ## Inheritance - * Creating "subclasses" with `can.Construct` is simple. All you need to do is call the base constructor - * with the new function's static and instance properties. For example, we want our `Snake` to - * be an `Animal`, but there are some differences: - * - * - * var Snake = Animal.extend({ - * legs: 0 - * }, { - * init: function() { - * Animal.prototype.init.call(this, 'ssssss'); - * }, - * slither: function() { - * console.log('slithering...'); - * } - * }); - * - * var baslisk = new Snake(); - * baslisk.speak(); // "ssssss" - * baslisk.slither(); // "slithering..." - * baslisk instanceof Snake; // true - * baslisk instanceof Animal; // true - * - * - * ## Static properties and inheritance - * - * If you pass all three arguments to can.Construct, the second one will be attached directy to the - * constructor, allowing you to imitate static properties and functions. You can access these - * properties through the `[can.Construct::constructor this.constructor]` property. - * - * Static properties can get overridden through inheritance just like instance properties. In the example below, - * we override both the legs static property as well as the the init function for each instance: - * - * ``` - * var Animal = can.Construct.extend({ - * legs: 4 - * }, { - * init: function(sound) { - * this.sound = sound; - * }, - * speak: function() { - * console.log(this.sound); - * } - * }); - * - * var Snake = Animal.extend({ - * legs: 0 - * }, { - * init: function() { - * this.sound = 'ssssss'; - * }, - * slither: function() { - * console.log('slithering...'); - * } - * }); - * - * Animal.legs; // 4 - * Snake.legs; // 0 - * var dog = new Animal('woof'); - * var blackMamba = new Snake(); - * dog.speak(); // 'woof' - * blackMamba.speak(); // 'ssssss' - * ``` - */ - extend: function (name, staticProperties, instanceProperties) { - var fullName = name, - klass = staticProperties, - proto = instanceProperties; - - // Figure out what was passed and normalize it. - if (typeof fullName !== 'string') { - proto = klass; - klass = fullName; - fullName = null; - } - if (!proto) { - proto = klass; - klass = null; - } - proto = proto || {}; - var _super_class = this, - _super = this.prototype, - Constructor, parts, current, _fullName, _shortName, propName, shortName, namespace, prototype; - // Instantiate a base class (but only create the instance, - // don't run the init constructor). - prototype = this.instance(); - // Copy the properties over onto the new prototype. - can.Construct._inherit(proto, _super, prototype); - - if(fullName) { - parts = fullName.split('.'); - shortName = parts.pop(); - } else if(klass && klass.shortName) { - shortName = klass.shortName; - } else if(this.shortName) { - shortName = this.shortName; - } - //!steal-remove-start - /* jshint ignore:start */ - // In dev builds we want constructor.name to be the same as shortName. - // The only way to do that right now is using eval. jshint does not like - // this at all so we hide it - - // Strip semicolons - var constructorName = shortName ? shortName.replace(/;/g, '') : 'Constructor'; - - // Assign a name to the constructor - eval('Constructor = function ' + constructorName + '() { return init.apply(this, arguments); }'); - /* jshint ignore:end */ - //!steal-remove-end - - // Make sure Constructor is still defined when the constructor name - // code is removed. - if(typeof constructorName === 'undefined') { - Constructor = function() { - return init.apply(this, arguments); - }; - } - // The dummy class constructor. - function init() { - // All construction is actually done in the init method. - if (!initializing) { - //!steal-remove-start - if(this.constructor !== Constructor && - // We are being called without `new` or we are extending. - arguments.length && Constructor.constructorExtends) { - can.dev.warn('can/construct/construct.js: extending a can.Construct without calling extend'); - } - //!steal-remove-end - - return this.constructor !== Constructor && - // We are being called without `new` or we are extending. - arguments.length && Constructor.constructorExtends ? Constructor.extend.apply(Constructor, arguments) : - // We are being called with `new`. - Constructor.newInstance.apply(Constructor, arguments); - } - } - // Copy old stuff onto class (can probably be merged w/ inherit) - for (propName in _super_class) { - if (_super_class.hasOwnProperty(propName)) { - Constructor[propName] = _super_class[propName]; - } - } - // Copy new static properties on class. - can.Construct._inherit(klass, _super_class, Constructor); - // Setup namespaces. - if (fullName) { - - current = can.getObject(parts.join('.'), window, true); - namespace = current; - _fullName = can.underscore(fullName.replace(/\./g, "_")); - _shortName = can.underscore(shortName); - - //!steal-remove-start - if (current[shortName]) { - can.dev.warn("can/construct/construct.js: There's already something called " + fullName); - } - //!steal-remove-end - - current[shortName] = Constructor; - } - // Set things that shouldn't be overwritten. - can.extend(Constructor, { - constructor: Constructor, - prototype: prototype, - /** - * @property {String} can.Construct.namespace namespace - * @parent can.Construct.static - * - * The `namespace` property returns the namespace your constructor is in. - * This provides a way organize code and ensure globally unique types. The - * `namespace` is the [can.Construct.fullName fullName] you passed without the [can.Construct.shortName shortName]. - * - * ``` - * can.Construct("MyApplication.MyConstructor",{},{}); - * MyApplication.MyConstructor.namespace // "MyApplication" - * MyApplication.MyConstructor.shortName // "MyConstructor" - * MyApplication.MyConstructor.fullName // "MyApplication.MyConstructor" - * ``` - */ - namespace: namespace, - /** - * @property {String} can.Construct.shortName shortName - * @parent can.Construct.static - * - * If you pass a name when creating a Construct, the `shortName` property will be set to the - * name you passed without the [can.Construct.namespace namespace]. - * - * ``` - * can.Construct("MyApplication.MyConstructor",{},{}); - * MyApplication.MyConstructor.namespace // "MyApplication" - * MyApplication.MyConstructor.shortName // "MyConstructor" - * MyApplication.MyConstructor.fullName // "MyApplication.MyConstructor" - * ``` - */ - _shortName: _shortName, - /** - * @property {String} can.Construct.fullName fullName - * @parent can.Construct.static - * - * If you pass a name when creating a Construct, the `fullName` property will be set to - * the name you passed. The `fullName` consists of the [can.Construct.namespace namespace] and - * the [can.Construct.shortName shortName]. - * - * ``` - * can.Construct("MyApplication.MyConstructor",{},{}); - * MyApplication.MyConstructor.namespace // "MyApplication" - * MyApplication.MyConstructor.shortName // "MyConstructor" - * MyApplication.MyConstructor.fullName // "MyApplication.MyConstructor" - * ``` - */ - fullName: fullName, - _fullName: _fullName - }); - - if (shortName !== undefined) { - Constructor.shortName = shortName; - } - // Make sure our prototype looks nice. - Constructor.prototype.constructor = Constructor; - // Call the class `setup` and `init` - var t = [_super_class].concat(can.makeArray(arguments)), - args = Constructor.setup.apply(Constructor, t); - if (Constructor.init) { - Constructor.init.apply(Constructor, args || t); - } - /** - * @prototype - */ - return Constructor; // - /** - * @property {Object} can.Construct.prototype.constructor constructor - * @parent can.Construct.prototype - * - * A reference to the constructor function that created the instance. This allows you to access - * the constructor's static properties from an instance. - * - * @body - * ## Example - * - * This can.Construct has a static counter that counts how many instances have been created: - * - * ``` - * var Counter = can.Construct.extend({ - * count: 0 - * }, { - * init: function() { - * this.constructor.count++; - * } - * }); - * - * var childCounter = new Counter(); - * console.log(childCounter.constructor.count); // 1 - * console.log(Counter.count); // 1 - * ``` - */ - } -}); -/** - * @function can.Construct.prototype.setup setup - * @parent can.Construct.prototype - * - * @signature `construct.setup(...args)` - * - * A setup function for the instantiation of a constructor function. - * - * @param {*} args The arguments passed to the constructor. - * - * @return {Array|undefined} If an array is returned, the array's items are passed as - * arguments to [can.Construct::init init]. The following example always makes - * sure that init is called with a jQuery wrapped element: - * - * WidgetFactory = can.Construct.extend({ - * setup: function(element){ - * return [$(element)] - * } - * }) - * - * MyWidget = WidgetFactory.extend({ - * init: function($el){ - * $el.html("My Widget!!") - * } - * }) - * - * Otherwise, the arguments to the - * constructor are passed to [can.Construct::init] and the return value of `setup` is discarded. - * - * @body - * - * ## Deciding between `setup` and `init` - * - * - * Usually, you should use [can.Construct::init init] to do your constructor function's initialization. - * You should, instead, use `setup` when: - * - * - there is initialization code that you want to run before the inheriting constructor's - * `init` method is called. - * - there is initialization code that should run whether or not inheriting constructors - * call their base's `init` methods. - * - you want to modify the arguments that will get passed to `init`. - * - * ## Example - * - * This code is a simplified version of the code in [can.Control]'s setup - * method. It converts the first argument to a jQuery collection and - * extends the controller's defaults with the options that were passed. - * - * - * can.Control = can.Construct.extend({ - * setup: function(domElement, rawOptions) { - * // set up this.element - * this.element = $(domElement); - * - * // set up this.options - * this.options = can.extend({}, - * this.constructor.defaults, - * rawOptions - * ); - * - * // pass this.element and this.options to init. - * return [this.element, this.options]; - * } - * }); - * - */ -can.Construct.prototype.setup = function () {}; -/** - * @function can.Construct.prototype.init init - * @parent can.Construct.prototype - * - * @description Called when a new instance of a can.Construct is created. - * - * @signature `construct.init(...args)` - * @param {*} args the arguments passed to the constructor (or the items of the array returned from [can.Construct::setup]) - * - * @body - * If a prototype `init` method is provided, `init` is called when a new Construct is created--- - * after [can.Construct::setup]. The `init` method is where the bulk of your initialization code - * should go. A common thing to do in `init` is save the arguments passed into the constructor. - * - * ## Examples - * - * First, we'll make a Person constructor that has a first and last name: - * - * ``` - * var Person = can.Construct.extend({ - * init: function(first, last) { - * this.first = first; - * this.last = last; - * } - * }); - * - * var justin = new Person("Justin", "Meyer"); - * justin.first; // "Justin" - * justin.last; // "Meyer" - * ``` - * - * Then, we'll extend Person into Programmer, and add a favorite language: - * - * ``` - * var Programmer = Person.extend({ - * init: function(first, last, language) { - * // call base's init - * Person.prototype.init.apply(this, arguments); - * - * // other initialization code - * this.language = language; - * }, - * bio: function() { - * return "Hi! I'm " + this.first + " " + this.last + - * " and I write " + this.language + "."; - * } - * }); - * - * var brian = new Programmer("Brian", "Moschel", 'ECMAScript'); - * brian.bio(); // "Hi! I'm Brian Moschel and I write ECMAScript."; - * ``` - * - * ## Modified Arguments - * - * [can.Construct::setup] is able to modify the arguments passed to `init`. - * If you aren't receiving the arguments you passed to `new Construct(args)`, - * check that they aren't being changed by `setup` along - * the inheritance chain. - */ -can.Construct.prototype.init = function () {}; - -module.exports = exports = can.Construct; +module.exports = can.Construct = require('can-construct'); diff --git a/construct/construct_test.js b/construct/construct_test.js index f67719ce4c2..994fde270b6 100644 --- a/construct/construct_test.js +++ b/construct/construct_test.js @@ -1,164 +1 @@ -/* global Foo, Car, Bar */ -require('steal-qunit'); -require('can/construct/construct'); - -QUnit.module('can/construct', { - setup: function () { - var Animal = this.Animal = can.Construct({ - count: 0, - test: function () { - return this.match ? true : false; - } - }, { - init: function () { - this.constructor.count++; - this.eyes = false; - } - }); - var Dog = this.Dog = this.Animal({ - match: /abc/ - }, { - init: function () { - Animal.prototype.init.apply(this, arguments); - }, - talk: function () { - return 'Woof'; - } - }); - this.Ajax = this.Dog({ - count: 0 - }, { - init: function (hairs) { - Dog.prototype.init.apply(this, arguments); - this.hairs = hairs; - this.setEyes(); - }, - setEyes: function () { - this.eyes = true; - } - }); - } -}); -test('inherit', function () { - var Base = can.Construct({}); - ok(new Base() instanceof can.Construct); - var Inherit = Base({}); - ok(new Inherit() instanceof Base); -}); -test('Creating', function () { - new this.Dog(); - var a1 = new this.Animal(); - new this.Animal(); - var ajax = new this.Ajax(1000); - equal(2, this.Animal.count, 'right number of animals'); - equal(1, this.Dog.count, 'right number of animals'); - ok(this.Dog.match, 'right number of animals'); - ok(!this.Animal.match, 'right number of animals'); - ok(this.Dog.test(), 'right number of animals'); - ok(!this.Animal.test(), 'right number of animals'); - equal(1, this.Ajax.count, 'right number of animals'); - equal(2, this.Animal.count, 'right number of animals'); - equal(true, ajax.eyes, 'right number of animals'); - equal(1000, ajax.hairs, 'right number of animals'); - ok(a1 instanceof this.Animal); - ok(a1 instanceof can.Construct); -}); -test('new instance', function () { - var d = this.Ajax.newInstance(6); - equal(6, d.hairs); -}); -test('namespaces', function () { - var fb = can.Construct.extend('Foo.Bar'); - can.Construct('Todo', {}, {}); - ok(Foo.Bar === fb, 'returns class'); - equal(fb.shortName, 'Bar', 'short name is right'); - //!steal-remove-start - if (can.dev && fb.name) { - equal(fb.name, 'Bar', 'short name is right'); - } - //!steal-remove-end - - equal(fb.fullName, 'Foo.Bar', 'fullName is right'); -}); -test('setups', function () { - var order = 0, - staticSetup, staticSetupArgs, staticInit, staticInitArgs, protoSetup, protoInitArgs, protoInit, staticProps = { - setup: function () { - staticSetup = ++order; - staticSetupArgs = arguments; - return ['something']; - }, - init: function () { - staticInit = ++order; - staticInitArgs = arguments; - } - }, protoProps = { - setup: function (name) { - protoSetup = ++order; - return ['Ford: ' + name]; - }, - init: function () { - protoInit = ++order; - protoInitArgs = arguments; - } - }; - can.Construct.extend('Car', staticProps, protoProps); - new Car('geo'); - equal(staticSetup, 1); - equal(staticInit, 2); - equal(protoSetup, 3); - equal(protoInit, 4); - deepEqual(can.makeArray(staticInitArgs), ['something']); - deepEqual(can.makeArray(protoInitArgs), ['Ford: geo']); - deepEqual(can.makeArray(staticSetupArgs), [ - can.Construct, - 'Car', - staticProps, - protoProps - ], 'static construct'); - //now see if staticSetup gets called again ... - Car.extend('Truck'); - equal(staticSetup, 5, 'Static setup is called if overwriting'); -}); -test('Creating without extend', function () { - can.Construct('Bar', { - ok: function () { - ok(true, 'ok called'); - } - }); - new Bar() - .ok(); - Bar('Foo', { - dude: function () { - ok(true, 'dude called'); - } - }); - new Foo() - .dude(true); -}); - -//!steal-remove-start -if (can.dev) { - test('console warning if extend is not used without new (#932)', function () { - - var oldlog = can.dev.warn; - can.dev.warn = function (text) { - ok(text, "got a message"); - can.dev.warn = oldlog; - }; - var K1 = can.Construct({}); - K1({}); - }); -} -//!steal-remove-end - -test("setup called with original arguments", function(){ - var o1 = { - setup: function(base, arg1, arg2){ - equal(o1, arg1, "first argument is correct"); - equal(o2, arg2, "second argument is correct"); - } - }; - var o2 = {}; - can.Construct.extend(o1,o2); -}); +require('can-construct/can-construct_test'); diff --git a/construct/doc/construct.md b/construct/doc/construct.md deleted file mode 100644 index f6ee10df620..00000000000 --- a/construct/doc/construct.md +++ /dev/null @@ -1,47 +0,0 @@ -@constructor can.Construct -@download can/construct -@test can/construct/test.html -@parent canjs -@group can.Construct.plugins plugins -@link ../docco/construct/construct.html docco - -@description - -Provides a way to easily use the power of prototypal inheritance -without worrying about hooking up all the particulars yourself. Use -[can.Construct.extend can.Construct.extend] to create an inheritable -constructor function of your own. - -@body - -## Use - -In the example below, `Animal` is a constructor function returned by [can.Construct.extend can.Construct.extend]. All instances of `Animal` will have a `speak` -method, and the `Animal` constructor has a `legs` property. - - - var Animal = can.Construct.extend({ - legs: 4 - }, { - init: function(sound) { - this.sound = sound; - }, - speak: function() { - console.log(this.sound); - } - }); - - -You can make instances of your object by calling your constructor function with the `new` keyword. When an object is created, the [can.Construct::init init] -method gets called (if you supplied one): - - var panther = new Animal('growl'); - panther.speak(); // "growl" - panther instanceof Animal; // true - -## Plugins - -There are two plugins available to help make using `can.Construct` even simpler. - -- [can.Construct.super] allows you to easily call base methods by making `this._super` available in inherited methods. -- [can.Construct.proxy] creates a static callback function that sets the value of `this` to an instance of the constructor function. \ No newline at end of file diff --git a/construct/super/super.html b/construct/super/super.html deleted file mode 100644 index a8e5d3b4b3b..00000000000 --- a/construct/super/super.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - can.Construct.proxy demo - - -

        Super Demo

        -

        Open the console to see the status messages.

        -

        This is a dummy page to show off your plugin

        - - - - \ No newline at end of file diff --git a/construct/super/super.js b/construct/super/super.js deleted file mode 100644 index 8724695f23d..00000000000 --- a/construct/super/super.js +++ /dev/null @@ -1,45 +0,0 @@ -steal('can/util', 'can/construct', function (can, Construct) { - // tests if we can get super in .toString() - var isFunction = can.isFunction, - fnTest = /xyz/.test(function () { - return this.xyz; - }) ? /\b_super\b/ : /.*/, - getset = ['get', 'set'], - getSuper = function (base, name, fn) { - return function () { - var tmp = this._super, - ret; - // Add a new ._super() method that is the same method - // but on the super-class - this._super = base[name]; - // The method only need to be bound temporarily, so we - // remove it when we're done executing - ret = fn.apply(this, arguments); - this._super = tmp; - return ret; - }; - }; - - can.Construct._defineProperty = function(addTo, base, name, descriptor) { - var _super = Object.getOwnPropertyDescriptor(base, name); - if(_super) { - can.each(getset, function (method) { - if(isFunction(_super[method]) && isFunction(descriptor[method])) { - descriptor[method] = getSuper(_super, method, descriptor[method]); - } else if(!isFunction(descriptor[method])) { - descriptor[method] = _super[method]; - } - }); - } - - Object.defineProperty(addTo, name, descriptor); - }; - - // overwrites a single property so it can still call super - can.Construct._overwrite = function (addTo, base, name, val) { - // Check if we're overwriting an existing function - addTo[name] = isFunction(val) && isFunction(base[name]) && fnTest.test(val) ? - getSuper(base, name, val) : val; - }; - return can; -}); diff --git a/construct/super/super.md b/construct/super/super.md deleted file mode 100644 index 834542eebab..00000000000 --- a/construct/super/super.md +++ /dev/null @@ -1,104 +0,0 @@ -@page can.Construct.super -@parent can.Construct.plugins -@plugin can/construct/super -@test can/construct/super/test.html -@download http://donejs.com/can/dist/can.construct.super.js - -can.Construct.super is a plugin that makes it easier to call base -functions from inside inheriting functions. - -@signature `construct._super([...args])` - -Calls the base constructor function's method. - -@param {...[*]} args parameters to pass to the base function - -@body -With this plugin, functions that are inheriting from base functions -are provided with a specialized `this._super` reference to the base -function from which they inherit. - -This is especially useful for calling base classes' `[can.Construct::init init]` and `[can.Construct::setup setup]`, but it can be used in any inheriting function. - -The `Person` and `Programmer` examples from `[can.Construct::init init]` demonstrate `_super`'s use. -Here's how those classes look without can.Construct.super: - -``` -var Person = can.Construct.extend({ - init: function(first, last) { - this.first = first; - this.last = last; - } -}); - -var Programmer = Person.extend({ - init: function(first, last, language) { - // call base's init - Person.prototype.init.apply(this, arguments); - - // other initialization code - this.language = language; - }, - bio: function() { - return "Hi! I'm " + this.first + " " + this.last + - " and I write " + this.language + "."; - } -}); -``` - -And here's how `Programmer` works using `_super`: - -``` -var Programmer = Person.extend({ - init: function(first, last, language) { - // call base's init - this._super(first, last); - - // other initialization code - this.language = language; - }, - bio: function() { - return "Hi! I'm " + this.first + " " + this.last + - " and I write " + this.language + "."; - } -}); -``` - -If you want to pass an array of arguments (or an arguments object) to `_super`, use [apply](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/apply): - -``` -var Programmer = Person.extend({ - init: function(first, last, language) { - // call base's init - this._super.apply(this, arguments); - - // other initialization code - this.language = language; - }, - bio: function() { - return "Hi! I'm " + this.first + " " + this.last + - " and I write " + this.language + "."; - } -}); -``` - -## `_super` on constructors - -can.Construct.super also adds `super` to the constructor, so you -can use it in static functions. - -Here is a base class that has a method that squares numbers and an inherited class that has a method that cubes numbers: - -``` -var Squarer = can.Construct.extend({ - raise: function(n) { - return n*n; - } -}, {}); - -var Cuber = Squarer.extend({ - raise: function(n) { - return n * this._super(n); - } -}, {}); -``` diff --git a/construct/super/super_test.js b/construct/super/super_test.js deleted file mode 100644 index 7aa6c367d32..00000000000 --- a/construct/super/super_test.js +++ /dev/null @@ -1,94 +0,0 @@ -steal("can/construct/super", "steal-qunit", function () { - QUnit.module('can/construct/super'); - test('prototype super', function () { - var A = can.Construct({ - init: function (arg) { - this.arg = arg + 1; - }, - add: function (num) { - return this.arg + num; - } - }); - var B = A({ - init: function (arg) { - this._super(arg + 2); - }, - add: function (arg) { - return this._super(arg + 1); - } - }); - var b = new B(1); - equal(b.arg, 4); - equal(b.add(2), 7); - }); - test('static super', function () { - var First = can.Construct({ - raise: function (num) { - return num; - } - }, {}); - var Second = First({ - raise: function (num) { - return this._super(num) * num; - } - }, {}); - equal(Second.raise(2), 4); - }); - test('findAll super', function () { - var Parent = can.Construct({ - findAll: function () { - equal(this.shortName, 'child'); - return new can.Deferred(); - }, - shortName: 'parent' - }, {}); - var Child = Parent({ - findAll: function () { - return this._super(); - }, - shortName: 'child' - }, {}); - stop(); - expect(1); - Child.findAll({}); - start(); - }); - //!steal-remove-start - // To avoid JSHint complaining about the missing getter - /* jshint ignore:start */ - if(Object.getOwnPropertyDescriptor) { - test("_super supports getters and setters", function () { - var Person = can.Construct.extend({ - get age() { - return 42; - }, - - set name(value) { - this._name = value; - }, - - get name() { - return this._name; - } - }); - - var OtherPerson = Person.extend({ - get age() { - return this._super() + 8; - }, - - set name(value) { - this._super(value + '_super'); - } - }); - - var test = new OtherPerson(); - test.base = 2; - equal(test.age, 50, 'Getter and _super works'); - test.name = 'David'; - equal(test.name, 'David_super', 'Setter ran'); - }); - } - /* jshint ignore:end */ - //!steal-remove-end -}); diff --git a/construct/super/test.html b/construct/super/test.html deleted file mode 100644 index 8bb709794bd..00000000000 --- a/construct/super/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/construct/super - -
        diff --git a/construct/test.html b/construct/test.html deleted file mode 100644 index e51f3c59566..00000000000 --- a/construct/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/construct - -
        diff --git a/control/control.extend.md b/control/control.extend.md deleted file mode 100644 index b3f9e6c7767..00000000000 --- a/control/control.extend.md +++ /dev/null @@ -1,69 +0,0 @@ -@function can.Control.extend -@parent can.Control.static - -@signature `can.Control.extend( [staticProperties,] instanceProperties )` - -Create a new, extended, control constructor -function. - -@param {Object} [staticProperties] An object of properties and methods that are added the control constructor -function directly. The most common property to add is [can.Control.defaults]. - -@param {Object} instanceProperties An object of properties and methods that belong to -instances of the `can.Control` constructor function. These properties are added to the -control's `prototype` object. Properties that -look like event handlers (ex: `"click"` or `"li mouseenter"`) are setup -as event handlers. - -@return {function(new:can.Construct,element,options)} A control constructor function that has been -extended with the provided `staticProperties` and `instanceProperties`. - -@body - -## Examples - - // Control that writes "hello world" - HelloWorld = can.Control.extend({ - init: function(element){ - element.text("hello world") - } - }); - new HelloWorld("#message"); - - // Control that shows how many times - // the element has been clicked on - ClickCounter = can.Control.extend({ - init: function(){ - this.count = 0; - this.element.text("click me") - }, - "click": function(){ - this.count++; - this.element.text("click count = "+this.count) - } - }) - new ClickCounter("#counter"); - - // Counter that counts a specified event - // type - CustomCounter = can.Control.extend({ - defaults: { - eventType: "click" - } - },{ - init: function(){ - this.count = 0; - this.element.text(this.options.eventType+" me") - }, - "{eventType}": function(){ - this.count++; - this.element.text(this.options.eventType+ - " count = "+ - this.count); - } - }) - new CustomCounter("#counter"); - new CustomCounter("#buy",{ - eventType: "mouseenter" - }); - diff --git a/control/control.html b/control/control.html deleted file mode 100644 index dbde32d3f62..00000000000 --- a/control/control.html +++ /dev/null @@ -1,93 +0,0 @@ - - - - Controller Example - - - -
        - -
        - Model Content -
        -
        - View Content -
        -
        - Controller Content -
        -
        - - - - diff --git a/control/control.js b/control/control.js index c9fbbf91d3b..fe6c0fd669f 100644 --- a/control/control.js +++ b/control/control.js @@ -1,334 +1,3 @@ -// # can/control/control.js -// -// Create organized, memory-leak free, rapidly performing, stateful -// controls with declarative eventing binding. Used when creating UI -// controls with behaviors, bound to elements on the page. -// ## helpers +var can = require('../util/can'); -var can = require('can/util/util'); -require('can/construct/construct'); - -// ### bind -// this helper binds to one element and returns a function that unbinds from that element. -var bind = function (el, ev, callback) { - - can.bind.call(el, ev, callback); - - return function () { - can.unbind.call(el, ev, callback); - }; -}, - isFunction = can.isFunction, - extend = can.extend, - each = can.each, - slice = [].slice, - paramReplacer = /\{([^\}]+)\}/g, - special = can.getObject("$.event.special", [can]) || {}, - - // ### delegate - // - // this helper binds to elements based on a selector and returns a - // function that unbinds. - delegate = function (el, selector, ev, callback) { - can.delegate.call(el, selector, ev, callback); - return function () { - can.undelegate.call(el, selector, ev, callback); - }; - }, - - // ### binder - // - // Calls bind or unbind depending if there is a selector. - binder = function (el, ev, callback, selector) { - return selector ? - delegate(el, can.trim(selector), ev, callback) : - bind(el, ev, callback); - }, - - basicProcessor; - -var Control = can.Control = can.Construct( - /** - * @add can.Control - */ - // ## *static functions* - /** - * @static - */ - { - // ## can.Control.setup - // - // This function pre-processes which methods are event listeners and which are methods of - // the control. It has a mechanism to allow controllers to inherit default values from super - // classes, like `can.Construct`, and will cache functions that are action functions (see `_isAction`) - // or functions with an underscored name. - setup: function () { - can.Construct.setup.apply(this, arguments); - - if (can.Control) { - var control = this, - funcName; - - control.actions = {}; - for (funcName in control.prototype) { - if (control._isAction(funcName)) { - control.actions[funcName] = control._action(funcName); - } - } - } - }, - // ## can.Control._shifter - // - // Moves `this` to the first argument, wraps it with `jQuery` if it's - // an element. - _shifter: function (context, name) { - - var method = typeof name === "string" ? context[name] : name; - - if (!isFunction(method)) { - method = context[method]; - } - - return function () { - context.called = name; - return method.apply(context, [this.nodeName ? can.$(this) : this].concat(slice.call(arguments, 0))); - }; - }, - - // ## can.Control._isAction - // - // Return `true` if `methodName` refers to an action. An action is a `methodName` value that - // is not the constructor, and is either a function or string that refers to a function, or is - // defined in `special`, `processors`. Detects whether `methodName` is also a valid method name. - _isAction: function (methodName) { - var val = this.prototype[methodName], - type = typeof val; - - return (methodName !== 'constructor') && - (type === "function" || (type === "string" && isFunction(this.prototype[val]))) && - !! (special[methodName] || processors[methodName] || /[^\w]/.test(methodName)); - }, - // ## can.Control._action - // - // Takes a method name and the options passed to a control and tries to return the data - // necessary to pass to a processor (something that binds things). - // - // For performance reasons, `_action` is called twice: - // * It's called when the Control class is created. for templated method names (e.g., `{window} foo`), it returns null. For non-templated method names it returns the event binding data. That data is added to `this.actions`. - // * It is called wehn a control instance is created, but only for templated actions. - _action: function (methodName, options) { - - // If we don't have options (a `control` instance), we'll run this later. If we have - // options, run `can.sub` to replace the action template `{}` with values from the `options` - // or `window`. If a `{}` template resolves to an object, `convertedName` will be an array. - // In that case, the event name we want will be the last item in that array. - paramReplacer.lastIndex = 0; - if (options || !paramReplacer.test(methodName)) { - var convertedName = options ? can.sub(methodName, this._lookup(options)) : methodName; - if (!convertedName) { - //!steal-remove-start - can.dev.log('can/control/control.js: No property found for handling ' + methodName); - //!steal-remove-end - return null; - } - var arr = can.isArray(convertedName), - name = arr ? convertedName[1] : convertedName, - parts = name.split(/\s+/g), - event = parts.pop(); - - return { - processor: processors[event] || basicProcessor, - parts: [name, parts.join(" "), event], - delegate: arr ? convertedName[0] : undefined - }; - } - }, - _lookup: function (options) { - return [options, window]; - }, - // ## can.Control.processors - // - // An object of `{eventName : function}` pairs that Control uses to - // hook up events automatically. - processors: {}, - // ## can.Control.defaults - // A object of name-value pairs that act as default values for a control instance - defaults: {} - }, { - // ## *prototype functions* - /** - * @prototype - */ - // ## setup - // - // Setup is where most of the Control's magic happens. It performs several pre-initialization steps: - // - Sets `this.element` - // - Adds the Control's name to the element's className - // - Saves the Control in `$.data` - // - Merges Options - // - Binds event handlers using `delegate` - // The final step is to return pass the element and prepareed options, to be used in `init`. - setup: function (element, options) { - - var cls = this.constructor, - pluginname = cls.pluginName || cls._fullName, - arr; - - // Retrieve the raw element, then set the plugin name as a class there. - this.element = can.$(element); - - if (pluginname && pluginname !== 'can_control') { - this.element.addClass(pluginname); - } - - // Set up the 'controls' data on the element. If it does not exist, initialize - // it to an empty array. - arr = can.data(this.element, 'controls'); - if (!arr) { - arr = []; - can.data(this.element, 'controls', arr); - } - arr.push(this); - - // The `this.options` property is an Object that contains configuration data - // passed to a control when it is created (`new can.Control(element, options)`) - // - // The `options` argument passed when creating the control is merged with `can.Control.defaults` - // in [can.Control.prototype.setup setup]. - // - // If no `options` value is used during creation, the value in `defaults` is used instead - this.options = extend({}, cls.defaults, options); - - this.on(); - - return [this.element, this.options]; - }, - // ## on - // - // This binds an event handler for an event to a selector under the scope of `this.element` - // If no options are specified, all events are rebound to their respective elements. The actions, - // which were cached in `setup`, are used and all elements are bound using `delegate` from `this.element`. - on: function (el, selector, eventName, func) { - if (!el) { - this.off(); - - var cls = this.constructor, - bindings = this._bindings, - actions = cls.actions, - element = this.element, - destroyCB = can.Control._shifter(this, "destroy"), - funcName, ready; - - for (funcName in actions) { - // Only push if we have the action and no option is `undefined` - if ( actions.hasOwnProperty(funcName) ) { - ready = actions[funcName] || cls._action(funcName, this.options, this); - if( ready ) { - bindings.control[funcName] = ready.processor(ready.delegate || element, - ready.parts[2], ready.parts[1], funcName, this); - } - } - } - - // Set up the ability to `destroy` the control later. - can.bind.call(element, "removed", destroyCB); - bindings.user.push(function (el) { - can.unbind.call(el, "removed", destroyCB); - }); - return bindings.user.length; - } - - // if `el` is a string, use that as `selector` and re-set it to this control's element... - if (typeof el === 'string') { - func = eventName; - eventName = selector; - selector = el; - el = this.element; - } - - // ...otherwise, set `selector` to null - if (func === undefined) { - func = eventName; - eventName = selector; - selector = null; - } - - if (typeof func === 'string') { - func = can.Control._shifter(this, func); - } - - this._bindings.user.push(binder(el, eventName, func, selector)); - - return this._bindings.user.length; - }, - // ## off - // - // Unbinds all event handlers on the controller. - // This should _only_ be called in combination with .on() - off: function () { - var el = this.element[0], - bindings = this._bindings; - if( bindings ) { - each(bindings.user || [], function (value) { - value(el); - }); - each(bindings.control || {}, function (value) { - value(el); - }); - } - // Adds bindings. - this._bindings = {user: [], control: {}}; - }, - // ## destroy - // - // Prepares a `control` for garbage collection. - // First checks if it has already been removed. Then, removes all the bindings, data, and - // the element from the Control instance. - destroy: function () { - if (this.element === null) { - //!steal-remove-start - can.dev.warn("can/control/control.js: Control already destroyed"); - //!steal-remove-end - return; - } - var Class = this.constructor, - pluginName = Class.pluginName || Class._fullName, - controls; - - this.off(); - - if (pluginName && pluginName !== 'can_control') { - this.element.removeClass(pluginName); - } - - controls = can.data(this.element, "controls"); - controls.splice(can.inArray(this, controls), 1); - - can.trigger(this, "destroyed"); - - this.element = null; - } - }); - -// ## Processors -// -// Processors do the binding. This basic processor binds events. Each returns a function that unbinds -// when called. -var processors = can.Control.processors; -basicProcessor = function (el, event, selector, methodName, control) { - return binder(el, event, can.Control._shifter(control, methodName), selector); -}; - -// Set common events to be processed as a `basicProcessor` -each(["change", "click", "contextmenu", "dblclick", "keydown", "keyup", - "keypress", "mousedown", "mousemove", "mouseout", "mouseover", - "mouseup", "reset", "resize", "scroll", "select", "submit", "focusin", - "focusout", "mouseenter", "mouseleave", - "touchstart", "touchmove", "touchcancel", "touchend", "touchleave", - "inserted","removed", - "dragstart", "dragenter", "dragover", "dragleave", "drag", "drop", "dragend" -], function (v) { - processors[v] = basicProcessor; -}); - -module.exports = exports = Control; +module.exports = can.Control = require('can-control'); diff --git a/control/control.md b/control/control.md deleted file mode 100644 index d830b8a3af2..00000000000 --- a/control/control.md +++ /dev/null @@ -1,382 +0,0 @@ -@constructor can.Control -@parent canjs -@download can/route -@test can/route/test.html -@test can/control/test.html -@inherits can.Construct -@description widget factory with declarative event binding. -@group can.Control.plugins plugins -@link ../docco/control/control.html docco - -@description Create organized, memory-leak free, rapidly performing, -stateful controls with declarative event binding. Use `can.Control` to create UI -controls like tabs, grids, and context menus, -and organize them into higher-order business rules with -[can.route]. It can serve as both a traditional view and a traditional controller. - -@signature `can.Control( [staticProperties,] instanceProperties )` - -Create a new, extended, control constructor -function. This functionality is inherited from [can.Construct] and is deprecated in favor of using -[can.Control.extend]. - -@param {Object} [staticProperties] An object of properties and methods that are added the control constructor -function directly. The most common property to add is [can.Control.defaults]. - -@param {Object} instanceProperties An object of properties and methods that belong to -instances of the `can.Control` constructor function. These properties are added to the -control's `prototype` object. Properties that -look like event handlers (ex: `"click"` or `"li mouseenter"`) are setup -as event handlers (see [Listening to events](#section_Listeningtoevents)). - -@return {function(new:can.Construct,element,options)} A control constructor function that has been -extended with the provided `staticProperties` and `instanceProperties`. - - -@signature `new can.Control( element, options )` - -Create an instance of a control. [can.Control.prototype.setup] processes -the arguments and sets up event binding. Write your initialization -code in [can.Control.prototype.init]. Note, you never call `new can.Control()` directly, -instead, you call it on constructor functions extended from `can.Control`. - -@param {HTMLElement|can.NodeList|CSSSelectorString} element Specifies the element the control -will be created on. - -@param {Object} [options] Option values merged with [can.Control.defaults can.Control.defaults] -and set as [can.Control.prototype.options this.options]. - -@return {can.Control} A new instance of the constructor function extending can.Control. - -@body - -## The Control Lifecycle - -The following walks through a control's lifecycle -with an example todo list widget. It's broken up into the following -lifecycle events: - - - Extending a control - - Creating a control instance - - Listening to events - - Destroying a control - -## Extending a control - -The following example builds up a basic todos widget for listing -and completing todo items. Start by creating a control constructor -function of your own by extending [can.Control] and defining an instance init method. - - var Todos = can.Control.extend({ - init: function( element, options ) { ... } - }); - -## Creating a control instance - -Create an instance of the Todos control on the `todos` element with: - - var todosControl = new Todos( '#todos', {} ); - -The control's associated [can.stache stache] template looks like: - - {{#each todo}} -
      • - {{ name }} - -
      • - {{/each}} - -### `init(element, options)` - -[can.Control.prototype.init] is called with the below arguments when new instances of [can.Control] are created: - -- __element__ - The wrapped element passed to the - control. Control accepts a - raw HTMLElement, a CSS selector, or a NodeList. This is - set as `this.element` on the control instance. -- __options__ - The second argument passed to new Control, extended with - the can.Control's static __defaults__. This is set as - `this.options` on the control instance. Note that static is used - formally to indicate that _default values are shared across control instances_. - -Any additional arguments provided to the constructor will be passed as normal. Use [can.view] to produce a document fragment -from your template and inject it in the passed element. Note that the `todos` parameter passed to [can.view] below -is an instance of [can.List]: - - var Todos = can.Control.extend({ - - //defaults are merged into the options arg provided to the constructor - defaults : { view: 'todos.stache' } - - }, { - init: function( element , options ) { - - //create a pointer to the control's scope - var self = this; - - //run the Todo model's .findAll() method to produce a can.List - Todo.findAll( {}, function( todos ) { - - //create a document fragment with can.view - //and inject it into the provided element's body - self.element.html( can.view(self.options.view, todos) ); - }); - } - }); - - // create a Todos Control with default options - new Todos( document.body.firstElementChild ); - - // overwrite the template default - new Todos( '#todos', { template: 'specialTodos.stache' } ); - -### `this.element` - -[can.Control::element] is the -NodeList consisting of the element the control is created on. - - var todosControl = new Todos( document.body.firstElementChild ); - todosControl.element[0] //-> document.body.firstElementChild - -Each library wraps elements differently. If you are using jQuery, for example, -the element is wrapped with `jQuery( element )`. - -### `this.options` - -[can.Control::options] is the second argument passed to -`new can.Control()`, merged with the control's static __defaults__ property. - -## Listening to events - -Control automatically binds prototype methods that look -like event handlers. Listen to __click__'s on `
      • ` elements like: - - var Todos = can.Control.extend({ - init: function( element , options ) {...}, - - 'li click': function( li, event ) { - console.log( 'You clicked', li.text() ); - - // let other controls know what happened - li.trigger( 'selected' ); - } - }); - -When an `
      • ` is clicked, `"li click"` is called with: - -- The library-wrapped __element__ that was clicked -- The __event__ data - -Control uses event delegation, so you can add `
      • `s without needing to rebind -event handlers. - -To destroy a todo when its `` link -is clicked: - - var Todos = can.Control.extend({ - init: function( element, options ) {...}, - - 'li click': function( li ) {...}, - - 'li .destroy click': function( el, ev ) { - // get the li element that has todo data - var li = el.closest( 'li' ); - - // get the model - var todo = li.data( 'todo' ); - - //destroy it - todo.destroy(); - } - }); - -When the todo is destroyed, stache's live binding will remove its LI automatically. - -### Templated Event Handlers Part 1 `"{eventName}"` - -Customize event handler behavior with `"{NAME}"` in -the event handler name. The following allows customization -of the event that destroys a todo: - - var Todos = can.Control.extend({ - init: function( element , options ) { ... }, - - 'li click': function( li ) { ... }, - - 'li .destroy {destroyEvent}': function( el, ev ) { - // previous destroy code here - } - }); - - // create Todos with this.options.destroyEvent - new Todos( '#todos', { destroyEvent: 'mouseenter' } ); - -Values inside `{NAME}` are looked up on the control's `this.options` first, -and then the `window`. For example, we could customize it instead like: - - var Todos = can.Control.extend({ - init: function( element , options ) { ... }, - - 'li click': function( li ) { ... }, - - 'li .destroy {Events.destroy}': function( el, ev ) { - // previous destroy code here - } - }); - - // Events config - Events = { destroy: 'click' }; - - // Events.destroy is looked up on the window. - new Todos( '#todos' ); - -The selector can also be templated. - - var Todos = can.Control.extend({ - init: function( element , options ) { ... }, - - '{listElement} click': function( li ) { ... }, - - '{listElement} .destroy {destroyEvent}': function( el, ev ) { - // previous destroy code here - } - }); - - // create Todos with this.options.destroyEvent - new Todos( '#todos', { - destroyEvent: 'mouseenter', - listElement: 'li' - } ); - -### Templated Event Handlers Part 2 `"{objectName}"` - -Control can also bind to objects other than `this.element` with -templated event handlers. This is _critical_ -for avoiding memory leaks that are so common among MVC applications. - -If the value inside `{NAME}` is an object, Control will bind to that -object to listen for events. For example, the following tooltip listens to -clicks on the window: - - var Tooltip = can.Control.extend({ - '{window} click': function( el, ev ) { - // hide only if we clicked outside the tooltip - if ( !this.element.has( ev.target ) ) { - this.element.remove(); - } - } - }); - - // create a Tooltip - new Tooltip( $( '
        INFO
        ' ).appendTo( el ) ); - -This is convenient when listening for model changes. If stache were not -taking care of removing `
      • `s after their associated models were destroyed, -we could implement it in `Todos` like: - - var Todos = can.Control.extend({ - init: function( element, options ) {...}, - - 'li click': function( li ) {...}, - - 'li .destroy click': function( el, ev ) { - // get the li element that has todo data - var li = el.closest( 'li' ); - - // get the model - var todo = li.data( 'todo' ); - - //destroy it - todo.destroy(); - }, - - '{Todo} destroyed': function( Todo, ev, todoDestroyed ) { - // find where the element - var index = this.todosList.indexOf( todoDestroyed ); - this.element.children( ':nth-child(' + ( index + 1 ) + ')' ) - .remove(); - } - }); - - new Todos( '#todos' ); - -### `on()` - -[can.Control::on] rebinds a control's event handlers. This is useful when you want -to listen to a specific model and change it: - - var Editor = can.Control.extend({ - todo: function( todo ) { - this.options.todo = todo; - this.on(); - this.setName(); - }, - - // a helper that sets the value of the input - // to the todo's name - setName: function() { - this.element.val( this.options.todo.name ); - }, - - // listen for changes in the todo - // and update the input - '{todo} updated': function() { - this.setName(); - }, - - // when the input changes - // update the todo instance - 'change': function() { - var todo = this.options.todo; - todo.attr( 'name', this.element.val() ); - todo.save(); - } - }); - - var todo1 = new Todo({ id: 6, name: 'trash' }), - todo2 = new Todo({ id: 6, name: 'dishes' }); - - // create the editor; - var editor = new Editor( '#editor' ); - - // show the first todo - editor.todo( todo1 ); - - // switch it to the second todo - editor.todo( todo2 ); - - -## Destroying a control - -[can.Control::destroy] unbinds a control's -event handlers and releases its element, but does not remove -the element from the page. - - var todo = new Todos( '#todos' ); - todo.destroy(); - -When a control's element is removed from the page -__destroy__ is called automatically. - - new Todos( '#todos' ); - $( '#todos' ).remove(); - -All event handlers bound with Control are unbound when the control -is destroyed (or its element is removed). - -_Brief aside on destroy and templated event binding. Taken -together, templated event binding, and control's automatic -clean-up make it almost impossible -to write leaking applications. An application that uses -only templated event handlers on controls within the body -could free up all -data by calling `$(document.body).empty()`._ - -## Tabs Example - -Here is an example of how to build a simple tab widget using can.Control: - - diff --git a/control/control.processor.md b/control/control.processor.md deleted file mode 100644 index 328ad9e2132..00000000000 --- a/control/control.processor.md +++ /dev/null @@ -1,19 +0,0 @@ -@typedef {function(HTMLElement,String,CSSSelectorString,Function,can.Control)} can.Control.processor(element,eventName,selector,handler,control) - -@description A function that handles the binding and unbinding of a [can.Control]'s declarative event method. - -@param {HTMLElement} element the control's element or the object -specified by the templated event handler (`"{object}"`). - -@param {String} eventName The event type. - -@param {CSSSelectorString} selector The selector preceding the event in the binding used on the Control. - -@param {function(this:can.Control,Object,Event)} handler(element, event) The callback function being bound. - -@option {Object} element foo -@option {Event} event bar - -@param {can.Control} control The Control the event is bound on. - -@return {function()} A callback function that unbinds any event handlers bound within this processor. diff --git a/control/control_test.js b/control/control_test.js index c0cffa6d839..7d284dbab6f 100644 --- a/control/control_test.js +++ b/control/control_test.js @@ -1,302 +1 @@ -/*global WeirdBind*/ -/* jshint asi:true*/ -require('can/control/control'); -require('steal-qunit'); - -QUnit.module('can/control'); - -test('data', function () { - var Things = can.Control({}); - can.append(can.$('#qunit-fixture'), '
        divspan
        '); - new Things('#things', {}); - new Things('#things', {}); - equal(can.data(can.$('#things'), 'controls') - .length, 2, 'there are 2 items in the data array'); - can.remove(can.$('#things')); -}); -if (window.jQuery) { - test('bind to any special', function () { - window.jQuery.event.special.crazyEvent = {}; - var called = false; - can.Control('WeirdBind', { - crazyEvent: function () { - called = true; - } - }); - var a = $('
        ') - .appendTo($('#qunit-fixture')); - new WeirdBind(a); - a.trigger('crazyEvent'); - ok(called, 'heard the trigger'); - $('#qunit-fixture') - .html(''); - }); -} -test('parameterized actions', function () { - // YUI does not like non-dom event - if (can.Y) { - can.Y.mix(can.Y.Node.DOM_EVENTS, { - sillyEvent: true - }); - } - var called = false, - WeirderBind = can.Control({ - '{parameterized}': function () { - called = true; - } - }), - a; - can.append(can.$('#qunit-fixture'), '
        '); - a = can.$('#crazy'); - new WeirderBind(a, { - parameterized: 'sillyEvent' - }); - can.trigger(a, 'sillyEvent'); - ok(called, 'heard the trigger'); - can.remove(a); -}); -test('windowresize', function () { - var called = false, - WindowBind = can.Control('', { - '{window} resize': function () { - called = true; - } - }); - can.append(can.$('#qunit-fixture'), '
        '); - new WindowBind('#weird'); - can.trigger(can.$(window), 'resize'); - ok(called, 'got window resize event'); - can.remove(can.$('#weird')); -}); - -test('on', function () { - var called = false, - DelegateTest = can.Control({ - click: function () {} - }), - Tester = can.Control({ - init: function (el, ops) { - this.on(window, 'click', function (ev) { - ok(true, 'Got window click event'); - }); - this.on(window, 'click', 'clicked'); - this.on('click', function () { - ok(true, 'Directly clicked element'); - }); - this.on('click', 'clicked'); - }, - clicked: function () { - ok(true, 'Controller action delegated click triggered, too'); - } - }), - div = document.createElement('div'); - can.append(can.$('#qunit-fixture'), div); - var rb = new Tester(div); - can.append(can.$('#qunit-fixture'), ''); - var els = can.$('#els'); - var dt = new DelegateTest(els); - dt.on(can.$('#els span'), 'a', 'click', function () { - called = true; - }); - can.trigger(can.$('#els a'), 'click'); - ok(called, 'delegate works'); - can.remove(els); - can.trigger(can.$(div), 'click'); - can.trigger(window, 'click'); - rb.destroy(); -}); - -test('inherit', function () { - var called = false, - Parent = can.Control({ - click: function () { - called = true; - } - }), - Child = Parent({}); - can.append(can.$('#qunit-fixture'), ''); - var els = can.$('#els'); - new Child(els); - can.trigger(can.$('#els'), 'click'); - ok(called, 'inherited the click method'); - can.remove(els); -}); -test('space makes event', 1, function () { - if (can.Y) { - can.Y.mix(can.Y.Node.DOM_EVENTS, { - foo: true - }); - } - var Dot = can.Control({ - ' foo': function () { - ok(true, 'called'); - } - }); - can.append(can.$('#qunit-fixture'), ''); - var els = can.$('#els'); - new Dot(els); - can.trigger(can.$('#els'), 'foo'); - can.remove(els); -}); -test('custom events with hyphens work', 1, function () { - can.append(can.$('#qunit-fixture'), '
        '); - var FooBar = can.Control({ - 'span custom-event': function () { - ok(true, 'Custom event was fired.'); - } - }); - new FooBar('#customEvent'); - can.trigger(can.$('#customEvent span'), 'custom-event'); -}); -test('inherit defaults', function () { - var BASE = can.Control({ - defaults: { - foo: 'bar' - } - }, {}); - var INHERIT = BASE({ - defaults: { - newProp: 'newVal' - } - }, {}); - ok(INHERIT.defaults.foo === 'bar', 'Class must inherit defaults from the parent class'); - ok(INHERIT.defaults.newProp === 'newVal', 'Class must have own defaults'); - var inst = new INHERIT(document.createElement('div'), {}); - ok(inst.options.foo === 'bar', 'Instance must inherit defaults from the parent class'); - ok(inst.options.newProp === 'newVal', 'Instance must have defaults of it`s class'); -}); -var bindable = function (b) { - if (window.jQuery) { - return b; - } else {} - return b; -}; -test('on rebinding', 2, function () { - var first = true; - var Rebinder = can.Control({ - '{item} foo': function (item, ev) { - if (first) { - equal(item.id, 1, 'first item'); - first = false; - } else { - equal(item.id, 2, 'first item'); - } - } - }); - var item1 = bindable({ - id: 1 - }), - item2 = bindable({ - id: 2 - }), - rb = new Rebinder(document.createElement('div'), { - item: item1 - }); - can.trigger(item1, 'foo'); - rb.options = { - item: item2 - }; - rb.on(); - can.trigger(item2, 'foo'); -}); -test("actions provide method names", function () { - var Tester = can.Control({ - "{item1} foo": "food", - "{item2} bar": "food", - food: function (item, ev, data) { - ok(true, "food called") - ok(item === item1 || item === item2, "called with an item") - } - }); - - var item1 = {}, - item2 = {}; - - new Tester(document.createElement('div'), { - item1: item1, - item2: item2 - }); - - can.trigger(item1, "foo"); - can.trigger(item2, "bar"); -}); -test("Don\'t bind if there are undefined values in templates", function () { - can.Control.processors.proc = function () { - ok(false, 'This processor should never be called'); - }; - var Control = can.Control({}, { - '{noExistStuff} proc': function () {} - }); - var c = new Control(document.createElement('div')); - equal(c._bindings.user.length, 1, 'There is only one binding'); -}); -test('Multiple calls to destroy', 2, function () { - var Control = can.Control({ - destroy: function () { - ok(true); - can.Control.prototype.destroy.call(this); - } - }), - div = document.createElement('div'), - c = new Control(div); - c.destroy(); - c.destroy(); -}); -// Added support for drag and drop events (#1955) -test("drag and drop events", function() { - expect(7); - var DragDrop = can.Control("", { - " dragstart": function() { - ok(true, "dragstart called"); - }, - " dragenter": function() { - ok(true, "dragenter called"); - }, - " dragover": function() { - ok(true, "dragover called"); - }, - " dragleave": function() { - ok(true, "dragleave called"); - }, - " drag": function() { - ok(true, "drag called"); - }, - " drop": function() { - ok(true, "drop called"); - }, - " dragend": function() { - ok(true, "dragend called"); - } - }); - can.append(can.$("#qunit-fixture"), '
        '); - new DragDrop("#draggable"); - can.trigger(can.$("#draggable"), "dragstart"); - can.trigger(can.$("#draggable"), "dragenter"); - can.trigger(can.$("#draggable"), "dragover"); - can.trigger(can.$("#draggable"), "dragleave"); - can.trigger(can.$("#draggable"), "drag"); - can.trigger(can.$("#draggable"), "drop"); - can.trigger(can.$("#draggable"), "dragend"); - can.remove(can.$("#draggable")); -}); -if (can.dev) { - test('Control is logging information in dev mode', function () { - expect(2); - var oldlog = can.dev.log; - var oldwarn = can.dev.warn; - can.dev.log = function (text) { - equal(text, 'can/control/control.js: No property found for handling {dummy} change', 'Text logged as expected'); - }; - var Control = can.Control({ - '{dummy} change': function () {} - }); - var instance = new Control(document.createElement('div')); - can.dev.warn = function (text) { - equal(text, 'can/control/control.js: Control already destroyed'); - }; - instance.destroy(); - instance.destroy(); - can.dev.warn = oldwarn; - can.dev.log = oldlog; - }); -} +require('can-control/can-control_test'); diff --git a/control/doc/defaults.md b/control/doc/defaults.md deleted file mode 100644 index 103a5923630..00000000000 --- a/control/doc/defaults.md +++ /dev/null @@ -1,46 +0,0 @@ -@property {Object} can.Control.defaults defaults -@parent can.Control.static -@description Default values for the Control's options. - -@body - -Default options provided for when a new control is created without values set in `options`. - -`defaults` provides default values for a Control's options. -Options passed into the constructor function will be shallowly merged -into the values from defaults in [can.Control::setup], and -the result will be stored in [can.Control::options this.options]. - - Message = can.Control.extend({ - defaults: { - message: "Hello World" - } - }, { - init: function(){ - this.element.text( this.options.message ); - } - }); - - new Message( "#el1" ); //writes "Hello World" - new Message( "#el12", { message: "hi" } ); //writes hi - -## Shared Properties - -New instances of a can.Control will create a shallow copy of the default -options. Be aware as shallow copies keep a reference to object types, such as -objects, maps and computes. - -``` -var Sample = can.Control.extend({ - defaults: { - computedProp: can.compute(), - primitiveProp: 'sample' - } -}, {}); - -var a = new Sample('div'); -var b = new Sample('li'); - -//`computedProp` will be shared across instances of the `Sample` control. -//a.options.computedProp === b.options.computedProp -``` diff --git a/control/doc/destroy.md b/control/doc/destroy.md deleted file mode 100644 index 24f04462a24..00000000000 --- a/control/doc/destroy.md +++ /dev/null @@ -1,71 +0,0 @@ -@function can.Control.prototype.destroy destroy -@parent can.Control.prototype -@description Remove a Control from an element and clean up the Control. -@signature `control.destroy()` - -Prepares a control for garbage collection and is a place to -reset any changes the control has made. - -@body -## Allowing Garbage Collection - -Destroy is called whenever a control's element is removed from the page using -the library's standard HTML modifier methods. This means that you -don't have to call destroy yourself and it -will be called automatically when appropriate. - -The following `Clicker` widget listens on the window for clicks and updates -its element's innerHTML. If we remove the element, the window's event handler -is removed auto-magically: - - - Clicker = can.Control({ - "{window} click": function() { - this.element.html( this.count ? - this.count++ : this.count = 0 ); - } - }); - - // create a clicker on an element - new Clicker( "#clickme" ); - - // remove the element - $( '#clickme' ).remove(); - - -## Teardown in Destroy - -Sometimes, you want to reset a controlled element back to its -original state when the control is destroyed. Overwriting destroy -lets you write teardown code of this manner. - -__NOTE__: When overwriting destroy, make sure you call Control's base functionality. - -The following example changes an element's text when the control is -created and sets it back when the control is removed: - - Changer = can.Control.extend({ - init: function() { - this.oldText = this.element.text(); - this.element.text( "Changed!!!" ); - }, - destroy: function() { - this.element.text( this.oldText ); - can.Control.prototype.destroy.call( this ); - } - }); - - // create a changer which changes #myel's text - var changer = new Changer( '#myel' ); - - // destroy changer which will reset it - changer.destroy(); - -## Base Functionality - -Control prepares the control for garbage collection by: - -- unbinding all event handlers -- clearing references to this.element and this.options -- clearing the element's reference to the control -- removing it's `can.Control.pluginName` from the element's className diff --git a/control/doc/element.md b/control/doc/element.md deleted file mode 100644 index a2df1d57048..00000000000 --- a/control/doc/element.md +++ /dev/null @@ -1,77 +0,0 @@ -@property {can.NodeList} can.Control.prototype.element element -@parent can.Control.prototype -@description The element passed to the Control when creating a new instance. - -@body - -The control instance's HTMLElement (or window) wrapped by the -util library for ease of use. - -It is set by the first parameter to `new can.Construct( element, options )` -in [can.Control::setup]. By default, a control listens to events on `this.element`. - -### Example - NodeList - -The following `HelloWorld` control sets the control`s text to "Hello World": - - HelloWorld = can.Control({ - init: function(){ - this.element.text( 'Hello World' ); - } - }); - - // create the controller on the element - new HelloWorld( document.getElementById( '#helloworld' ) ); - -## Wrapped NodeList - -`this.element` is a wrapped NodeList of one HTMLELement (or window). This -is for convenience in libraries like jQuery where all methods operate only on a -NodeList. To get the raw HTMLElement, write: - - this.element[0] //-> HTMLElement - -## Changing `this.element` - -Sometimes you don't want what's passed to `new can.Control` -to be `this.element`. You can change this by overwriting -setup or by unbinding, setting this.element, and rebinding. - -### Overwriting Setup - -The following Combobox overwrites setup to wrap a -select element with a div. That div is used -as `this.element`. Notice how `destroy` sets back the -original element. - - Combobox = can.Control({ - setup: function( el, options ) { - this.oldElement = $( el ); - var newEl = $( '
        ' ); - this.oldElement.wrap( newEl ); - can.Control.prototype.setup.call( this, newEl, options ); - }, - init: function() { - this.element //-> the div - }, - ".option click": function() { - // event handler bound on the div - }, - destroy: function() { - var div = this.element; //save reference - can.Control.prototype.destroy.call( this ); - div.replaceWith( this.oldElement ); - } - }); - -### Unbinding, setting, and rebinding. - -You could also change this.element by calling -[can.Control::off], setting this.element, and -then calling [can.Control::on] like: - - move: function( newElement ) { - this.off(); - this.element = $( newElement ); - this.on(); - } diff --git a/control/doc/on.md b/control/doc/on.md deleted file mode 100644 index 5a837ac2725..00000000000 --- a/control/doc/on.md +++ /dev/null @@ -1,96 +0,0 @@ -@function can.Control.prototype.on on -@parent can.Control.prototype -@description Bind an event handler to a Control, or rebind all event handlers on a Control. - -@signature `control.on([el,] selector, eventName, func)` -@param {HTMLElement|jQuery collection|Object} [el=this.element] -The element to be bound. If no element is provided, the control's element is used instead. -@param {CSSSelectorString} selector A CSS selector for event delegation. -@param {String} eventName The name of the event to listen for. -@param {Function|String} func A callback function or the String name of a control function. If a control -function name is given, the control function is called back with the bound element and event as the first -and second parameter. Otherwise the function is called back like a normal bind. -@return {Number} The id of the binding in this._bindings. - -`on(el, selector, eventName, func)` binds an event handler for an event to a selector under the scope of the given element. - -@signature `control.on()` - -Rebind all of a control's event handlers. - -@return {Number} The number of handlers bound to this Control. - -@body -`this.on()` is used to rebind -all event handlers when [can.Control::options this.options] has changed. It -can also be used to bind or delegate from other elements or objects. - -## Rebinding - -By using templated event handlers, a control can listen to objects outside -`this.element`. This is extremely common in MVC programming. For example, -the following control might listen to a task model's `completed` property and -toggle a strike className like: - - TaskStriker = can.Control({ - "{task} completed": function(){ - this.update(); - }, - update: function(){ - if ( this.options.task.completed ) { - this.element.addClass( 'strike' ); - } else { - this.element.removeClass( 'strike' ); - } - } - }); - - var taskstriker = new TaskStriker({ - task: new Task({ completed: 'true' }) - }); - -To update the `taskstriker`'s task, add a task method that updates -this.options and rebinds the event handlers for the new task like: - - TaskStriker = can.Control({ - "{task} completed": function(){ - this.update(); - }, - update: function() { - if ( this.options.task.completed ) { - this.element.addClass( 'strike' ); - } else { - this.element.removeClass( 'strike' ); - } - }, - task: function( newTask ) { - this.options.task = newTask; - this.on(); - this.update(); - } - }); - - var taskstriker = new TaskStriker({ - task: new Task({ completed: true }) - }); - - // Now, add a new task that is not yet completed - taskstriker.task(new Task({ completed: false })); - -## Adding new events - -If events need to be bound to outside of the control and templated event handlers -are not sufficient, you can call this.on to bind or delegate programmatically: - - init: function() { - // calls somethingClicked( el, ev ) - this.on( 'click', 'somethingClicked' ); - - // calls function when the window is clicked - this.on( window, 'click', function( ev ) { - // do something - }); - }, - somethingClicked: function( el, ev ) { - // ... - } \ No newline at end of file diff --git a/control/doc/options.md b/control/doc/options.md deleted file mode 100644 index 794b6de5dea..00000000000 --- a/control/doc/options.md +++ /dev/null @@ -1,41 +0,0 @@ -@property {Object} can.Control.prototype.options options -@parent can.Control.prototype -@description Options used to configure a control. - -@body - -The `this.options` property is an Object that contains -configuration data passed to a control when it is -created (`new can.Control(element, options)`). - -In the following example, an options object with -a message is passed to a `Greeting` control. The -`Greeting` control changes the text of its [can.Control::element element] -to the options' message value. - - var Greeting = can.Control.extend({ - init: function(){ - this.element.text( this.options.message ) - } - }); - - new Greeting("#greeting",{message: "I understand this.options"}); - -The options argument passed when creating the control -is merged with [can.Control.defaults defaults] in -[can.Control.prototype.setup setup]. - -In the following example, if no message property is provided, -the defaults' message property is used. - - var Greeting = can.Control.extend({ - defaults: { - message: "Defaults merged into this.options" - } - },{ - init: function(){ - this.element.text( this.options.message ) - } - }); - - new Greeting("#greeting"); \ No newline at end of file diff --git a/control/doc/processors.md b/control/doc/processors.md deleted file mode 100644 index 975c81fc4d0..00000000000 --- a/control/doc/processors.md +++ /dev/null @@ -1,91 +0,0 @@ -@property {Object.} can.Control.processors processors -@parent can.Control.static -@description A collection of hookups for custom events on Controls. -@body - -`processors` is an object that allows you to add new events to bind -to on a control, or to change how existent events are bound. Each -key-value pair of `processors` is a specification that pertains to -an event where the key is the name of the event, and the value is -a function that processes calls to bind to the event. - -The processor function takes five arguments: - -- _el_: The Control's element. -- _event_: The event type. -- _selector_: The selector preceding the event in the binding used on the Control. -- _callback_: The callback function being bound. -- _control_: The Control the event is bound on. - -Inside your processor function, you should bind _callback_ to the event, and -return a function for can.Control to call when _callback_ needs to be unbound. -(If _selector_ is defined, you will likely want to use some form of delegation -to bind the event.) - -Here is a Control with a custom event processor set and two callbacks bound -to that event: - - can.Control.processors.birthday = function(el, ev, selector, callback, control) { - if(selector) { - myFramework.delegate(ev, el, selector, callback); - return function() { myFramework.undelegate(ev, el, selector, callback); }; - } else { - myFramework.bind(ev, el, callback); - return function() { myFramework.unbind(ev, el, callback); }; - } - }; - - can.Control("EventTarget", { }, { - 'birthday': function(el, ev) { - // do something appropriate for the occasion - }, - '.grandchild birthday': function(el, ev) { - // do something appropriate for the occasion - } - }); - - var target = new EventTarget('#person'); - -When `target` is initialized, can.Control will call `can.Control.processors.birthday` -twice (because there are two event hookups for the _birthday_ event). The first -time it's called, the arguments will be: - -- _el_: A NodeList that wraps the element with id 'person'. -- _ev_: `'birthday'` -- _selector_: `''` -- _callback_: The function assigned to `' birthday'` in the prototype section of `EventTarget`'s -definition. -- _control_: `target` itself. - -The second time, the arguments are slightly different: - -- _el_: A NodeList that wraps the element with id 'person'. -- _ev_: `'birthday'` -- _selector_: `'.grandchild'` -- _callback_: The function assigned to `'.grandchild birthday'` in the prototype section of `EventTarget`'s -definition. -- _control_: `target` itself. - -can.Control already has processors for these events: - -- change -- click -- contextmenu -- dblclick -- focusin -- focusout -- keydown -- keyup -- keypress -- mousedown -- mouseenter -- mouseleave -- mousemove -- mouseout -- mouseover -- mouseup -- reset -- resize -- scroll -- select -- submit diff --git a/control/doc/setup.md b/control/doc/setup.md deleted file mode 100644 index e413d6ce671..00000000000 --- a/control/doc/setup.md +++ /dev/null @@ -1,45 +0,0 @@ -@function can.Control.prototype.setup setup -@parent can.Control.prototype -@description Perform pre-initialization logic for control instances and classes. - -@signature `control.setup(element, options)` -@param {HTMLElement|NodeList|String} element The element as passed to the constructor. -@param {Object} [options] option values for the control. These get added to -this.options and merged with [can.Control.static.defaults defaults]. -@return {undefined|Array} return an array if you want to change what init is called with. By -default it is called with the element and options passed to the control. - -@body - -## Lifecycle of `setup` - -Setup, when called, does the following: - -### Sets this.element - -The first parameter passed to new Control( el, options ) is expected to be -an element. This gets converted to a Wrapped NodeList element and set as -[can.Control.prototype.element this.element]. - -### Adds the control's name to the element's className - -Control adds it's plugin name to the element's className for easier -debugging. For example, if your Control is named "Foo.Bar", it adds -"foo_bar" to the className. - -### Saves the control in $.data - -A reference to the control instance is saved in $.data. You can find -instances of "Foo.Bar" like: - - $( '#el' ).data( 'controls' )[ 'foo_bar' ] - -### Merges Options - -Merges the default options with optional user-supplied ones. -Additionally, default values are exposed in the static [can.Control.static.defaults defaults] -so that users can change them. - -### Binds event handlers - -Setup does the event binding described in [can.Control]. \ No newline at end of file diff --git a/control/eventDescription.md b/control/eventDescription.md deleted file mode 100644 index 424de44c227..00000000000 --- a/control/eventDescription.md +++ /dev/null @@ -1,5 +0,0 @@ -@typedef {String} can.Control.eventDescription eventDescription -@parent can.Control.types - -@signature `"[CONTEXT ][SELECTOR ]EVENTNAME"` - diff --git a/control/eventHandler.md b/control/eventHandler.md deleted file mode 100644 index 189966ab047..00000000000 --- a/control/eventHandler.md +++ /dev/null @@ -1,5 +0,0 @@ -@typedef {function} can.Control.eventHandler eventHandler(element, event) -@parent can.Control.types - -@signature `function(element, event)` - diff --git a/control/perf.html b/control/perf.html deleted file mode 100644 index 411aaaaebe6..00000000000 --- a/control/perf.html +++ /dev/null @@ -1,201 +0,0 @@ - - - - Playground - - - -

        In synch mode:
        Can ~300
        Backbone ~ 450
        Ember ~1400

        -

        In asynch mode:
        Can ~150
        Backbone ~150
        Ember ~90

        - - - - -
        - - - - - - - -
        -
        - -
        - Tab 1 Content -
        -
        - Tab 2 Content -
        -
        - Tab 3 Content -
        -
        -
        - - - \ No newline at end of file diff --git a/control/test.html b/control/test.html deleted file mode 100644 index 68431c836e3..00000000000 --- a/control/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/control - -
        diff --git a/event/delegate/delegate.js b/event/delegate/delegate.js deleted file mode 100644 index 26e4ecf4b22..00000000000 --- a/event/delegate/delegate.js +++ /dev/null @@ -1,85 +0,0 @@ -// # can/event/delegate -// -// This is a `can/event` plugin that implements the ability to delegate events -// on normal objects. Delegation depends on the inclusion of the -// `can/event/propagate` for event propagation/bubbling. There is an additional dependency -// on `can/construct`, as in order to have a friendly delegation syntax, it needs to know -// a little about how to describe the objects (in this case, the shortName on the constructor). -var can = require('can/util/can'); -require('can/event/event'); -require('can/event/propagate/propagate'); -require('can/construct/construct'); - -// ## can.event.delegate -// -// Adds a delegate event listener. -/** - * @function can.event.delegate.delegate - * @parent can.event.delegate - * @plugin can/event/delegate - * @signature `obj.delegate( selector, event, handler )` - * - * Adds a delegate event listener. - * - * @param {String} selector A selector to match against the stack of propagated objects. - * The names used are based on the names of the constructors of - * the original objects. - * @param {Object|String} event The event name or object to listen to. - * @param {Function} handler The handler to call when the event occurs. - * @return {Object} this - */ -can.event.delegate = function(selector, event, handler) { - // Split the selector into parts that can be verified - var parts = selector && selector.split(/\s+/), - // Implement the custom delegation handler - // This is used to verify the selector prior to executing the original handler - delegate = function(ev) { - // Verify descendants against the selector - // These descendants are tracked in the `can/event/propagate` plugin - for (var i = 0, j = 0, descendant; j < parts.length && (descendant = ev.descendants[i]); i++) { - // A descendant name is considered valid if it matches the `shortName` or `_shortName` - // properties on the constructor. Generally, this assumes that `can.Construct` or a - // similar can-based class is used (which defines those properties by default). - if (descendant.constructor && (parts[j] === descendant.constructor._shortName || parts[j] === descendant.constructor.shortName)) { - j++; - } - } - - // Only if every part of the selector was matched, execute the handler - if (j >= parts.length) { - return handler.apply(this, arguments); - } - }; - - // Cache the selector and handler, so that we can undelegate later with only that information. - delegate.handler = handler; - delegate.selector = selector; - return can.addEvent.call(this, event, delegate); -}; - -// ## can.event.undelegate -// -// Removes a delegate event listener. -/** - * @function can.event.delegate.undelegate - * @parent can.event.delegate - * @plugin can/event/delegate - * @signature `object.undelegate( selector, event, handler )` - * - * Removes a delegate event listener. - * - * @param {String} selector A selector to match against the stack of propagated objects. - * @param {Object|String} event The event name or object to listen to. - * @param {Function} handler The handler to call when the event occurs. - * @return {Object} this - */ -can.event.undelegate = function(selector, event, fn) { - var isFunction = typeof fn === 'function'; - // Attempt to remove the event, with a custom verification function - return can.removeEvent.call(this, event, fn, function(ev) { - // This allows us to check for the cached selector and handler - // as part of the verification. This is necessary because the user isn't - // going to have access to the custom handler generated for delegation. - return isFunction && (ev.handler === fn || (ev.handler.handler === fn && ev.handler.selector === selector)) || !isFunction && ev.cid === fn; - }); -}; diff --git a/event/delegate/delegate.md b/event/delegate/delegate.md deleted file mode 100644 index 1c70cfe0259..00000000000 --- a/event/delegate/delegate.md +++ /dev/null @@ -1,33 +0,0 @@ -@hide - -@page can.event.delegate -@parent can.event.plugins -@plugin can/event/delegate -@test can/event/delegate/test.html - -**This plugin is in development and should not be included in the official documentation.** - - // - // ``` - // var SomeClass = can.Construct("SomeClass"); - // SomeClass.shortName; // = "SomeClass" - // SomeClass._shortName; // = "some_class" - // var ParentClass = can.Construct("ParentClass"); - // - // // Set up event and propagaton support - // can.extend(SomeClass.prototype, can.event, { propagate: "parent" }); - // can.extend(ParentClass.prototype, can.event); - // - // // Create a couple nodes and associate them using the propagate property - // var parent = new ParentClass(); - // var child = new SomeClass(); - // child.parent = parent; - // - // // Listen for an event - // parent.delegate("some_class", "action", function(ev) { - // // This will fire if (and only if) ev.target matches "some_class" - // }); - // - // // This triggers the delegate listener! - // child.dispatch("action"); - // ``` diff --git a/event/delegate/delegate_test.js b/event/delegate/delegate_test.js deleted file mode 100644 index 743b61efc0a..00000000000 --- a/event/delegate/delegate_test.js +++ /dev/null @@ -1,95 +0,0 @@ -var event = require('can/event/delegate/delegate'); -require('steal-qunit'); - -QUnit.module('can/event/delegate'); - -test('Delegate/undelegate are bound and unbound properly', 9, function() { - var node1 = { name: 'root' }, - node2 = { name: 'mid', parent: node1 }, - node3 = { name: 'child', parent: node2 }, - fn; - - can.extend(node1, can.event, { propagate: 'parent' }); - can.extend(node2, can.event, { propagate: 'parent' }); - can.extend(node3, can.event, { propagate: 'parent' }); - - // Verify delegate - node1.delegate('', 'action', fn = function(ev) { - equal(ev.target.name, 'custom', 'target is custom target'); - equal(ev.currentTarget.name, 'root', 'currentTarget is node1'); - equal(this.name, 'root', 'delegate is node1'); - equal(ev.descendants.length, 3, 'event has 3 descendants (node2, node3, custom target)'); - equal(ev.descendants[0].name, 'mid', 'first descendant is node2'); - equal(ev.descendants[1].name, 'child', 'second descendant is node3'); - equal(ev.descendants[2].name, 'custom', 'third descendant is custom target'); - }); - equal(node1.__bindEvents.action.length, 1); - node3.dispatch({ - type: 'action', - target: { name: 'custom' } - }); - - // Verify undelegate - node1.undelegate('', 'action', fn); - equal(node1.__bindEvents.action.length, 0); - node3.dispatch({ - type: 'action', - target: { name: 'custom' } - }); -}); - -test('Delegate selector is enforced', 17, function() { - var DelegateClass = can.Construct.extend("DelegateClass", { - init: function(options) { - can.simpleExtend(this, options); - } - }), - AnotherClass = DelegateClass.extend("AnotherClass"), - node1 = new DelegateClass({ name: 'root' }), - node2 = new AnotherClass({ name: 'mid', parentNode: node1 }), - node3 = new DelegateClass({ name: 'child', parent: node2 }), - fn, fn2; - - can.extend(DelegateClass.prototype, can.event, { propagate: 'parent' }); - can.extend(AnotherClass.prototype, can.event, { propagate: 'parentNode' }); - - // Verify delegate - node1.delegate('AnotherClass DelegateClass', 'action', fn = function(ev) { - equal(ev.target.name, 'custom', 'target is custom target'); - equal(ev.currentTarget.name, 'root', 'currentTarget is node1'); - equal(this.name, 'root', 'delegate is node1'); - equal(ev.descendants.length, 3, 'event has 3 descendants (node2, node3, custom target)'); - equal(ev.descendants[0].name, 'mid', 'first descendant is node2'); - equal(ev.descendants[1].name, 'child', 'second descendant is node3'); - equal(ev.descendants[2].name, 'custom', 'third descendant is custom target'); - }); - // Support _shortName and shortName - node1.delegate('another_class delegate_class', 'action', fn2 = function(ev) { - equal(ev.target.name, 'custom', 'target is custom target'); - equal(ev.currentTarget.name, 'root', 'currentTarget is node1'); - equal(this.name, 'root', 'delegate is node1'); - equal(ev.descendants.length, 3, 'event has 3 descendants (node2, node3, custom target)'); - equal(ev.descendants[0].name, 'mid', 'first descendant is node2'); - equal(ev.descendants[1].name, 'child', 'second descendant is node3'); - equal(ev.descendants[2].name, 'custom', 'third descendant is custom target'); - }); - node1.delegate('NotTheRightClass', 'action', function(ev) { - notEqual(this.name, 'root', 'This delegate should never fire'); - }); - equal(node1.__bindEvents.action.length, 3, 'delegate exists in the events'); - node3.dispatch({ - type: 'action', - target: { name: 'custom' } - }); - - // Verify undelegate - node1.undelegate('', 'action', fn); - equal(node1.__bindEvents.action.length, 3, 'a non-matching selector should not remove the delegate'); - node1.undelegate('AnotherClass DelegateClass', 'action', fn); - node1.undelegate('another_class delegate_class', 'action', fn2); - equal(node1.__bindEvents.action.length, 1, 'a matching selector should remove the delegate'); - node3.dispatch({ - type: 'action', - target: { name: 'custom' } - }); -}); diff --git a/event/delegate/test.html b/event/delegate/test.html deleted file mode 100644 index 4b9db014a77..00000000000 --- a/event/delegate/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/event/delegate - -
        diff --git a/event/event.js b/event/event.js index d2ff004c954..199f7bc89dd 100644 --- a/event/event.js +++ b/event/event.js @@ -1,444 +1,6 @@ -// # can/event -// -// Implements a basic event system that can be used with any type of object. -// In addition to adding basic event functionality, it also provides the `can.event` object -// that can be mixed into objects and prototypes. -// -// Most of the time when this is used, it will be used with the mixin: -// -// ``` -// var SomeClass = can.Construct("SomeClass"); -// can.extend(SomeClass.prototype, can.event); -// ``` -var can = require('can/util/can'); +var can = require('../util/can'); -// ## can.event.addEvent -// -// Adds a basic event listener to an object. -// This consists of storing a cache of event listeners on each object, -// that are iterated through later when events are dispatched. -/** - * @function can.event.addEvent - * @parent can.event.static - * @signature `obj.addEvent( event, handler )` - * - * Add a basic event listener to an object. - * - * @param {String} event The name of the event to listen for. - * @param {Function} handler The handler that will be executed to handle the event. - * @return {Object} this - * - * @signature `can.event.addEvent.call( obj, event, handler )` - * - * This syntax can be used for objects that don't include the `can.event` mixin. - */ -can.addEvent = function (event, handler) { - // Initialize event cache. - var allEvents = this.__bindEvents || (this.__bindEvents = {}), - eventList = allEvents[event] || (allEvents[event] = []); +can.assign(can, require('can-event')); +can.assign(can, require('can-event/batch/batch')); - // Add the event - eventList.push({ - handler: handler, - name: event - }); - return this; -}; - -// ## can.event.listenTo -// -// Listens to an event without know how bind is implemented. -// The primary use for this is to listen to another's objects event while -// tracking events on the local object (similar to namespacing). -// -// The API was heavily influenced by BackboneJS: http://backbonejs.org/ -/** - * @function can.event.listenTo - * @parent can.event.static - * @signature `obj.listenTo( other, event, handler )` - * - * Listens for an event on another object. - * This is similar to concepts like event namespacing, except that the namespace - * is the scope of the calling object. - * - * @param {Object} other The object to listen for events on. - * @param {String} event The name of the event to listen for. - * @param {Function} handler The handler that will be executed to handle the event. - * @return {Object} this - * - * @signature `can.event.listenTo.call( obj, other, event, handler )` - * - * This syntax can be used for objects that don't include the `can.event` mixin. - */ -can.listenTo = function (other, event, handler) { - // Initialize event cache - var idedEvents = this.__listenToEvents; - if (!idedEvents) { - idedEvents = this.__listenToEvents = {}; - } - - // Identify the other object - var otherId = can.cid(other); - var othersEvents = idedEvents[otherId]; - - // Create a local event cache - if (!othersEvents) { - othersEvents = idedEvents[otherId] = { - obj: other, - events: {} - }; - } - var eventsEvents = othersEvents.events[event]; - if (!eventsEvents) { - eventsEvents = othersEvents.events[event] = []; - } - - // Add the event, both locally and to the other object - eventsEvents.push(handler); - can.bind.call(other, event, handler); -}; - -// ## can.event.stopListening -// -// Stops listening for events on other objects -/** - * @function can.event.stopListening - * @parent can.event.static - * @signature `obj.stopListening( other, event, handler )` - * - * Stops listening for an event on another object. - * - * @param {Object} other The object to listen for events on. - * @param {String} event The name of the event to listen for. - * @param {Function} handler The handler that will be executed to handle the event. - * @return {Object} this - * - * @signature `can.event.stopListening.call( obj, other, event, handler )` - * - * This syntax can be used for objects that don't include the `can.event` mixin. - */ -can.stopListening = function (other, event, handler) { - var idedEvents = this.__listenToEvents, - iterIdedEvents = idedEvents, - i = 0; - if (!idedEvents) { - return this; - } - if (other) { - var othercid = can.cid(other); - (iterIdedEvents = {})[othercid] = idedEvents[othercid]; - // you might be trying to listen to something that is not there - if (!idedEvents[othercid]) { - return this; - } - } - - // Clean up events on the other object - for (var cid in iterIdedEvents) { - var othersEvents = iterIdedEvents[cid], - eventsEvents; - other = idedEvents[cid].obj; - - // Find the cache of events - if (!event) { - eventsEvents = othersEvents.events; - } else { - (eventsEvents = {})[event] = othersEvents.events[event]; - } - - // Unbind event handlers, both locally and on the other object - for (var eventName in eventsEvents) { - var handlers = eventsEvents[eventName] || []; - i = 0; - while (i < handlers.length) { - if (handler && handler === handlers[i] || !handler) { - can.unbind.call(other, eventName, handlers[i]); - handlers.splice(i, 1); - } else { - i++; - } - } - // no more handlers? - if (!handlers.length) { - delete othersEvents.events[eventName]; - } - } - if (can.isEmptyObject(othersEvents.events)) { - delete idedEvents[cid]; - } - } - return this; -}; - -// ## can.event.removeEvent -// -// Removes a basic event listener from an object. -// This removes event handlers from the cache of listened events. -/** - * @function can.event.removeEvent - * @parent can.event.static - * @signature `obj.removeEvent( event, handler )` - * - * Removes a basic event listener from an object. - * - * @param {String} event The name of the event to listen for. - * @param {Function} handler The handler that will be executed to handle the event. - * @param {Function} [__validate] An extra function that can validate an event handler - * as a match. This is an internal parameter and only used - * for `can/event` plugins. - * @return {Object} this - * - * @signature `can.event.removeEvent.call( obj, event, handler )` - * - * This syntax can be used for objects that don't include the `can.event` mixin. - */ -can.removeEvent = function (event, fn, __validate) { - if (!this.__bindEvents) { - return this; - } - var events = this.__bindEvents[event] || [], - i = 0, - ev, isFunction = typeof fn === 'function'; - while (i < events.length) { - ev = events[i]; - // Determine whether this event handler is "equivalent" to the one requested - // Generally this requires the same event/function, but a validation function - // can be included for extra conditions. This is used in some plugins like `can/event/namespace`. - if (__validate ? __validate(ev, event, fn) : isFunction && ev.handler === fn || !isFunction && (ev.cid === fn || !fn)) { - events.splice(i, 1); - } else { - i++; - } - } - return this; -}; - -// ## can.event.dispatch -// -// Dispatches/triggers a basic event on an object. -/** - * @function can.event.dispatch - * @parent can.event.static - * @signature `obj.dispatch( event, args )` - * - * Dispatches/triggers a basic event on an object. - * - * @param {String|Object} event The event to dispatch - * @param {Array} [args] Additional arguments to pass to event handlers - * @return {Object} event The resulting event object - * - * @signature `can.event.dispatch.call( obj, event, args )` - * - * This syntax can be used for objects that don't include the `can.event` mixin. - */ -can.dispatch = function (event, args) { - var events = this.__bindEvents; - if (!events) { - return; - } - var eventName; - // Initialize the event object - if (typeof event === 'string') { - eventName = event; - event = { - type: event - }; - } else { - eventName = event.type; - } - - // Grab event listeners - var handlers = events[eventName]; - if(!handlers) { - return; - } else { - handlers = handlers.slice(0); - } - - - var passed = [event]; - - // Execute handlers listening for this event. - if(args) { - passed.push.apply(passed, args); - } - - for (var i = 0, len = handlers.length; i < len; i++) { - handlers[i].handler.apply(this, passed); - } - - return event; -}; - -// ## can.event.one -// -// Adds a basic event listener that listens to an event once and only once. -/** - * @function can.event.one - * @parent can.event.static - * @signature `obj.one( event, handler )` - * - * Adds a basic event listener that listens to an event once and only once. - * - * @param {String} event The name of the event to listen for. - * @param {Function} handler The handler that will be executed to handle the event. - * @return {Object} this - */ -can.one = function(event, handler) { - // Unbind the listener after it has been executed - var one = function() { - can.unbind.call(this, event, one); - return handler.apply(this, arguments); - }; - - // Bind the altered listener - can.bind.call(this, event, one); - return this; -}; - -// ## can.event -// Create and export the `can.event` mixin -can.event = { - // Event method aliases - /** - * @function can.event.on - * @parent can.event.static - * @signature `obj.on( event, handler )` - * - * Add a basic event listener to an object. - * - * This is an alias of [can.event.addEvent addEvent]. - * - * @signature `can.event.on.call( obj, event, handler )` - * - * This syntax can be used for objects that don't include the `can.event` mixin. - */ - on: function() { - if (arguments.length === 0 && can.Control && this instanceof can.Control) { - return can.Control.prototype.on.call(this); - } - else { - return can.addEvent.apply(this, arguments); - } - }, - - /** - * @function can.event.off - * @parent can.event.static - * @signature `obj.off( event, handler )` - * - * Removes a basic event listener from an object. - * - * This is an alias of [can.event.removeEvent removeEvent]. - * - * @signature `can.event.off.call( obj, event, handler )` - * - * This syntax can be used for objects that don't include the `can.event` mixin. - */ - off: function() { - if (arguments.length === 0 && can.Control && this instanceof can.Control) { - return can.Control.prototype.off.call(this); - } - else { - return can.removeEvent.apply(this, arguments); - } - }, - - /** - * @function can.event.bind - * @parent can.event.static - * @signature `obj.bind( event, handler )` - * - * Add a basic event listener to an object. - * - * This is an alias of [can.event.addEvent addEvent]. - * - * @signature `can.event.bind.call( obj, event, handler )` - * - * This syntax can be used for objects that don't include the `can.event` mixin. - */ - bind: can.addEvent, - /** - * @function can.event.unbind - * @parent can.event.static - * @signature `obj.unbind( event, handler )` - * - * Removes a basic event listener from an object. - * - * This is an alias of [can.event.removeEvent removeEvent]. - * - * @signature `can.event.unbind.call( obj, event, handler )` - * - * This syntax can be used for objects that don't include the `can.event` mixin. - */ - unbind: can.removeEvent, - /** - * @function can.event.delegate - * @parent can.event.static - * @signature `obj.delegate( selector, event, handler )` - * - * Provides a compatibility layer for adding delegate event listeners. - * This doesn't actually implement delegates, but rather allows - * logic that assumes a delegate to still function. - * - * Therefore, this is essentially an alias of [can.event.addEvent addEvent] with the selector ignored. - * - * @param {String} selector The **ignored** selector to use for the delegate. - * @param {String} event The name of the event to listen for. - * @param {Function} handler The handler that will be executed to handle the event. - * @return {Object} this - * - * @signature `can.event.delegate.call( obj, selector, event, handler )` - * - * This syntax can be used for objects that don't include the `can.event` mixin. - */ - delegate: function(selector, event, handler) { - return can.addEvent.call(this, event, handler); - }, - /** - * @function can.event.undelegate - * @parent can.event.static - * @signature `obj.undelegate( selector, event, handler )` - * - * Provides a compatibility layer for removing delegate event listeners. - * This doesn't actually implement delegates, but rather allows - * logic that assumes a delegate to still function. - * - * Therefore, this is essentially an alias of [can.event.removeEvent removeEvent] with the selector ignored. - * - * @param {String} selector The **ignored** selector to use for the delegate. - * @param {String} event The name of the event to listen for. - * @param {Function} handler The handler that will be executed to handle the event. - * @return {Object} this - * - * @signature `can.event.undelegate.call( obj, selector, event, handler )` - * - * This syntax can be used for objects that don't include the `can.event` mixin. - */ - undelegate: function(selector, event, handler) { - return can.removeEvent.call(this, event, handler); - }, - /** - * @function can.event.trigger - * @parent can.event.static - * @signature `obj.trigger( event, args )` - * - * Dispatches/triggers a basic event on an object. - * This is an alias of [can.event.dispatch dispatch]. - * - * @signature `can.event.trigger.call( obj, event, args )` - * - * This syntax can be used for objects that don't include the `can.event` mixin. - */ - trigger: can.dispatch, - - // Normal can/event methods - one: can.one, - addEvent: can.addEvent, - removeEvent: can.removeEvent, - listenTo: can.listenTo, - stopListening: can.stopListening, - dispatch: can.dispatch -}; - -module.exports = exports = can.event; +module.exports = can; diff --git a/event/event.md b/event/event.md deleted file mode 100644 index aa966ed9e0d..00000000000 --- a/event/event.md +++ /dev/null @@ -1,65 +0,0 @@ -@page can.event can.event -@parent canjs -@test can/event/test.html -@link ../docco/event.html docco -@group can.event.plugins plugins -@group can.event.static static -@release 2.1 - -@description - -Add event functionality into your objects. - -The `can.event` object provides a number of methods for handling events in objects. This functionality is best used by mixing the `can.event` object into an object or prototype. However, event listeners can still be used even on objects that don't include `can.event`. - -All methods provided by `can.event` assume that they are mixed into an object -- `this` should be the object dispatching the events. - -@signature `can.extend(YourClass.prototype, can.event)` - -Adds event functionality to `YourClass` objects. This can also be applied to normal objects: `can.extend(someObject, can.event)`. - -@body - -## Using as a mixin - -The easiest way to add events to your classes and objects is by mixing `can.event` into your object or prototype. - -``` -var SomeClass = can.Construct("SomeClass", { - init: function() { - this.value = 0; - }, - increment: function() { - this.value++; - this.dispatch("change", [this.value]); - } -}); -can.extend(SomeClass.prototype, can.event); -``` - -Now that `can.event` is included in the prototype, we can add/remove/dispatch events on the object instances. - -``` -var instance = new SomeClass(); -instance.on("change", function(ev, value) { - alert("The instance changed to " + value); -}); - -// This will dispatch the "change" event and show the alert -instance.increment(); -``` - -## Using without mixing in - -The same event functionality from `can.event` can be used, even if the given object doesn't include `can.event`. Every method within `can.event` supports being called with an alternate scope. - -``` -var obj = {}; - -can.event.on.call(obj, "change", function() { - alert("object change!"); -}); - -// This will dispatch the "change" event and show the alert -can.event.dispatch.call(obj, "change"); -``` diff --git a/event/event_test.js b/event/event_test.js index 4084d3561ab..6a9d330d246 100644 --- a/event/event_test.js +++ b/event/event_test.js @@ -1,264 +1 @@ -var event = require('can/event/event'); -var Control = require('can/control/control'); -require('can/control/control'); -require('steal-qunit'); - -QUnit.module('can/event'); -test('basics', 4, function () { - var obj = { - addEvent: can.addEvent, - removeEvent: can.removeEvent, - dispatch: can.dispatch - }; - var handler = function (ev, arg1, arg2) { - ok(true, 'foo called'); - equal(ev.type, 'foo'); - equal(arg1, 1, 'one'); - equal(arg2, 2, 'two'); - }; - obj.addEvent('foo', handler); - obj.dispatch({ - type: 'foo' - }, [ - 1, - 2 - ]); - obj.removeEvent('foo', handler); - obj.dispatch({ - type: 'foo', - data: [ - 1, - 2 - ] - }); -}); -test('listenTo and stopListening', 9, function () { - var parent = { - bind: can.bind, - unbind: can.unbind, - listenTo: can.listenTo, - stopListening: can.stopListening - }; - var child1 = { - bind: can.bind, - unbind: can.unbind - }; - var child2 = { - bind: can.bind, - unbind: can.unbind - }; - var change1WithId = 0; - parent.listenTo(child1, 'change', function () { - change1WithId++; - if (change1WithId === 1) { - ok(true, 'child 1 handler with id called'); - } else { - ok(false, 'child 1 handler with id should only be called once'); - } - }); - child1.bind('change', function () { - ok(true, 'child 1 handler without id called'); - }); - var foo1WidthId = 0; - parent.listenTo(child1, 'foo', function () { - foo1WidthId++; - if (foo1WidthId === 1) { - ok(true, 'child 1 foo handler with id called'); - } else { - ok(false, 'child 1 foo handler should not be called twice'); - } - }); - // child2 stuff - (function () { - var okToCall = true; - parent.listenTo(child2, 'change', function () { - ok(okToCall, 'child 2 handler with id called'); - okToCall = false; - }); - }()); - child2.bind('change', function () { - ok(true, 'child 2 handler without id called'); - }); - parent.listenTo(child2, 'foo', function () { - ok(true, 'child 2 foo handler with id called'); - }); - can.trigger(child1, 'change'); - can.trigger(child1, 'foo'); - can.trigger(child2, 'change'); - can.trigger(child2, 'foo'); - parent.stopListening(child1); - parent.stopListening(child2, 'change'); - can.trigger(child1, 'change'); - can.trigger(child1, 'foo'); - can.trigger(child2, 'change'); - can.trigger(child2, 'foo'); -}); -test('stopListening on something you\'ve never listened to ', function () { - var parent = { - bind: can.bind, - unbind: can.unbind, - listenTo: can.listenTo, - stopListening: can.stopListening - }; - var child = { - bind: can.bind, - unbind: can.unbind - }; - parent.listenTo({}, 'foo'); - parent.stopListening(child, 'change'); - ok(true, 'did not error'); -}); - -// Disable document tests for MooTools -// MooTools doesn't support dispatching events on the document -if (!window.MooTools) { - test('bind on document', function () { - var called = false, - handler = function () { - called = true; - }; - can.bind.call(document, 'click', handler); - can.trigger(can.$(document), 'click'); - ok(called, 'got click event'); - ok(true, 'did not error'); - can.unbind.call(document, 'click', handler); - }); - test('delegate on document', function () { - var called = false, - handler = function () { - called = true; - }; - can.delegate.call(document, 'body', 'click', handler); - can.trigger(can.$(document.body), 'click'); - ok(called, 'got click event'); - ok(true, 'did not error'); - can.undelegate.call(document, 'body', 'click', handler); - }); -} - -test('Delegate/undelegate should fallback to using bind/unbind (#754)', 4, function() { - var bind_fallback_fired = false, - unbind_fallback_fired = false, - handler_fired = false; - - var addEvent = can.addEvent; - can.addEvent = can.event.addEvent = function() { - bind_fallback_fired = true; - return addEvent.apply(this, arguments); - }; - var removeEvent = can.removeEvent; - can.removeEvent = can.event.removeEvent = function() { - unbind_fallback_fired = true; - return removeEvent.apply(this, arguments); - }; - - // Create event-dispatching class - var some_object = can.extend({}, can.event); - - var handler = function() { - handler_fired = true; - ok(this === some_object, "Scope is correct"); - }; - - // Delegate and fire the event - can.event.delegate.call(some_object, '', 'some_event', handler); - some_object.dispatch("some_event"); - can.event.undelegate.call(some_object, '', 'some_event', handler); - - // Fire the event - equal(bind_fallback_fired, true, "Bind fallback fired"); - equal(handler_fired, true, "Delegated handler fired"); - equal(unbind_fallback_fired, true, "Unbind fallback fired"); - - can.addEvent = can.event.addEvent = addEvent; - can.removeEvent = can.event.removeEvent = removeEvent; -}); - -test('One will listen to an event once, then unbind', function() { - var obj = {}, - count = 0, - mixin = 0; - - // Direct once call - can.one.call(obj, 'action', function() { - count++; - }); - can.dispatch.call(obj, 'action'); - can.dispatch.call(obj, 'action'); - can.dispatch.call(obj, 'action'); - equal(count, 1, 'one should only fire a handler once (direct)'); - - // Mixin call - can.extend(obj, can.event); - obj.one('mixin', function() { - mixin++; - }); - obj.dispatch('mixin'); - obj.dispatch('mixin'); - obj.dispatch('mixin'); - equal(mixin, 1, 'one should only fire a handler once (mixin)'); - -}); - -test('Test events using mixin', function() { - var obj = {}, fn; - can.extend(obj, can.event); - - // Verify bind/unbind/dispatch mixins - var bindCount = 0; - obj.bind('action', fn = function() { - ++bindCount; - }); - obj.dispatch('action'); - obj.dispatch('action'); - obj.unbind('action', fn); - obj.dispatch('action'); - equal(bindCount, 2, 'action triggered twice'); - - // Verify one mixin - bindCount = 0; - obj.one('action', fn = function() { - ++bindCount; - }); - obj.dispatch('action'); - obj.dispatch('action'); - equal(bindCount, 1, 'action triggered only once, then unbound'); - - // Verify listenTo/stopListening - var other = {}; - bindCount = 0; - can.extend(other, can.event); - obj.listenTo(other, 'action', fn = function() { - ++bindCount; - }); - other.dispatch('action'); - other.dispatch('action'); - obj.stopListening(other, 'action', fn); - other.dispatch('action'); - equal(bindCount, 2, 'action triggered twice'); -}); - -test("When mixed in, can.Control-based classes should still retain on/off functionality (#981)", function() { - var clicked = false; - var MyControl = can.Control.extend(can.extend({}, can.event, { - " click": function() { - clicked = true; - } - })); - - var div = document.createElement("div"); - var instance = new MyControl(div, {}); - - can.$(div).trigger("click"); - equal(clicked, true, "click event handler was bound successfully via init"); - - clicked = false; - instance.off(); - can.$(div).trigger("click"); - equal(clicked, false, "click event handler was unbound successfully via off()"); - - clicked = false; - instance.on(); - can.$(div).trigger("click"); - equal(clicked, true, "click event handler was bound successfully via on()"); -}); +require('can-event/can-event_test'); diff --git a/event/namespace/namespace.js b/event/namespace/namespace.js deleted file mode 100644 index bd9273205aa..00000000000 --- a/event/namespace/namespace.js +++ /dev/null @@ -1,103 +0,0 @@ -// # can/event/namespace -// -// This is a `can/event` plugin that implements the namespacing for events. -// Namespacing allows you to add listeners for a given namespace, then remove -// them later by just using the namespace. -// -// Namespacing is **disabled** for can.Map-based classes, because Maps use -// the same syntax as namespaces for binding to deep attributes. -// -// ``` -// object.bind("change.orange", function() {}); -// object.bind("click.orange", function() {}); -// -// // This unbinds all events using the "orange" namespace. -// object.unbind(".orange"); -// ``` -var can = require('can/util/can'); -require('can/event/event'); - -// ## can.event.addEvent -// -// Adds an event listener (with namespacing support included). -var addEvent = can.addEvent; -can.addEvent = can.event.addEvent = can.event.on = can.event.bind = function(event, fn) { - // Bypass namespaces for Maps - // Otherwise it conflicts with map attribute binding. - if (can.Map && this instanceof can.Map) { - return addEvent.call(this, event, fn); - } - - // Split the namespaces out - if (event && event.indexOf('.') > -1) { - var namespaces = event.split('.'); - // The event name is the first item in the string - event = namespaces[0]; - } - - // Add the listener using the original addEvent - addEvent.call(this, event, fn); - - // Inject namespaces if applicable - if (namespaces && namespaces.length > 1) { - var events = this.__bindEvents[event]; - // Assign the namespaces property, including the array of all namespaces - events[events.length-1].namespaces = namespaces.slice(1); - } - - return this; -}; - -// ## can.event.removeEvent -// -// Removes an event listener (with namespacing support included). -var removeEvent = can.removeEvent; -can.removeEvent = can.event.removeEvent = can.event.off = can.event.unbind = function(event, fn, __validate) { - // Bypass namespaces for Maps - // Otherwise it conflicts with map attribute binding. - if (can.Map && this instanceof can.Map) { - return removeEvent.call(this, event, fn); - } - - // Split the namespaces out - if (event && event.indexOf('.') > -1) { - var namespaces = event.split('.'); - // The event name is the first item in the string. - // Also, remove this item from the namespace array for future processing. - event = namespaces.splice(0,1)[0]; - } - - // Handle namespace-only (no event name). - if (!event && namespaces && namespaces.length > 0) { - var allEvents = this.__bindEvents || {}, - self = this; - - // Given each event name, attempt to remove it with all namespaces included. - can.each(allEvents, function(events, event) { - // Use **this** overridden removeEvent function. - // Using the original removeEvent would bypass namespace validation. - can.removeEvent.call(self, event + '.' + namespaces.join('.'), fn, __validate); - }); - return this; - } - // Handle normal events (with namespace validation where applicable) - else { - var isFunction = typeof fn === 'function'; - // Attempt to remove the event listener with the original removeEvent. - // Include a custom validation function for validating namespaces on events. - return removeEvent.call(this, event, fn, namespaces ? function(ev) { - if (ev.namespaces && (__validate ? __validate(ev, event, fn) : (isFunction && ev.handler === fn || !isFunction && (ev.cid === fn || !fn)))) { - // Verify that **all** namespaces specified are matched. - for (var i = 0; i < namespaces.length; i++) { - if (can.inArray(namespaces[i], ev.namespaces) === -1) { - return false; - } - } - return true; - } - return false; - } : __validate); - } -}; - -module.exports = exports = can.event; diff --git a/event/namespace/namespace.md b/event/namespace/namespace.md deleted file mode 100644 index 7fabadeb0bb..00000000000 --- a/event/namespace/namespace.md +++ /dev/null @@ -1,9 +0,0 @@ -@hide - -@page can.event.namespace -@parent can.event.plugins -@plugin can/event/namespace -@test can/event/namespace/test.html -@download http://donejs.com/can/dist/can.event.namespace.js - -**This plugin is in development and should not be included in the official documentation.** diff --git a/event/namespace/namespace_test.js b/event/namespace/namespace_test.js deleted file mode 100644 index c4f2a384a24..00000000000 --- a/event/namespace/namespace_test.js +++ /dev/null @@ -1,76 +0,0 @@ -require('can/util/util'); -require('can/event/namespace/namespace'); -require('steal-qunit'); - -QUnit.module('can/event/namespace'); - -test('Event with namespaces', function() { - var node = can.extend({ name: 'root' }, can.event), - count = 0; - fn = function(ev) { - ++count; - }; - - count = 0; - node.bind('action.namespace', fn); - node.dispatch('action'); - equal(count, 1, 'Action with namespace fired'); - - count = 0; - node.dispatch('action'); - node.unbind('action.namespace', fn); - node.dispatch('action'); - equal(count, 1, 'Action with namespace not fired after unbind'); - - count = 0; - node.bind('action.namespace', fn); - node.dispatch('action'); - node.unbind('.namespace', fn); - node.dispatch('action'); - equal(count, 1, 'Action unbound by namespace'); -}); - -test('Events with multiple namespaces', function() { - var node = can.extend({ name: 'root' }, can.event), - count = 0; - fn = function(ev) { - ++count; - }; - - count = 0; - node.bind('action.namespace', fn); - node.dispatch('action'); - node.unbind('.namespace', fn); - node.dispatch('action'); - equal(count, 1, 'Action unbound by namespace'); - - count = 0; - node.bind('action.namespace.another.other', fn); - node.dispatch('action'); - equal(count, 1, 'Action with multiple namespaces fired'); - node.unbind('.another', fn); - node.dispatch('action'); - equal(count, 1, 'Any matching namespace should remove the event'); - node.bind('action.namespace.another.other', fn); - node.unbind('action'); - node.dispatch('action'); - equal(count, 1, 'Normal event should remove listeners'); - - count = 0; - node.bind('action.namespace', fn); - node.bind('other.namespace', fn); - node.dispatch('action'); - node.dispatch('other'); - node.unbind('.namespace'); - node.dispatch('action'); - node.dispatch('action'); - node.dispatch('other'); - node.dispatch('other'); - equal(count, 2, 'Removing by namespace should remove all events assigned to it'); - - count = 0; - node.bind('action.namespace.another', fn); - node.unbind('.namespace.other'); - node.dispatch('action'); - equal(count, 1, 'All namespaces much match when unbinding, if present'); -}); diff --git a/event/namespace/test.html b/event/namespace/test.html deleted file mode 100644 index edcf91dd85a..00000000000 --- a/event/namespace/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/event/namespace - -
        diff --git a/event/propagate/propagate.js b/event/propagate/propagate.js deleted file mode 100644 index 77dd7a24e9f..00000000000 --- a/event/propagate/propagate.js +++ /dev/null @@ -1,92 +0,0 @@ -// # can/event/propagate -// -// This is a `can/event` plugin that implements the event propagation bubbling. -// The event will be propagated/bubbled on a per object basis for each object -// that implements the `propagate` property. -// -// ``` -// var SomeClass = can.Construct("SomeClass"); -// can.extend(SomeClass.prototype, can.event, { propagate: "parentNode" }); -// -// var parent = new SomeClass(); -// var child = new SomeClass(); -// child.parentNode = parent; -// -// // This will dispatch on both child **and** parent. -// child.dispatch("action"); -// ``` -var can = require('can/util/can'); -require('can/event/event'); -// ## can.event.dispatch -// -// Adds propagation of events. -// Typically this will be integrated using `can.extend` on an object. -// -// ``` -// can.extend(Class.prototype, can.event, { propagate: 'prop' }) -// ``` -var dispatch = can.dispatch; -can.dispatch = can.event.dispatch = can.event.trigger = function (event, args) { - var propagate = this.propagate || false; - - // Inject propagation into event, when applicable - if (typeof event.isPropagationStopped === 'undefined') { - // Initialize the event into an object - if (typeof event === 'string') { - event = { - type: event - }; - } - - // Add extra event properties - // This could be done with can.simpleExtend, but this avoids extra logic execution - var stop = false, - prevent = false; - - // Add propagation, if applicable - if (propagate) { - // Current target should always be the object the event is triggering on - event.currentTarget = this; - // Target is always the original object triggering the event - event.target = event.target || this; - // Descendants is a cache of the stack of objects generating this event. - // This is primarily used for the `can/event/delegate` plugin. - event.descendants = event.target === event.currentTarget ? [] : [event.target]; - // Allow the user to stop propagation - event.stopPropagation = function() { - stop = true; - }; - event.isPropagationStopped = function() { - return stop; - }; - } - - // Always add default prevention - event.preventDefault = function() { - prevent = true; - }; - event.isDefaultPrevented = function() { - return prevent; - }; - } - // If the propagated event already exists, just inject the new target - else if (propagate) { - // Set the current target when propagating - event = can.simpleExtend({}, event); - event.descendants = [event.currentTarget].concat(event.descendants); - event.currentTarget = this; - } - - // Call original dispatch - dispatch.call(this, event, args); - - // Call propagated events - if (propagate && !event.isPropagationStopped() && this[propagate]) { - // Call the propagated can.dispatch (otherwise it'll only propagate one level) - can.dispatch.call(this[propagate], event, args); - } - - return event; -}; - -module.exports = exports = can.event; diff --git a/event/propagate/propagate.md b/event/propagate/propagate.md deleted file mode 100644 index 67bb9efbbe2..00000000000 --- a/event/propagate/propagate.md +++ /dev/null @@ -1,97 +0,0 @@ -@page can.event.propagate -@parent can.event.plugins -@plugin can/event/propagate -@test can/event/propagate/test.html -@download http://donejs.com/can/dist/can.event.propagate.js - -@description - -This changes [can.event.dispatch can.event.dispatch] to add support for propagating events to parent objects. - -This is implemented similarly to how events work in the DOM: Events dispatched on an object will be dispatched on their parent object, all the way up until no more parent objects are defined. - -@signature `can.extend(YourClass.prototype, can.event, { propagate: "parent" })` - -Adds event functionality with event propagation to `YourClass` objects. This can also be applied to normal objects: `can.extend(someObject, can.event, { propagate: "parent" })`. - -The extra object, `{ propagate: "parent" }`, is used to define which object property is that object's parent. With the `propagate` property set to `"parent"`, any event dispatched on the `YourClass` instances will also be dispatched on `instance.parent` if it exists. - -## Using propagation - -In order to add propagation to an object or prototype, mix it into the object using `can.extend` along with whatever property should be considered the propagation property. - -``` -var SomeClass = can.Construct("SomeClass", { - init: function() { - this.value = 0; - }, - setParent: function(obj) { - this.parent = obj; - this.dispatch("parent", [this, obj]); - } -}); -can.extend(SomeClass.prototype, can.event, { propagate: "parent" }); -``` - -Now that propagation has been added, events will be dispatched up the tree for as long as a valid propagate property exists. - -``` -var instance = new SomeClass(); -var root = new SomeClass(); - -root.on("parent", function(ev, obj, parent) { - // obj has set root as its parent -}); - -// This triggers the "parent" event on instance -// The "parent" event then propagates and triggers on root as well! -instance.setParent(root); -``` - -## Stopping propagation - -When using the propagate plugin, `stopPropagation()` and `isPropagationStopped()` methods will be added to the event object. These methods can be used to prevent the event from propagating further up the tree. - -``` -SomeClass.prototype.setParent = function(obj) { - this.parent = obj; - var event = this.dispatch("parent", [this, obj]); - - console.log(event.isPropagationStopped()); // => true -}; - -instance.on("parent", function(ev, obj, parent) { - // Don't let this propagate past this object - ev.stopPropagation(); -}); - -root.on("parent", function(ev, obj, parent) { - // This code is never reached -}); - -// This will execute the instance listener, but not the -// root listener, because propagation has been stopped. -instance.setParent(root); -``` - -## Preventing default functionality - -When using the propagate plugin, `preventDefault()` and `isDefaultPrevented()` methods will be added to the event object. An object might implement some logic that will be executed after an event is dispatched. In the case that this logic should be optional dependent on the event handlers, default prevention can be used. - -``` -SomeClass.prototype.setParent = function(obj) { - this.parent = obj; - var event = this.dispatch("parent", [this, obj]); - - // Only execute this code if the default isn't prevented - if (!event.isDefaultPrevented()) { - this.parent.children = this.parent.children || []; - this.parent.children.push(this); - } -}; - -instance.on("parent", function(ev, obj, parent) { - // Don't let the default functionality execute - ev.preventDefault(); -}); -``` diff --git a/event/propagate/propagate_test.js b/event/propagate/propagate_test.js deleted file mode 100644 index 0b99f682159..00000000000 --- a/event/propagate/propagate_test.js +++ /dev/null @@ -1,134 +0,0 @@ -var event = require('can/event/propagate/propagate'); -require('can/test/test'); -require('steal-qunit'); - -QUnit.module('can/event/propagate'); - -test('Propagation', 9, function() { - var node1 = { name: 'root' }, - node2 = { name: 'mid', parent: node1 }, - node3 = { name: 'child', parent: node2 }; - - can.extend(node1, can.event, { propagate: 'parent' }); - can.extend(node2, can.event, { propagate: 'parent' }); - can.extend(node3, can.event, { propagate: 'parent' }); - - // Test propagation - node1.bind('action', function(ev) { - equal(ev.target.name, 'child', 'target is node3'); - equal(ev.currentTarget.name, 'root', 'currentTarget is node1'); - equal(this.name, 'root', 'delegate is node1'); - }); - node2.bind('action', function(ev) { - equal(ev.target.name, 'child', 'target is node3'); - equal(ev.currentTarget.name, 'mid', 'currentTarget is node2'); - equal(this.name, 'mid', 'delegate is node2'); - }); - node3.bind('action', function(ev) { - equal(ev.target.name, 'child', 'target is node1'); - equal(ev.currentTarget.name, 'child', 'currentTarget is node1'); - equal(this.name, 'child', 'delegate is node1'); - }); - node3.dispatch('action'); -}); - -test('Stop propagation', 6, function() { - var node1 = { name: 'root' }, - node2 = { name: 'mid', parent: node1 }, - node3 = { name: 'child', parent: node2 }; - - can.extend(node1, can.event, { propagate: 'parent' }); - can.extend(node2, can.event, { propagate: 'parent' }); - can.extend(node3, can.event, { propagate: 'parent' }); - - // Test stop propagation - node1.bind('stop', function(ev) { - // This should never fire - ok(false); - }); - node2.bind('stop', function(ev) { - equal(ev.target.name, 'child', 'target is node3'); - equal(ev.currentTarget.name, 'mid', 'currentTarget is node2'); - equal(this.name, 'mid', 'delegate is node2'); - ev.stopPropagation(); - }); - node3.bind('stop', function(ev) { - equal(ev.target.name, 'child', 'target is node1'); - equal(ev.currentTarget.name, 'child', 'currentTarget is node1'); - equal(this.name, 'child', 'delegate is node1'); - }); - node3.dispatch('stop'); -}); - -test('Prevent default', 9, function() { - var node1 = { name: 'root' }, - node2 = { name: 'mid', parent: node1 }, - node3 = { name: 'child', parent: node2 }; - - can.extend(node1, can.event, { propagate: 'parent' }); - can.extend(node2, can.event, { propagate: 'parent' }); - can.extend(node3, can.event, { propagate: 'parent' }); - - // Test stop propagation - node1.bind('stop', function(ev) { - // This should never fire - ok(false); - }); - node2.bind('stop', function(ev) { - equal(ev.target.name, 'child', 'target is node3'); - equal(ev.currentTarget.name, 'mid', 'currentTarget is node2'); - equal(this.name, 'mid', 'delegate is node2'); - ev.stopPropagation(); - equal(ev.isDefaultPrevented(), true, 'default is prevented'); - }); - node3.bind('stop', function(ev) { - equal(ev.isDefaultPrevented(), false, 'default not prevented'); - equal(ev.target.name, 'child', 'target is node1'); - equal(ev.currentTarget.name, 'child', 'currentTarget is node1'); - equal(this.name, 'child', 'delegate is node1'); - equal(ev.isDefaultPrevented(), false, 'default not prevented'); - ev.preventDefault(); - }); - node3.dispatch('stop'); -}); - -test('Events propagate even if the original target has no listeners', 3, function() { - var node1 = { name: 'root' }, - node2 = { name: 'mid', parent: node1 }, - node3 = { name: 'child', parent: node2 }; - - can.extend(node1, can.event, { propagate: 'parent' }); - can.extend(node2, can.event, { propagate: 'parent' }); - can.extend(node3, can.event, { propagate: 'parent' }); - - node1.bind('action', function(ev) { - equal(ev.target.name, 'child', 'target is node3'); - equal(ev.currentTarget.name, 'root', 'currentTarget is node1'); - equal(this.name, 'root', 'delegate is node1'); - }); - node3.dispatch('action'); -}); - -test('Descendants are available to verify delegation', 7, function() { - var node1 = { name: 'root' }, - node2 = { name: 'mid', parent: node1 }, - node3 = { name: 'child', parent: node2 }; - - can.extend(node1, can.event, { propagate: 'parent' }); - can.extend(node2, can.event, { propagate: 'parent' }); - can.extend(node3, can.event, { propagate: 'parent' }); - - node1.bind('action', function(ev) { - equal(ev.target.name, 'custom', 'target is custom target'); - equal(ev.currentTarget.name, 'root', 'currentTarget is node1'); - equal(this.name, 'root', 'delegate is node1'); - equal(ev.descendants.length, 3, 'event has 3 descendants (node2, node3, custom target)'); - equal(ev.descendants[0].name, 'mid', 'first descendant is node2'); - equal(ev.descendants[1].name, 'child', 'second descendant is node3'); - equal(ev.descendants[2].name, 'custom', 'third descendant is custom target'); - }); - node3.dispatch({ - type: 'action', - target: { name: 'custom' } - }); -}); diff --git a/event/propagate/test.html b/event/propagate/test.html deleted file mode 100644 index 52c137aa761..00000000000 --- a/event/propagate/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/event/propagate - -
        diff --git a/event/test.html b/event/test.html deleted file mode 100644 index b33a76c6dea..00000000000 --- a/event/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/event - -
        diff --git a/event/test_all.html b/event/test_all.html deleted file mode 100644 index 1eb5181e224..00000000000 --- a/event/test_all.html +++ /dev/null @@ -1,14 +0,0 @@ -can/event Test All - -
        diff --git a/list/doc/Map.md b/list/doc/Map.md deleted file mode 100644 index 173e9c9f5cc..00000000000 --- a/list/doc/Map.md +++ /dev/null @@ -1,32 +0,0 @@ -@page can.List.Map Map -@parent can.List.static - -@property {can.Map} can.List.Map - -@description Specify the Map type used to make objects added to this list observable. - -@option {can.Map} When objects are added to a can.List, those objects are converted into can.Map instances. For example: - - var list = new can.List(); - list.push({name: "Justin"}); - - var map = list.attr(0); - map.attr("name") //-> "Justin" - -By changing [can.List.Map], you can specify a different type of Map instance to create. For example: - - var User = can.Map.extend({ - fullName: function(){ - return this.attr("first")+" "+this.attr("last") - } - }); - - User.List = can.List.extend({ - Map: User - }, {}); - - var list = new User.List(); - list.push({first: "Justin", last: "Meyer"}); - - var user = list.attr(0); - user.fullName() //-> "Justin Meyer" \ No newline at end of file diff --git a/list/doc/extend.md b/list/doc/extend.md deleted file mode 100644 index e19cce2e8f2..00000000000 --- a/list/doc/extend.md +++ /dev/null @@ -1,14 +0,0 @@ -@page can.List.extend extend -@parent can.List.static - -@function extend - -@signature `can.List.extend([name,] [staticProperties,] instanceProperties)` - -Creates a new extended constructor function. Learn more at [can.Construct.extend]. - -@param {String} [name] If provided, adds the extened List constructor function to the window at the given name. - -@param {Object} [staticProperties] Properties and methods directly on the constructor function. The most common property to set is [can.List.Map]. - -@param {Object} [instanceProperties] Properties and methods on instances of this list type. \ No newline at end of file diff --git a/list/doc/filter.md b/list/doc/filter.md deleted file mode 100644 index 51d363a4970..00000000000 --- a/list/doc/filter.md +++ /dev/null @@ -1,24 +0,0 @@ -@function can.List.prototype.filter filter -@parent can.List.prototype - -@description Filter the elements of a List, returning a new List instance with just filtered items. - -@signature `list.filter(filterFunc, context)` - -@param {function(this:*,*,Number,can.List):Boolean} filterFunc(item, index, list) A function to call with each element of the list. Returning `false` will remove the index. -@param {Object} context The object to use as `this` inside the callback. - -@body - -A filter function that accepts a function, which is run on every element of the list. If the -filter callback returns true, the list returned will contain this item, false and it will not. - -Returns a new can.List instance. - - var list = new can.List([1, 2, 3]) - - // returns new can.List([1, 2]) - var filtered = list.filter( function(item, index, list) - { - return item < 3; - }); \ No newline at end of file diff --git a/list/doc/list.md b/list/doc/list.md deleted file mode 100644 index 50b47e6cf97..00000000000 --- a/list/doc/list.md +++ /dev/null @@ -1,121 +0,0 @@ -@constructor can.List -@inherits can.Map -@download can/list -@test can/list/test.html -@parent canjs -@release 2.0 - -@group can.List.prototype 0 Prototype -@group can.List.static 1 Static -@group can.List.plugins 2 plugins - -@link ../docco/list/list.html docco - -Use for observable array-like objects. - -@signature `new can.List([array])` - -Create an observable array-like object. - -@param {Array} [array] Items to seed the List with. - -@return {can.List} An instance of `can.List` with the elements from _array_. - -@signature `new can.List(deferred)` - -@param {can.Deferred} deferred A deferred that resolves to an -array. When the deferred resolves, its values will be added to the list. - -@return {can.List} An initially empty `can.List`. - - -@body - -## Use - -`can.List` is used to observe changes to an Array. `can.List` extends `[can.Map]`, so all the -ways that you're used to working with Maps also work here. - -Use [can.List::attr attr] to read and write properties of a list: - - var hobbies = new can.List(["JS","Party Rocking"]) - hobbies.attr(0) //-> "JS" - hobbies.attr("length") //-> 2 - - hobbies.attr(0,"JavaScript") - - hobbies.attr() //-> ["JavaScript","Party Rocking"] - -Just as you shouldn't set properties of an Map directly, you shouldn't change elements -of a List directly. Always use `attr` to set the elements of a List, or use [can.List::push push], -[can.List::pop pop], [can.List::shift shift], [can.List::unshift unshift], or [can.List::splice splice]. - -Here is a tour through the forms of `can.List`'s `attr` that parallels the one found under [can.Map.prototype.attr attr]: - -``` -var people = new can.List(['Alex', 'Bill']); - -// set an element: -people.attr(0, 'Adam'); -people[0] = 'Adam'; // don't do this! - -// get an element: -people.attr(0); // 'Adam' -people[0]; // 'Adam' - -// get all elements: -people.attr(); // ['Adam', 'Bill'] - -// extend the array: -people.attr(4, 'Charlie'); -people.attr(); // ['Adam', 'Bill', undefined, undefined, 'Charlie'] - -// merge the elements: -people.attr(['Alice', 'Bob', 'Eve']); -people.attr(); // ['Alice', 'Bob', 'Eve', undefined, 'Charlie'] -``` - -## Listening to changes - -As with `can.Map`s, the real power of observable arrays comes from being able to -react to changes in the member elements of the array. Lists emit five types of events: - -- the _change_ event fires on every change to a List. -- the _set_ event is fired when an element is set. -- the _add_ event is fired when an element is added to the List. -- the _remove_ event is fired when an element is removed from the List. -- the _length_ event is fired when the length of the List changes. - -This example presents a brief concrete survey of the times these events are fired: - -``` -var list = new can.List(['Alice', 'Bob', 'Eve']); - -list.bind('change', function() { console.log('An element changed.'); }); -list.bind('set', function() { console.log('An element was set.'); }); -list.bind('add', function() { console.log('An element was added.'); }); -list.bind('remove', function() { - console.log('An element was removed.'); -}); -list.bind('length', function() { - console.log('The length of the list changed.'); -}); - -list.attr(0, 'Alexis'); // 'An element changed.' - // 'An element was set.' - -list.attr(3, 'Xerxes'); // 'An element changed.' - // 'An element was added.' - // 'The length of the list was changed.' - -list.attr(['Adam', 'Bill']); // 'An element changed.' - // 'An element was set.' - // 'An element was changed.' - // 'An element was set.' - -list.pop(); // 'An element changed.' - // 'An element was removed.' - // 'The length of the list was changed.' -``` - -More information about binding to these events can be found under [can.List::attr attr]. diff --git a/list/doc/prototype.attr.md b/list/doc/prototype.attr.md deleted file mode 100644 index 07470d1fd87..00000000000 --- a/list/doc/prototype.attr.md +++ /dev/null @@ -1,243 +0,0 @@ -@page can.List.prototype.attr attr -@parent can.List.prototype - -@description Get or set elements in a List. -@function can.List.prototype.attr attr - -@signature `list.attr()` - -Gets an array of all the elements in this `can.List`. - -@return {Array} An array with all the elements in this List. - -@signature `list.attr(index)` - -Reads an element from this `can.List`. - -@param {Number} index The element to read. -@return {*} The value at _index_. - -@signature `list.attr(index, value)` - -Assigns _value_ to the index _index_ on this `can.List`, expanding the list if necessary. - -@param {Number} index The element to set. -@param {*} value The value to assign at _index_. -@return {can.List} This list, for chaining. - -@signature `list.attr(elements[, replaceCompletely])` - -Merges the members of _elements_ into this List, replacing each from the beginning in order. If _elements_ is longer than the current List, the current List will be expanded. If _elements_ is shorter than the current List, the extra existing members are not affected (unless _replaceCompletely_ is `true`). To remove elements without replacing them, use `[can.Map::removeAttr removeAttr]`. - -@param {Array} elements An array of elements to merge in. - -@param {bool} [replaceCompletely=false] whether to completely replace the elements of List - -If _replaceCompletely_ is `true` and _elements_ is shorter than the List, the existing extra members of the List will be removed. - -@return {can.List} This list, for chaining. - -@body - - -## Use - -`attr` gets or sets elements on the `can.List` it's called on. Here's a tour through how all of its forms work: - - var people = new can.List(['Alex', 'Bill']); - - // set an element: - people.attr(0, 'Adam'); - - // get an element: - people.attr(0); // 'Adam' - people[0]; // 'Adam' - - // get all elements: - people.attr(); // ['Adam', 'Bill'] - - // extend the array: - people.attr(4, 'Charlie'); - people.attr(); // ['Adam', 'Bill', undefined, undefined, 'Charlie'] - - // merge the elements: - people.attr(['Alice', 'Bob', 'Eve']); - people.attr(); // ['Alice', 'Bob', 'Eve', undefined, 'Charlie'] - -## Deep properties - -`attr` can also set and read deep properties. All you have to do is specify the property name as you normally would if you weren't using `attr`. - -``` -var people = new can.List([{name: 'Alex'}, {name: 'Bob'}]); - -// set a property: -people.attr('0.name', 'Alice'); - -// get a property: -people.attr('0.name'); // 'Alice' -people[0].attr('name'); // 'Alice' - -// get all properties: -people.attr(); // [{name: 'Alice'}, {name: 'Bob'}] -``` - -The discussion of deep properties under `[can.Map.prototype.attr]` may also be enlightening. - -## Events - -`can.List`s emit five types of events in response to changes. They are: - -- the _change_ event fires on every change to a List. -- the _set_ event is fired when an element is set. -- the _add_ event is fired when an element is added to the List. -- the _remove_ event is fired when an element is removed from the List. -- the _length_ event is fired when the length of the List changes. - -### The _change_ event - -The first event that is fired is the _change_ event. The _change_ event is useful -if you want to react to all changes on an List. - -``` -var list = new can.List([]); -list.bind('change', function(ev, index, how, newVal, oldVal) { - console.log('Something changed.'); -}); -``` - -The parameters of the event handler for the _change_ event are: - -- _ev_ The event object. -- _index_ Where the change took place. -- _how_ Whether elements were added, removed, or set. - Possible values are `'add'`, `'remove'`, or `'set'`. -- _newVal_ The elements affected after the change - _newVal_ will be a single value when an index is set, an Array when elements -were added, and `undefined` if elements were removed. -- _oldVal_ The elements affected before the change. -_newVal_ will be a single value when an index is set, an Array when elements -were removed, and `undefined` if elements were added. - -Here is a concrete tour through the _change_ event handler's arguments: - -``` -var list = new can.List(); -list.bind('change', function(ev, index, how, newVal, oldVal) { - console.log(ev + ', ' + index + ', ' + how + ', ' + newVal + ', ' + oldVal); -}); - -list.attr(['Alexis', 'Bill']); // [object Object], 0, add, ['Alexis', 'Bill'], undefined -list.attr(2, 'Eve'); // [object Object], 2, add, Eve, undefined -list.attr(0, 'Adam'); // [object Object], 0, set, Adam, Alexis -list.attr(['Alice', 'Bob']); // [object Object], 0, set, Alice, Adam - // [object Object], 1, set, Bob, Bill -list.removeAttr(1); // [object Object], 1, remove, undefined, Bob -``` - -### The _set_ event - -_set_ events are fired when an element at an index that already exists in the List is modified. Actions can cause _set_ events to fire never also cause _length_ events to fire (although some functions, such as `[can.List.prototype.splice splice]` may cause unrelated sets of events to fire after being batched). - -The parameters of the event handler for the _set_ event are: - -- _ev_ The event object. -- _newVal_ The new value of the element. -- _index_ where the set took place. - -Here is a concrete tour through the _set_ event handler's arguments: - -``` -var list = new can.List(); -list.bind('set', function(ev, newVal, index) { - console.log(newVal + ', ' + index); -}); - -list.attr(['Alexis', 'Bill']); -list.attr(2, 'Eve'); -list.attr(0, 'Adam'); // Adam, 0 -list.attr(['Alice', 'Bob']); // Alice, 0 - // Bob, 1 -list.removeAttr(1); -``` - -### The _add_ event - -_add_ events are fired when elements are added or inserted -into the List. - -The parameters of the event handler for the _add_ event are: - -- _ev_ The event object. -- _newElements_ The new elements. - If more than one element is added, _newElements_ will be an array. Otherwise, it is simply the new element itself. -- _index_ Where the add or insert took place. - -Here is a concrete tour through the _add_ event handler's arguments: - -``` -var list = new can.List(); -list.bind('add', function(ev, newElements, index) { - console.log(newElements + ', ' + index); -}); - -list.attr(['Alexis', 'Bill']); // ['Alexis', 'Bill'], 0 -list.attr(2, 'Eve'); // Eve, 2 -list.attr(0, 'Adam'); -list.attr(['Alice', 'Bob']); - -list.removeAttr(1); -``` - -### The _remove_ event - -_remove_ events are fired when elements are removed from the list. - -The parameters of the event handler for the _remove_ event are: - -- _ev_ The event object. -- _removedElements_ The removed elements. - If more than one element was removed, _removedElements_ will be an array. Otherwise, it is simply the element itself. -- _index_ Where the removal took place. - -Here is a concrete tour through the _remove_ event handler's arguments: - -``` -var list = new can.List(); -list.bind('remove', function(ev, removedElements, index) { - console.log(removedElements + ', ' + index); -}); - -list.attr(['Alexis', 'Bill']); -list.attr(2, 'Eve'); -list.attr(0, 'Adam'); -list.attr(['Alice', 'Bob']); - -list.removeAttr(1); // Bob, 1 -``` - -### The _length_ event - -_length_ events are fired whenever the list changes. - -The parameters of the event handler for the _length_ event are: - -- _ev_ The event object. -- _length_ The current length of the list. - If events were batched when the _length_ event was triggered, _length_ will have the length of the list when `stopBatch` was called. Because of this, you may receive multiple _length_ events with the same _length_ parameter. - -Here is a concrete tour through the _length_ event handler's arguments: - -``` -var list = new can.List(); -list.bind('length', function(ev, length) { - console.log(length); -}); - -list.attr(['Alexis', 'Bill']); // 2 -list.attr(2, 'Eve'); // 3 -list.attr(0, 'Adam'); -list.attr(['Alice', 'Bob']); - -list.removeAttr(1); // 2 -``` diff --git a/list/doc/prototype.each.md b/list/doc/prototype.each.md deleted file mode 100644 index b6b49ad877d..00000000000 --- a/list/doc/prototype.each.md +++ /dev/null @@ -1,36 +0,0 @@ -@page can.List.prototype.each each -@parent can.List.prototype - -@function can.List.prototype.each each -@description Call a function on each element of a List. -@signature `list.each( callback(item, index) )` - -`each` iterates through the List, calling a function -for each element. - -@param {function(*, Number)} callback the function to call for each element -The value and index of each element will be passed as the first and second -arguments, respectively, to the callback. If the callback returns false, -the loop will stop. - -@return {can.List} this List, for chaining - -@body -``` -var i = 0; -new can.List([1, 10, 100]).each(function(element, index) { - i += element; -}); - -i; // 111 - -i = 0; -new can.List([1, 10, 100]).each(function(element, index) { - i += element; - if(index >= 1) { - return false; - } -}); - -i; // 11 -``` diff --git a/list/doc/prototype.map.md b/list/doc/prototype.map.md deleted file mode 100644 index 143283b7e66..00000000000 --- a/list/doc/prototype.map.md +++ /dev/null @@ -1,47 +0,0 @@ -@function can.List.prototype.map map -@parent can.List.prototype - -@description Call a function on each element of a List and return a new List instance from the results. -@signature `list.map( callback(item, index, listReference), context )` - -@param {function(*, Number, can.List)} callback A function to call with each -element of the list. -@param {Object} context An optional object to use as `this` inside the callback. - -@return {can.List} A new can.List instance. - -@body -``` -var list = new can.List([1, 10, 100, 1000, 10000, 100000]); -var newList = list.map(function(element, index, listReference) { - var result; - - switch(index) { - case 0: { - result = false; - break; - } - case 1: { - result = undefined; - break; - } - case 2: { - result = element; - break; - } - case 3: { - result = element * 5; - break; - } - default: { - result = listReference[index] /= 2; - break; - } - } - - return result; -}); - -console.log(list); // [ 1, 10, 100, 1000, 5000, 50000] -console.log(newList); // [false, undefined, 100, 5000, 5000, 50000] -``` diff --git a/list/doc/prototype.reverse.md b/list/doc/prototype.reverse.md deleted file mode 100644 index 4096b549034..00000000000 --- a/list/doc/prototype.reverse.md +++ /dev/null @@ -1,25 +0,0 @@ -@page can.List.prototype.reverse reverse -@parent can.List.prototype - -@function can.List.prototype.reverse reverse -@description Reverse the order of a List. -@signature `list.reverse()` - -`reverse` reverses the elements of the List in place. - -@return {can.List} the List, for chaining - -@body -``` -var list = new can.List(['Alice', 'Bob', 'Eve']); -var reversedList = list.reverse(); - -reversedList.attr(); // ['Eve', 'Bob', 'Alice']; -list === reversedList; // true -``` - -`reverse` calls `replace` internally and triggers corresponding `add`, `remove`, `change` and `length` events respectively. - -## Demo - -@iframe can/list/doc/reverse.html 350 \ No newline at end of file diff --git a/list/doc/prototype.splice.md b/list/doc/prototype.splice.md deleted file mode 100644 index 8ee9855a8b9..00000000000 --- a/list/doc/prototype.splice.md +++ /dev/null @@ -1,65 +0,0 @@ -@page can.List.prototype.splice splice -@parent can.List.prototype - -@function can.List.prototype.splice splice -@description Insert and remove elements from a List. -@signature `list.splice(index[, howMany[, ...newElements]])` -@param {Number} index where to start removing or inserting elements - -@param {Number} [howMany] the number of elements to remove - If _howMany_ is not provided, `splice` will remove all elements from `index` to the end of the List. - -@param {*} newElements elements to insert into the List - -@return {Array} the elements removed by `splice` - -@body - `splice` lets you remove elements from and insert elements into a List. - - This example demonstrates how to do surgery on a list of numbers: - -``` - var list = new can.List([0, 1, 2, 3]); - - // starting at index 2, remove one element and insert 'Alice' and 'Bob': - list.splice(2, 1, 'Alice', 'Bob'); - list.attr(); // [0, 1, 'Alice', 'Bob', 3] -``` - - ## Events - - `splice` causes the List it's called on to emit _change_ events, - _add_ events, _remove_ events, and _length_ events. If there are - any elements to remove, a _change_ event, a _remove_ event, and a - _length_ event will be fired. If there are any elements to insert, a - separate _change_ event, an _add_ event, and a separate _length_ event - will be fired. - - This slightly-modified version of the above example should help - make it clear how `splice` causes events to be emitted: - -``` - var list = new can.List(['a', 'b', 'c', 'd']); - list.bind('change', function(ev, attr, how, newVals, oldVals) { - console.log('change: ' + attr + ', ' + how + ', ' + newVals + ', ' + oldVals); - }); - list.bind('add', function(ev, newVals, where) { - console.log('add: ' + newVals + ', ' + where); - }); - list.bind('remove', function(ev, oldVals, where) { - console.log('remove: ' + oldVals + ', ' + where); - }); - list.bind('length', function(ev, length) { - console.log('length: ' + length + ', ' + this.attr()); - }); - - // starting at index 2, remove one element and insert 'Alice' and 'Bob': - list.splice(2, 1, 'Alice', 'Bob'); // change: 2, 'remove', undefined, ['c'] - // remove: ['c'], 2 - // length: 5, ['a', 'b', 'Alice', 'Bob', 'd'] - // change: 2, 'add', ['Alice', 'Bob'], ['c'] - // add: ['Alice', 'Bob'], 2 - // length: 5, ['a', 'b', 'Alice', 'Bob', 'd'] -``` - - More information about binding to these events can be found under [can.List.attr attr]. diff --git a/list/doc/reverse.html b/list/doc/reverse.html deleted file mode 100644 index 59a7f0adf87..00000000000 --- a/list/doc/reverse.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - diff --git a/list/list.js b/list/list.js index eb7f5b20760..e9a9ac64093 100644 --- a/list/list.js +++ b/list/list.js @@ -1,881 +1,3 @@ -/* jshint -W079 */ -var can = require('can/util/util'); -var Map = require('can/map/map'); -var bubble = require('can/map/bubble'); -var mapHelpers = require('can/map/map_helpers'); +var can = require('../util/can'); -// Helpers for `observable` lists. -var splice = [].splice, - // test if splice works correctly - spliceRemovesProps = (function () { - // IE's splice doesn't remove properties - var obj = { - 0: "a", - length: 1 - }; - splice.call(obj, 0, 1); - return !obj[0]; - })(); - -/** - * @add can.List - */ -var list = Map.extend( - /** - * @static - */ - { - /** - * @property {can.Map} can.List.Map - * - * @description Specify the Map type used to make objects added to this list observable. - * - * @option {can.Map} When objects are added to a can.List, those objects are - * converted into can.Map instances. For example: - * - * var list = new can.List(); - * list.push({name: "Justin"}); - * - * var map = list.attr(0); - * map.attr("name") //-> "Justin" - * - * By changing [can.List.Map], you can specify a different type of Map instance to - * create. For example: - * - * var User = can.Map.extend({ - * fullName: function(){ - * return this.attr("first")+" "+this.attr("last") - * } - * }); - * - * User.List = can.List.extend({ - * Map: User - * }, {}); - * - * var list = new User.List(); - * list.push({first: "Justin", last: "Meyer"}); - * - * var user = list.attr(0); - * user.fullName() //-> "Justin Meyer" - * - * - * - */ - Map: Map - /** - * @function can.Map.extend - * - * @signature `can.List.extend([name,] [staticProperties,] instanceProperties)` - * - * Creates a new extended constructor function. Learn more at [can.Construct.extend]. - * - * @param {String} [name] If provided, adds the extened List constructor function - * to the window at the given name. - * - * @param {Object} [staticProperties] Properties and methods - * directly on the constructor function. The most common property to set is [can.List.Map]. - * - * @param {Object} [instanceProperties] Properties and methods on instances of this list type. - * - * @body - * - * ## Use - * - * - */ - }, - /** - * @prototype - */ - { - setup: function (instances, options) { - this.length = 0; - can.cid(this, ".map"); - this._setupComputedProperties(); - instances = instances || []; - var teardownMapping; - - if (can.isDeferred(instances)) { - this.replace(instances); - } else { - teardownMapping = instances.length && mapHelpers.addToMap(instances, this); - this.push.apply(this, can.makeArray(instances || [])); - } - - if (teardownMapping) { - teardownMapping(); - } - - // this change needs to be ignored - can.simpleExtend(this, options); - }, - _triggerChange: function (attr, how, newVal, oldVal) { - - Map.prototype._triggerChange.apply(this, arguments); - // `batchTrigger` direct add and remove events... - var index = +attr; - // Make sure this is not nested and not an expando - if (!~(""+attr).indexOf('.') && !isNaN(index)) { - - if (how === 'add') { - can.batch.trigger(this, how, [newVal, index]); - can.batch.trigger(this, 'length', [this.length]); - } else if (how === 'remove') { - can.batch.trigger(this, how, [oldVal, index]); - can.batch.trigger(this, 'length', [this.length]); - } else { - can.batch.trigger(this, how, [newVal, index]); - } - - } - - }, - ___get: function (attr) { - if (attr) { - if (this[attr] && this[attr].isComputed && can.isFunction(this.constructor.prototype[attr])) { - return this[attr](); - } else { - return this[attr]; - } - } else { - return this; - } - }, - __set: function (prop, value, current) { - // We want change events to notify using integers if we're - // setting an integer index. Note that % 1 !== 0; - prop = isNaN(+prop) || (prop % 1) ? prop : +prop; - - // Check to see if we're doing a .attr() on an out of - // bounds index property. - if (typeof prop === "number" && - prop > this.length - 1) { - var newArr = new Array((prop + 1) - this.length); - newArr[newArr.length-1] = value; - this.push.apply(this, newArr); - return newArr; - } - - return can.Map.prototype.__set.call(this, ""+prop, value, current); - }, - ___set: function (attr, val) { - this[attr] = val; - if (+attr >= this.length) { - this.length = (+attr + 1); - } - }, - __remove: function(prop, current) { - // if removing an expando property - if(isNaN(+prop)) { - delete this[prop]; - this._triggerChange(prop, "remove", undefined, current); - } else { - this.splice(prop, 1); - } - }, - _each: function (callback) { - var data = this.___get(); - for (var i = 0; i < data.length; i++) { - callback(data[i], i); - } - }, - // Returns the serialized form of this list. - /** - * @hide - * Returns the serialized form of this list. - */ - serialize: function () { - return mapHelpers.serialize(this, 'serialize', []); - }, - /** - * @function can.List.prototype.each each - * @description Call a function on each element of a List. - * @signature `list.each( callback(item, index) )` - * - * `each` iterates through the Map, calling a function - * for each element. - * - * @param {function(*, Number)} callback the function to call for each element - * The value and index of each element will be passed as the first and second - * arguments, respectively, to the callback. If the callback returns false, - * the loop will stop. - * - * @return {can.List} this List, for chaining - * - * @body - * ``` - * var i = 0; - * new can.Map([1, 10, 100]).each(function(element, index) { - * i += element; - * }); - * - * i; // 111 - * - * i = 0; - * new can.Map([1, 10, 100]).each(function(element, index) { - * i += element; - * if(index >= 1) { - * return false; - * } - * }); - * - * i; // 11 - * ``` - */ - // - /** - * @function can.List.prototype.splice splice - * @description Insert and remove elements from a List. - * @signature `list.splice(index[, howMany[, ...newElements]])` - * @param {Number} index where to start removing or inserting elements - * - * @param {Number} [howMany] the number of elements to remove - * If _howMany_ is not provided, `splice` will remove all elements from `index` to the end of the List. - * - * @param {*} newElements elements to insert into the List - * - * @return {Array} the elements removed by `splice` - * - * @body - * `splice` lets you remove elements from and insert elements into a List. - * - * This example demonstrates how to do surgery on a list of numbers: - * - * ``` - * var list = new can.List([0, 1, 2, 3]); - * - * // starting at index 2, remove one element and insert 'Alice' and 'Bob': - * list.splice(2, 1, 'Alice', 'Bob'); - * list.attr(); // [0, 1, 'Alice', 'Bob', 3] - * ``` - * - * ## Events - * - * `splice` causes the List it's called on to emit _change_ events, - * _add_ events, _remove_ events, and _length_ events. If there are - * any elements to remove, a _change_ event, a _remove_ event, and a - * _length_ event will be fired. If there are any elements to insert, a - * separate _change_ event, an _add_ event, and a separate _length_ event - * will be fired. - * - * This slightly-modified version of the above example should help - * make it clear how `splice` causes events to be emitted: - * - * ``` - * var list = new can.List(['a', 'b', 'c', 'd']); - * list.bind('change', function(ev, attr, how, newVals, oldVals) { - * console.log('change: ' + attr + ', ' + how + ', ' + newVals + ', ' + oldVals); - * }); - * list.bind('add', function(ev, newVals, where) { - * console.log('add: ' + newVals + ', ' + where); - * }); - * list.bind('remove', function(ev, oldVals, where) { - * console.log('remove: ' + oldVals + ', ' + where); - * }); - * list.bind('length', function(ev, length) { - * console.log('length: ' + length + ', ' + this.attr()); - * }); - * - * // starting at index 2, remove one element and insert 'Alice' and 'Bob': - * list.splice(2, 1, 'Alice', 'Bob'); // change: 2, 'remove', undefined, ['c'] - * // remove: ['c'], 2 - * // length: 5, ['a', 'b', 'Alice', 'Bob', 'd'] - * // change: 2, 'add', ['Alice', 'Bob'], ['c'] - * // add: ['Alice', 'Bob'], 2 - * // length: 5, ['a', 'b', 'Alice', 'Bob', 'd'] - * ``` - * - * More information about binding to these events can be found under [can.List.attr attr]. - */ - splice: function (index, howMany) { - var args = can.makeArray(arguments), - added =[], - i, len, listIndex, - allSame = args.length > 2; - - index = index || 0; - - // converting the arguments to the right type - for (i = 0, len = args.length-2; i < len; i++) { - listIndex = i + 2; - args[listIndex] = this.__type(args[listIndex], listIndex); - added.push(args[listIndex]); - - // Now lets check if anything will change - if(this[i+index] !== args[listIndex]) { - allSame = false; - } - } - - // if nothing has changed, then return - if(allSame && this.length <= added.length) { - return added; - } - - // default howMany if not provided - if (howMany === undefined) { - howMany = args[1] = this.length - index; - } - - var removed = splice.apply(this, args); - - // delete properties for browsers who's splice sucks (old ie) - if (!spliceRemovesProps) { - for (i = this.length; i < removed.length + this.length; i++) { - delete this[i]; - } - } - - can.batch.start(); - if (howMany > 0) { - // tears down bubbling - bubble.removeMany(this, removed); - this._triggerChange("" + index, "remove", undefined, removed); - } - if (args.length > 2) { - // make added items bubble to this list - bubble.addMany(this, added); - this._triggerChange("" + index, "add", added, removed); - } - can.batch.stop(); - return removed; - }, - _getAttrs: function(){ - return mapHelpers.serialize(this, 'attr', []); - }, - _setAttrs: function (items, remove) { - // Create a copy. - items = can.makeArray(items); - - can.batch.start(); - this._updateAttrs(items, remove); - can.batch.stop(); - }, - - _updateAttrs: function (items, remove) { - var len = Math.min(items.length, this.length); - - for (var prop = 0; prop < len; prop++) { - var curVal = this[prop], - newVal = items[prop]; - - if ( can.isMapLike(curVal) && mapHelpers.canMakeObserve(newVal)) { - curVal.attr(newVal, remove); - //changed from a coercion to an explicit - } else if (curVal !== newVal) { - this._set(prop+"", newVal); - } else { - - } - } - if (items.length > this.length) { - // Add in the remaining props. - this.push.apply(this, items.slice(this.length)); - } else if (items.length < this.length && remove) { - this.splice(items.length); - } - } - }), - - // Converts to an `array` of arguments. - getArgs = function (args) { - return args[0] && can.isArray(args[0]) ? - args[0] : - can.makeArray(args); - }; -// Create `push`, `pop`, `shift`, and `unshift` -can.each({ - /** - * @function can.List.prototype.push push - * @description Add elements to the end of a list. - * @signature `list.push(...elements)` - * - * `push` adds elements onto the end of a List. - * - * @param {*} elements the elements to add to the List - * - * @return {Number} the new length of the List - * - * @body - * `push` adds elements onto the end of a List here is an example: - * - * ``` - * var list = new can.List(['Alice']); - * - * list.push('Bob', 'Eve'); - * list.attr(); // ['Alice', 'Bob', 'Eve'] - * ``` - * - * If you have an array you want to concatenate to the end - * of the List, you can use `apply`: - * - * ``` - * var names = ['Bob', 'Eve'], - * list = new can.List(['Alice']); - * - * list.push.apply(list, names); - * list.attr(); // ['Alice', 'Bob', 'Eve'] - * ``` - * - * ## Events - * - * `push` causes _change_, _add_, and _length_ events to be fired. - * - * ## See also - * - * `push` has a counterpart in [can.List::pop pop], or you may be - * looking for [can.List::unshift unshift] and its counterpart [can.List::shift shift]. - */ - push: "length", - /** - * @function can.List.prototype.unshift unshift - * @description Add elements to the beginning of a List. - * @signature `list.unshift(...elements)` - * - * `unshift` adds elements onto the beginning of a List. - * - * @param {*} elements the elements to add to the List - * - * @return {Number} the new length of the List - * - * @body - * `unshift` adds elements to the front of the list in bulk in the order specified: - * - * ``` - * var list = new can.List(['Alice']); - * - * list.unshift('Bob', 'Eve'); - * list.attr(); // ['Bob', 'Eve', 'Alice'] - * ``` - * - * If you have an array you want to concatenate to the beginning - * of the List, you can use `apply`: - * - * ``` - * var names = ['Bob', 'Eve'], - * list = new can.List(['Alice']); - * - * list.unshift.apply(list, names); - * list.attr(); // ['Bob', 'Eve', 'Alice'] - * ``` - * - * ## Events - * - * `unshift` causes _change_, _add_, and _length_ events to be fired. - * - * ## See also - * - * `unshift` has a counterpart in [can.List::shift shift], or you may be - * looking for [can.List::push push] and its counterpart [can.List::pop pop]. - */ - unshift: 0 - }, - // Adds a method - // `name` - The method name. - // `where` - Where items in the `array` should be added. - function (where, name) { - var orig = [][name]; - list.prototype[name] = function () { - // Get the items being added. - var args = [], - // Where we are going to add items. - len = where ? this.length : 0, - i = arguments.length, - res, val; - - // Go through and convert anything to a `map` that needs to be converted. - while (i--) { - val = arguments[i]; - args[i] = bubble.set(this, i, this.__type(val, i) ); - } - - // Call the original method. - res = orig.apply(this, args); - - if (!this.comparator || args.length) { - - this._triggerChange("" + len, "add", args, undefined); - } - - return res; - }; - }); - -can.each({ - /** - * @function can.List.prototype.pop pop - * @description Remove an element from the end of a List. - * @signature `list.pop()` - * - * `pop` removes an element from the end of a List. - * - * @return {*} the element just popped off the List, or `undefined` if the List was empty - * - * @body - * `pop` is the opposite action from `[can.List.push push]`: - * - * ``` - * var list = new can.List(['Alice', 'Bob', 'Eve']); - * list.attr(); // ['Alice', 'Bob', 'Eve'] - * - * list.pop(); // 'Eve' - * list.pop(); // 'Bob' - * list.pop(); // 'Alice' - * list.pop(); // undefined - * ``` - * - * ## Events - * - * `pop` causes _change_, _remove_, and _length_ events to be fired if the List is not empty - * when it is called. - * - * ## See also - * - * `pop` has its counterpart in [can.List::push push], or you may be - * looking for [can.List::unshift unshift] and its counterpart [can.List::shift shift]. - */ - pop: "length", - /** - * @function can.List.prototype.shift shift - * @description Remove en element from the front of a list. - * @signature `list.shift()` - * - * `shift` removes an element from the beginning of a List. - * - * @return {*} the element just shifted off the List, or `undefined` if the List is empty - * - * @body - * `shift` is the opposite action from `[can.List::unshift unshift]`: - * - * ``` - * var list = new can.List(['Alice']); - * - * list.unshift('Bob', 'Eve'); - * list.attr(); // ['Bob', 'Eve', 'Alice'] - * - * list.shift(); // 'Bob' - * list.shift(); // 'Eve' - * list.shift(); // 'Alice' - * list.shift(); // undefined - * ``` - * - * ## Events - * - * `pop` causes _change_, _remove_, and _length_ events to be fired if the List is not empty - * when it is called. - * - * ## See also - * - * `shift` has a counterpart in [can.List::unshift unshift], or you may be - * looking for [can.List::push push] and its counterpart [can.List::pop pop]. - */ - shift: 0 - }, - // Creates a `remove` type method - function (where, name) { - list.prototype[name] = function () { - if (!this.length) { - // For shift and pop, we just return undefined without - // triggering events. - return undefined; - } - - var args = getArgs(arguments), - len = where && this.length ? this.length - 1 : 0; - - var res = [][name].apply(this, args); - - // Create a change where the args are - // `len` - Where these items were removed. - // `remove` - Items removed. - // `undefined` - The new values (there are none). - // `res` - The old, removed values (should these be unbound). - this._triggerChange("" + len, "remove", undefined, [res]); - - if (res && res.unbind) { - bubble.remove(this, res); - } - - return res; - }; - }); - -can.extend(list.prototype, { - /** - * @function can.List.prototype.indexOf indexOf - * @description Look for an item in a List. - * @signature `list.indexOf(item)` - * - * `indexOf` finds the position of a given item in the List. - * - * @param {*} item the item to find - * - * @return {Number} the position of the item in the List, or -1 if the item is not found. - * - * @body - * ``` - * var list = new can.List(['Alice', 'Bob', 'Eve']); - * list.indexOf('Alice'); // 0 - * list.indexOf('Charlie'); // -1 - * ``` - * - * It is trivial to make a `contains`-type function using `indexOf`: - * - * ``` - * function(list, item) { - * return list.indexOf(item) >= 0; - * } - * ``` - */ - indexOf: function (item, fromIndex) { - can.__observe(this, "length"); - return can.inArray(item, this, fromIndex); - }, - - /** - * @function can.List.prototype.join join - * @description Join a List's elements into a string. - * @signature `list.join(separator)` - * - * `join` turns a List into a string by inserting _separator_ between the string representations - * of all the elements of the List. - * - * @param {String} separator the string to seperate elements with - * - * @return {String} the joined string - * - * @body - * ``` - * var list = new can.List(['Alice', 'Bob', 'Eve']); - * list.join(', '); // 'Alice, Bob, Eve' - * - * var beatles = new can.List(['John', 'Paul', 'Ringo', 'George']); - * beatles.join('&'); // 'John&Paul&Ringo&George' - * ``` - */ - join: function () { - can.__observe(this, "length"); - return [].join.apply(this, arguments); - }, - - /** - * @function can.List.prototype.reverse reverse - * @description Reverse the order of a List. - * @signature `list.reverse()` - * - * `reverse` reverses the elements of the List in place. - * - * @return {can.List} the List, for chaining - * - * @body - * ``` - * var list = new can.List(['Alice', 'Bob', 'Eve']); - * var reversedList = list.reverse(); - * - * reversedList.attr(); // ['Eve', 'Bob', 'Alice']; - * list === reversedList; // true - * ``` - */ - reverse: function() { - var list = [].reverse.call(can.makeArray(this)); - return this.replace(list); - }, - - /** - * @function can.List.prototype.slice slice - * @description Make a copy of a part of a List. - * @signature `list.slice([start[, end]])` - * - * `slice` creates a copy of a portion of the List. - * - * @param {Number} [start=0] the index to start copying from - * - * @param {Number} [end] the first index not to include in the copy - * If _end_ is not supplied, `slice` will copy until the end of the list. - * - * @return {can.List} a new `can.List` with the extracted elements - * - * @body - * ``` - * var list = new can.List(['Alice', 'Bob', 'Charlie', 'Daniel', 'Eve']); - * var newList = list.slice(1, 4); - * newList.attr(); // ['Bob', 'Charlie', 'Daniel'] - * ``` - * - * `slice` is the simplest way to copy a List: - * - * ``` - * var list = new can.List(['Alice', 'Bob', 'Eve']); - * var copy = list.slice(); - * - * copy.attr(); // ['Alice', 'Bob', 'Eve'] - * list === copy; // false - * ``` - */ - slice: function () { - // tells computes to listen on length for changes. - can.__observe(this, "length"); - var temp = Array.prototype.slice.apply(this, arguments); - return new this.constructor(temp); - }, - - /** - * @function can.List.prototype.concat concat - * @description Merge many collections together into a List. - * @signature `list.concat(...args)` - * @param {Array|can.List|*} args Any number of arrays, Lists, or values to add in - * For each parameter given, if it is an Array or a List, each of its elements will be added to - * the end of the concatenated List. Otherwise, the parameter itself will be added. - * - * @body - * `concat` makes a new List with the elements of the List followed by the elements of the parameters. - * - * ``` - * var list = new can.List(); - * var newList = list.concat( - * 'Alice', - * ['Bob', 'Charlie']), - * new can.List(['Daniel', 'Eve']), - * {f: 'Francis'} - * ); - * newList.attr(); // ['Alice', 'Bob', 'Charlie', 'Daniel', 'Eve', {f: 'Francis'}] - * ``` - */ - concat: function () { - var args = []; - can.each(can.makeArray(arguments), function (arg, i) { - args[i] = arg instanceof can.List ? arg.serialize() : arg; - }); - return new this.constructor(Array.prototype.concat.apply(this.serialize(), args)); - }, - - /** - * @function can.List.prototype.forEach forEach - * @description Call a function for each element of a List. - * @signature `list.forEach(callback[, thisArg])` - * @param {function(element, index, list)} callback a function to call with each element of the List - * The three parameters that _callback_ gets passed are _element_, the element at _index_, _index_ the - * current element of the list, and _list_ the List the elements are coming from. - * @param {Object} [thisArg] the object to use as `this` inside the callback - * - * @body - * `forEach` calls a callback for each element in the List. - * - * ``` - * var list = new can.List([1, 2, 3]); - * list.forEach(function(element, index, list) { - * list.attr(index, element * element); - * }); - * list.attr(); // [1, 4, 9] - * ``` - */ - forEach: function (cb, thisarg) { - return can.each(this, cb, thisarg || this); - }, - - /** - * @function can.List.prototype.replace replace - * @description Replace all the elements of a List. - * @signature `list.replace(collection)` - * @param {Array|can.List|can.Deferred} collection the collection of new elements to use - * If a [can.Deferred] is passed, it must resolve to an `Array` or `can.List`. - * The elements of the list are not actually removed until the Deferred resolves. - * - * @body - * `replace` replaces all the elements of this List with new ones. - * - * `replace` is especially useful when `can.List`s are live-bound into `[can.Control]`s, - * and you intend to populate them with the results of a `[can.Model]` call: - * - * ``` - * can.Control({ - * init: function() { - * this.list = new Todo.List(); - * // live-bind the list into the DOM - * this.element.html(can.view('list.stache', this.list)); - * // when this AJAX call returns, the live-bound DOM will be updated - * this.list.replace(Todo.findAll()); - * } - * }); - * ``` - * - * Learn more about [can.Model.List making Lists of models]. - * - * ## Events - * - * A major difference between `replace` and `attr(newElements, true)` is that `replace` always emits - * an _add_ event and a _remove_ event, whereas `attr` will cause _set_ events along with an _add_ or _remove_ - * event if needed. Corresponding _change_ and _length_ events will be fired as well. - * - * The differences in the events fired by `attr` and `replace` are demonstrated concretely by this example: - * ``` - * var attrList = new can.List(['Alexis', 'Bill']); - * attrList.bind('change', function(ev, index, how, newVals, oldVals) { - * console.log(index + ', ' + how + ', ' + newVals + ', ' + oldVals); - * }); - * - * var replaceList = new can.List(['Alexis', 'Bill']); - * replaceList.bind('change', function(ev, index, how, newVals, oldVals) { - * console.log(index + ', ' + how + ', ' + newVals + ', ' + oldVals); - * }); - * - * attrList.attr(['Adam', 'Ben'], true); // 0, set, Adam, Alexis - * // 1, set, Ben, Bill - * replaceList.replace(['Adam', 'Ben']); // 0, remove, undefined, ['Alexis', 'Bill'] - * // 0, add, ['Adam', 'Ben'], ['Alexis', 'Bill'] - * - * attrList.attr(['Amber'], true); // 0, set, Amber, Adam - * // 1, remove, undefined, Ben - * replaceList.replace(['Amber']); // 0, remove, undefined, ['Adam', 'Ben'] - * // 0, add, Amber, ['Adam', 'Ben'] - * - * attrList.attr(['Alice', 'Bob', 'Eve'], true); // 0, set, Alice, Amber - * // 1, add, ['Bob', 'Eve'], undefined - * replaceList.replace(['Alice', 'Bob', 'Eve']); // 0, remove, undefined, Amber - * // 0, add, ['Alice', 'Bob', 'Eve'], Amber - * ``` - */ - replace: function (newList) { - if (can.isDeferred(newList)) { - if(this._promise) { - this._promise.__isCurrentPromise = false; - } - var promise = this._promise = newList; - promise.__isCurrentPromise = true; - var self = this; - newList.then(function(newList){ - if(promise.__isCurrentPromise) { - self.replace(newList); - } - }); - } else { - this.splice.apply(this, [0, this.length].concat(can.makeArray(newList || []))); - } - - return this; - }, - filter: function (callback, thisArg) { - var filteredList = new this.constructor(), - self = this, - filtered; - this.each(function(item, index, list){ - filtered = callback.call( thisArg | self, item, index, self); - if(filtered){ - filteredList.push(item); - } - }); - return filteredList; - }, - map: function (callback, thisArg) { - var filteredList = new can.List(), - self = this; - this.each(function(item, index, list){ - var mapped = callback.call( thisArg | self, item, index, self); - filteredList.push(mapped); - - }); - return filteredList; - } -}); -can.List = Map.List = list; -module.exports = exports = can.List; +module.exports = can.List = require('can-list'); diff --git a/list/list_test.js b/list/list_test.js index fdcaba45284..459196e5010 100644 --- a/list/list_test.js +++ b/list/list_test.js @@ -1,370 +1 @@ -require('can/list/list'); -require('can/compute/compute'); -require('steal-qunit'); - -QUnit.module('can/list'); - -test('list attr changes length', function () { - var l = new can.List([ - 0, - 1, - 2 - ]); - l.attr(3, 3); - equal(l.length, 4); -}); -test('removeAttr on list', function() { - var l = new can.List([0, 1, 2]); - l.removeAttr(1); - equal(l.attr('length'), 2); - deepEqual(l.attr(), [0, 2]); -}); -test('list splice', function () { - var l = new can.List([ - 0, - 1, - 2, - 3 - ]), - first = true; - l.bind('change', function (ev, attr, how, newVals, oldVals) { - equal(attr, '1'); - if (first) { - equal(how, 'remove', 'removing items'); - equal(newVals, undefined, 'no new Vals'); - } else { - deepEqual(newVals, [ - 'a', - 'b' - ], 'got the right newVals'); - equal(how, 'add', 'adding items'); - } - first = false; - }); - l.splice(1, 2, 'a', 'b'); - deepEqual(l.serialize(), [ - 0, - 'a', - 'b', - 3 - ], 'serialized'); -}); -test('list pop', function () { - var l = new can.List([ - 0, - 1, - 2, - 3 - ]); - l.bind('change', function (ev, attr, how, newVals, oldVals) { - equal(attr, '3'); - equal(how, 'remove'); - equal(newVals, undefined); - deepEqual(oldVals, [3]); - }); - l.pop(); - deepEqual(l.serialize(), [ - 0, - 1, - 2 - ]); -}); -test('remove nested property in item of array map', function () { - var state = new can.List([{ - nested: true - }]); - state.bind('change', function (ev, attr, how, newVal, old) { - equal(attr, '0.nested'); - equal(how, 'remove'); - deepEqual(old, true); - }); - state.removeAttr('0.nested'); - equal(undefined, state.attr('0.nested')); -}); -test('pop unbinds', function () { - var l = new can.List([{ - foo: 'bar' - }]); - var o = l.attr(0), - count = 0; - l.bind('change', function (ev, attr, how, newVal, oldVal) { - count++; - if (count === 1) { - equal(attr, '0.foo', 'count is set'); - } else if (count === 2) { - equal(how, 'remove'); - equal(attr, '0'); - } else { - ok(false, 'called too many times'); - } - }); - equal(o.attr('foo'), 'bar'); - o.attr('foo', 'car'); - l.pop(); - o.attr('foo', 'bad'); -}); -test('splice unbinds', function () { - var l = new can.List([{ - foo: 'bar' - }]); - var o = l.attr(0), - count = 0; - l.bind('change', function (ev, attr, how, newVal, oldVal) { - count++; - if (count === 1) { - equal(attr, '0.foo', 'count is set'); - } else if (count === 2) { - equal(how, 'remove'); - equal(attr, '0'); - } else { - ok(false, 'called too many times'); - } - }); - equal(o.attr('foo'), 'bar'); - o.attr('foo', 'car'); - l.splice(0, 1); - o.attr('foo', 'bad'); -}); -test('always gets right attr even after moving array items', function () { - var l = new can.List([{ - foo: 'bar' - }]); - var o = l.attr(0); - l.unshift('A new Value'); - l.bind('change', function (ev, attr, how) { - equal(attr, '1.foo'); - }); - o.attr('foo', 'led you'); -}); -test('Array accessor methods', 11, function () { - var l = new can.List([ - 'a', - 'b', - 'c' - ]), - sliced = l.slice(2), - joined = l.join(' | '), - concatenated = l.concat([ - 2, - 1 - ], new can.List([0])); - ok(sliced instanceof can.List, 'Slice is an Observable list'); - equal(sliced.length, 1, 'Sliced off two elements'); - equal(sliced[0], 'c', 'Single element as expected'); - equal(joined, 'a | b | c', 'Joined list properly'); - ok(concatenated instanceof can.List, 'Concatenated is an Observable list'); - deepEqual(concatenated.serialize(), [ - 'a', - 'b', - 'c', - 2, - 1, - 0 - ], 'List concatenated properly'); - l.forEach(function (letter, index) { - ok(true, 'Iteration'); - if (index === 0) { - equal(letter, 'a', 'First letter right'); - } - if (index === 2) { - equal(letter, 'c', 'Last letter right'); - } - }); -}); -test('splice removes items in IE (#562)', function () { - var l = new can.List(['a']); - l.splice(0, 1); - ok(!l.attr(0), 'all props are removed'); -}); - -test('list sets up computed attributes (#790)', function() { - var List = can.List.extend({ - i: can.compute(0), - a: 0 - }); - - var l = new List([1]); - equal(l.attr('i'), 0); - - var Map = can.Map.extend({ - f: can.compute(0) - }); - - var m = new Map(); - m.attr('f'); -}); - -test('reverse triggers add/remove events (#851)', function() { - expect(6); - var l = new can.List([1,2,3]); - - l.bind('change', function() { ok(true, 'change should be called'); }); - l.bind('set', function() { ok(false, 'set should not be called'); }); - l.bind('add', function() { ok(true, 'add called'); }); - l.bind('remove', function() { ok(true, 'remove called'); }); - l.bind('length', function() { ok(true, 'length should be called'); }); - - l.reverse(); -}); - -test('filter', function(){ - var l = new can.List([{id: 1, name: "John"}, {id: 2, name: "Mary"}]); - - var filtered = l.filter(function(item){ - return item.name === "Mary"; - }); - - notEqual(filtered._cid, l._cid, "not same object"); - equal(filtered.length, 1, "one item"); - equal(filtered[0].name, "Mary", "filter works"); -}); - - -test('removing expandos on lists', function(){ - var list = new can.List(["a","b"]); - - list.removeAttr("foo"); - - equal(list.length, 2); -}); - -test('No Add Events if List Splice adds the same items that it is removing. (#1277, #1399)', function() { - var list = new can.List(["a","b"]); - - list.bind('add', function() { - ok(false, 'Add callback should not be called.'); - }); - - list.bind('remove', function() { - ok(false, 'Remove callback should not be called.'); - }); - - var result = list.splice(0, 2, "a", "b"); - - deepEqual(result, ["a", "b"]); -}); - -test("add event always returns an array as the value (#998)", function() { - var list = new can.List([]), - msg; - list.bind("add", function(ev, newElements, index) { - deepEqual(newElements, [4], msg); - }); - msg = "works on push"; - list.push(4); - list.pop(); - msg = "works on attr()"; - list.attr(0, 4); - list.pop(); - msg = "works on replace()"; - list.replace([4]); -}); - -test("Setting with .attr() out of bounds of length triggers add event with leading undefineds", function() { - var list = new can.List([1]); - list.bind("add", function(ev, newElements, index) { - deepEqual(newElements, [undefined, undefined, 4], - "Leading undefineds are included"); - equal(index, 1, "Index takes into account the leading undefineds from a .attr()"); - }); - list.attr(3, 4); -}); - -test("No events should fire if removals happened on empty arrays", function() { - var list = new can.List([]), - msg; - list.bind("remove", function(ev, removed, index) { - ok(false, msg); - }); - msg = "works on pop"; - list.pop(); - msg = "works on shift"; - list.shift(); - ok(true, "No events were fired."); -}); - -test('setting an index out of bounds does not create an array', function() { - expect(1); - var l = new can.List(); - - l.attr('1', 'foo'); - equal(l.attr('1'), 'foo'); -}); - -test('splice with similar but less items works (#1606)', function() { - var list = new can.List([ 'aa', 'bb', 'cc']); - - list.splice(0, list.length, 'aa', 'cc', 'dd'); - deepEqual(list.attr(), ['aa', 'cc', 'dd']); - - list.splice(0, list.length, 'aa', 'cc'); - deepEqual(list.attr(), ['aa', 'cc']); -}); - -test('filter returns same list type (#1744)', function() { - var ParentList = can.List.extend(); - var ChildList = ParentList.extend(); - - var children = new ChildList([1,2,3]); - - ok(children.filter(function() {}) instanceof ChildList); -}); - -test('reverse returns the same list instance (#1744)', function() { - var ParentList = can.List.extend(); - var ChildList = ParentList.extend(); - - var children = new ChildList([1,2,3]); - ok(children.reverse() === children); -}); - - -test("slice and join are observable by a compute (#1884)", function(){ - expect(2); - - var list = new can.List([1,2,3]); - - var sliced = can.compute(function(){ - return list.slice(0,1); - }); - var joined = can.compute(function(){ - return list.join(","); - }); - - sliced.bind("change", function(ev, newVal){ - deepEqual(newVal.attr(), [2], "got a new can.List"); - }); - joined.bind("change", function(ev, newVal){ - equal(newVal, "2,3", "joined is observable"); - }); - - list.shift(); - - -}); - - -test("list is always updated with the last promise passed to replace (#2136)", function(){ - - var list = new can.List(); - - stop(); - - list.replace( new can.Deferred( function( def ) { - setTimeout( function(){ - def.resolve([ "A" ]); - - setTimeout(function(){ - equal(list.attr(0), "B", "list set to last promise's value"); - start(); - },10); - - }, 20 ); - })); - - list.replace( new can.Deferred( function( def ) { - setTimeout( function(){ - def.resolve([ "B" ]); - }, 10 ); - })); -}); +require('can-list/can-list_test'); diff --git a/list/promise/doc/always.md b/list/promise/doc/always.md deleted file mode 100644 index 08750bc226b..00000000000 --- a/list/promise/doc/always.md +++ /dev/null @@ -1,29 +0,0 @@ -@function can.List.prototype.always -@parent can.List.plugins.promise - -@signature `list.always( alwaysCallback )` - -Add handlers to be called when the list is either resolved or -rejected. This works very similar -to [jQuery's always](http://api.jquery.com/deferred.always/). - -@param {function(*|can.List)} alwaysCallback(reasonOrList) - -A function that is called when the list's promise is resolved -or rejected. It will be called with the list if the promise is resolved, -or the [can.List::reason reason] if the promise is rejected. - -@return {Promise} The list's promise. - -@body - -## Use - - var data = new can.Deferred(); - var list = new can.List(data); - - list.always(function(l){ - l === list //-> true - }); - - data.resolved(["a","b"]); diff --git a/list/promise/doc/done.md b/list/promise/doc/done.md deleted file mode 100644 index 9f529395027..00000000000 --- a/list/promise/doc/done.md +++ /dev/null @@ -1,29 +0,0 @@ -@function can.List.prototype.done -@parent can.List.plugins.promise - -@signature `list.done( doneCallback )` - -Add handlers to be called when the list is resolved. This works very similar -to [jQuery's done](http://api.jquery.com/deferred.done/). - -@param {function(can.List)} doneCallback(list) - -A function that is called when the list's promise is resolved. -It will be called with the list instance. - -@return {Promise} The list's promise. - -@body - - -## Use - - var data = new can.Deferred(); - var list = new can.List(data); - - list.done(function(l){ - l === list //-> true - }); - - data.resolved(["a","b"]); - diff --git a/list/promise/doc/fail.md b/list/promise/doc/fail.md deleted file mode 100644 index aedb95d5de4..00000000000 --- a/list/promise/doc/fail.md +++ /dev/null @@ -1,28 +0,0 @@ -@function can.List.prototype.fail -@parent can.List.plugins.promise - -@signature `list.fail( failCallback )` - -Add handlers to be called when the list is rejected. This works very similar -to [jQuery's fail](http://api.jquery.com/deferred.fail/). - -@param {function(*)} failCallback(reason) - -A function that is called when the list's promise is rejected. -It will be called with the [can.List::reason reason] provided to `reject`. - -@return {Promise} The list's promise. - -@body - - -## Use - - var data = new can.Deferred(); - var list = new can.List(data); - - list.fail(function(reason){ - reason //-> "borked" - }); - - data.reject("borked"); \ No newline at end of file diff --git a/list/promise/doc/isPending.md b/list/promise/doc/isPending.md deleted file mode 100644 index 849095a6e56..00000000000 --- a/list/promise/doc/isPending.md +++ /dev/null @@ -1,18 +0,0 @@ -@function can.List.prototype.isPending -@parent can.List.plugins.promise - -@signature `list.isPending()` - -Returns if the [can.List::state state] of the list is pending. - -@return {Boolean} `true` if the list is pending. `false` if otherwise. - -@body - -## Use - - var data = new can.Deferred(); - var list = new can.List(data); - - list.isPending() //-> true - \ No newline at end of file diff --git a/list/promise/doc/isRejected.md b/list/promise/doc/isRejected.md deleted file mode 100644 index 098487a1895..00000000000 --- a/list/promise/doc/isRejected.md +++ /dev/null @@ -1,21 +0,0 @@ -@function can.List.prototype.isRejected -@parent can.List.plugins.promise - -@signature `list.isRejected()` - -Returns if the [can.List::state state] of the list is rejected. - -@return {Boolean} `true` if the list is rejected. `false` if otherwise. - -@body - -## Use - - var data = new can.Deferred(); - var list = new can.List(data); - - list.fail(function(){ - list.isRejected() //-> true - }) - - data.reject("epic fail"); diff --git a/list/promise/doc/isResolved.md b/list/promise/doc/isResolved.md deleted file mode 100644 index fa977859061..00000000000 --- a/list/promise/doc/isResolved.md +++ /dev/null @@ -1,21 +0,0 @@ -@function can.List.prototype.isResolved -@parent can.List.plugins.promise - -@signature `list.isResolved()` - -Returns if the [can.List::state state] of the list is resolved. - -@return {Boolean} `true` if the list is resolved. `false` if otherwise. - -@body - -## Use - - var data = new can.Deferred(); - var list = new can.List(data); - - list.done(function(){ - list.isResolved() //-> true - }) - - data.resolve(["a","b","c"]); \ No newline at end of file diff --git a/list/promise/doc/promise.md b/list/promise/doc/promise.md deleted file mode 100644 index a4c97a5b156..00000000000 --- a/list/promise/doc/promise.md +++ /dev/null @@ -1,23 +0,0 @@ -@function can.List.plugins.promise promise -@parent can.List.plugins -@release 2.1 - -Adds observable promise methods to [can.List]. - -## Use - -The `can/list/promise` plugin adds observable promise methods -to [can.List]. These methods let you know when the list data has been -resolved, rejected, or is still pending. - -The following demo illustrates how [can.List::isPending isPending], -[can.List::isResolved isResolved] and [can.List::isRejected isRejected] -are used to show the state of the application loading a list. Notice -how: - - - `Loading` is shown as the list is loading. - - `Error` is shown when an error on the server happens. - - `No items` is shown when there are no items returned by the server. - -@demo can/list/promise/promise.html - diff --git a/list/promise/doc/reason.md b/list/promise/doc/reason.md deleted file mode 100644 index 0a80d7206e3..00000000000 --- a/list/promise/doc/reason.md +++ /dev/null @@ -1,19 +0,0 @@ -@property {*} can.List.prototype.reason -@parent can.List.plugins.promise - -@option {*} The reason the list's deferred was rejected. Read it via -[can.Map::attr attr]. - -@body - -## Use - - var def = new can.Deferred(); - var list = new can.List( def ); - - list.fail(function(){ - list.attr("reason") //-> {message: "epic fail"} - }); - - def.reject({message: "epic fail"}); - diff --git a/list/promise/doc/state.md b/list/promise/doc/state.md deleted file mode 100644 index 28167907cd2..00000000000 --- a/list/promise/doc/state.md +++ /dev/null @@ -1,20 +0,0 @@ -@property {String} can.List.prototype.state -@parent can.List.plugins.promise - -@option {String} The state of the list's promise. Read it via -[can.Map::attr attr]. It will be one of the following: - - - undefined - the list does not have a deferred - - "pending" - the list's promise is pending - - "resolved" - the list's promise was resolved - - "rejected" - the list's promise was rejected - -@body - -## Use - - var def = new can.Deferred(); - var list = new can.List( def ); - - list.attr('state') //-> "pending" - diff --git a/list/promise/doc/then.md b/list/promise/doc/then.md deleted file mode 100644 index 7ceb03c9ca0..00000000000 --- a/list/promise/doc/then.md +++ /dev/null @@ -1,45 +0,0 @@ -@function can.List.prototype.then -@parent can.List.plugins.promise - -@signature `list.then( doneFilter, [failFilter] )` - -Add handlers to be called when the list -is resolved or rejected. This works very similar -to [jQuery's done](http://api.jquery.com/deferred.then/). - -@param {function(can.List)} doneFilter(list) - -A function that is called when the list's promise is resolved. -It will be called with the list instance. If -the function returns a value it will be used to resolve -the promise returned by `.then`. - -@param {function(*)} [failFilter(reason)] - -A function that is called when the list's promise is rejected. -It will be called with the reason. If -the function returns a value it will be used to reject -the promise returned by `.then`. - -@return {Promise} A new promise that will be resolved and rejected -based upon what `doneFilter` and `fileFilter` return. - -@body - - -## Use - - var data = new can.Deferred(); - var list = new can.List(data); - - var sumDef = list.then(function(l){ - var sum = 0; - l.each(function(num){ sum += num; }); - return sum; - }); - - data.resolved([1,2, 3]); - - sumDef.then(function(value){ - value //-> 6 - }); diff --git a/list/promise/promise.html b/list/promise/promise.html deleted file mode 100644 index 3ba636de710..00000000000 --- a/list/promise/promise.html +++ /dev/null @@ -1,104 +0,0 @@ - - - -
        -
        - - -
        - - diff --git a/list/promise/promise.js b/list/promise/promise.js deleted file mode 100644 index 4a2c8e91d2c..00000000000 --- a/list/promise/promise.js +++ /dev/null @@ -1,75 +0,0 @@ -var can = require('can/util/util'); -require('can/list/list'); - -var oldReplace = can.List.prototype.replace; - -can.List.prototype.replace = function (data) { - // First call the old replace so its - // deferred callbacks will be called first - var result = oldReplace.apply(this, arguments); - - // If there is a deferred: - if (can.isDeferred(data)) { - if(this._deferred) { - this._deferred.__cancelState = true; - } - - // Set up its state. Must call this way - // because we are working on an array. - can.batch.start(); - this.attr("state", data.state()); - this.removeAttr("reason"); - can.batch.stop(); - - var self = this; - // update its state when it changes - var deferred = this._deferred = new can.Deferred(); - deferred.__cancelState = false; - - data.then(function(){ - if(!deferred.__cancelState) { - self.attr("state", data.state()); - // The deferred methods will always return this object - deferred.resolve(self); - } - },function(reason){ - if(!deferred.__cancelState) { - can.batch.start(); - self.attr("state", data.state()); - self.attr("reason", reason); - can.batch.stop(); - deferred.reject(reason); - } - }); - } - return result; -}; - -can.each({ - isResolved: "resolved", - isPending: "pending", - isRejected: "rejected" -}, function (value, method) { - can.List.prototype[method] = function () { - return this.attr("state") === value; - }; -}); - -can.each([ - "then", - "done", - "fail", - "always", - "promise" -], function (name) { - can.List.prototype[name] = function () { - // it's possible a list is created manually and returned as the result - // of .then. It should not break. - if(!this._deferred) { - this._deferred = new can.Deferred(); - this._deferred.resolve(this); - } - - return this._deferred[name].apply(this._deferred, arguments); - }; -}); diff --git a/list/promise/promise_test.js b/list/promise/promise_test.js deleted file mode 100644 index 63f661a9748..00000000000 --- a/list/promise/promise_test.js +++ /dev/null @@ -1,182 +0,0 @@ -require('can/list/promise/promise'); -require('can/compute/compute'); -require('steal-qunit'); - -QUnit.module("can/list/promise"); - -test("list.isResolved", function () { - - var def = new can.Deferred(); - - var l = new can.List(def); - - ok(!l.isResolved(), "deferred-list is not resolved"); - - stop(); - - l.done(function () { - - ok(l.isResolved(), "it's resolved!"); - deepEqual(l.attr(), ["one", 2], "has data"); - - start(); - }); - - def.resolve(["one", 2]); - -}); - -test("list.isResolved in a compute", function () { - - var def = new can.Deferred(); - - var l = new can.List(def); - - var c = can.compute(function () { - return l.isResolved(); - }); - - ok(!c(), "not resolved"); - - var callbackCount = 0; - - c.bind("change", function (ev, newVal, oldVal) { - callbackCount++; - - if (callbackCount === 1) { - ok(newVal, "resolved"); - deepEqual(l.attr(), [1, 2]); - } else if (callbackCount === 2) { - ok(!newVal, "not resolved"); - } else if (callbackCount === 3) { - ok(newVal, "resolved"); - deepEqual(l.attr(), ["a", "b"]); - start(); - } - }); - - stop(); - - def.resolve([1, 2]); - - setTimeout(function () { - - var def2 = new can.Deferred(); - l.replace(def2); - - setTimeout(function () { - def2.resolve(["a", "b"]); - }, 60); - - }, 60); - -}); - - -test("then and done are called with the list instance", function(){ - stop(); - - var def = new can.Deferred(); - - var l = new can.List(def); - - l.then(function(list){ - equal(list , l, "then is called back with the list argument"); - }); - l.done(function(list){ - equal(list, l, "done is called back with the list argument"); - }); - l.always(function(){ - start(); - }); - - def.resolve([1, 2]); -}); - -test("rejecting adds a reason attr", function(){ - stop(); - var def = new can.Deferred(); - - var l = new can.List(def); - - l.fail(function(reason){ - equal(reason, "failed!", "got fail reason"); - }); - - l.bind("reason", function(ev, newVal){ - equal(newVal, "failed!", "event updated"); - start(); - }); - - def.reject("failed!"); - -}); - -test("A list is treated like a promise",function(){ - stop(); - // Make sure this works with normal promises - var def1 = new can.Deferred(); - var def2 = new can.Deferred(), - def2promise = def2.promise(); - var def3 = def1.then(function(){ - return def2promise; - }); - - def3.then(function(value){ - var returningPromisesWorks = value === "def2"; - ok(returningPromisesWorks, "returning a promise works"); - - def1 = new can.Deferred(); - def2 = new can.Deferred(); - - var list = new can.List(def2); - - def3 = def1.then(function(){ - return list; - }); - - def3.then(function(list){ - equal(list.length, 2, "there are 2 items in the list, the outer deferred waited on the list's deferred"); - start(); - }); - setTimeout(function(){ - def1.resolve(); - setTimeout(function(){ - def2.resolve(["a","b"]); - },10); - },10); - }); - - setTimeout(function(){ - def1.resolve(); - setTimeout(function(){ - def2.resolve("def2"); - },10); - },10); - -}); - -test("list is always updated with the last promise passed to replace (#2136)", function(){ - - var list = new can.List(); - - stop(); - - list.replace( new can.Deferred( function( def ) { - setTimeout( function(){ - def.resolve([ "A" ]); - - setTimeout(function(){ - equal(list.attr(0), "B", "list set to last promise's value"); - start(); - },10); - - }, 20 ); - })); - - list.replace( new can.Deferred( function( def ) { - setTimeout( function(){ - def.resolve([ "B" ]); - }, 10 ); - })); -}); diff --git a/list/promise/test.html b/list/promise/test.html deleted file mode 100644 index 6d3a876cc78..00000000000 --- a/list/promise/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/list/promise - -
        diff --git a/list/sort/20k.txt b/list/sort/20k.txt deleted file mode 100644 index 337a0be397b..00000000000 --- a/list/sort/20k.txt +++ /dev/null @@ -1,20000 +0,0 @@ -723686234 -178360134 -271540711 -15045283 -228419153 --723686234 -1280893 -720389146 -166547983 -21870028 -222185102 -321454477 -166587197 -565054904 -841266942 -654153814 -788966187 --788966187 -349449764 --166547983 -472796084 -242054408 -164879988 -179451063 --565054904 -864988235 -139501713 -967047756 -218862594 -550785917 --242054408 -584250538 --550785917 -675499921 -771607094 -385219258 --271540711 -36878410 -99849292 -327149194 -733325758 -554106360 -555206048 --178360134 -859871967 --675499921 -733801020 --472796084 -459079401 -418040982 -733447841 --139501713 -6838320 -363345735 -513847066 -913486466 -414698887 --321454477 -348343018 -991631691 -947419247 -750018425 -124413888 -724267331 -424294052 -69002364 --991631691 -58122584 --327149194 -967565251 -135653292 -527046655 -109325328 -948465704 -901772669 -798344956 --109325328 -716970569 -507145997 --222185102 -313656334 --948465704 -642484537 --859871967 -335078445 -127953713 --15045283 -555077843 -175842908 -383481386 -524859048 -655876872 -950765395 -181472406 -82444643 --901772669 -37822791 --554106360 -316036020 -529134627 -438247622 -59417002 --524859048 -801856717 --335078445 -263788847 --418040982 -410073324 -644265806 --947419247 -2533783 -131742335 -756072874 -43643435 -669305381 -898137772 -649028220 -269347073 -833888299 -217889343 --733325758 -748168061 -200354927 --269347073 -529021781 -416766223 -759727465 -982740870 -691420087 -57530222 --733447841 -444936216 -717262100 -105981238 --459079401 -75165813 -967634810 -379381347 -379400581 --135653292 -636134836 --164879988 -689129225 --444936216 -768023514 -575230088 -842829679 -972206980 -123830376 --175842908 -964596095 -390534986 --759727465 -522665531 -99157226 -417672291 --218862594 -365240108 -631082492 -807878729 -209532757 --385219258 -421501400 --69002364 -19077224 -79970977 --209532757 -832568745 -247807514 -411918365 --689129225 -170543174 -151260027 -268345981 -285037409 -651237638 -731243229 -710947806 -968939310 -675595637 --6838320 -553060320 -30440731 -264035602 -818924105 --349449764 -315839527 -54408206 -325819170 -107222019 -318470052 -900017421 -688477785 -572328148 -122591425 -903680370 --414698887 -732381085 -251333945 -410956644 -496353881 --898137772 -358148568 -989211270 -178901261 -409209778 --900017421 -944642090 -739472028 --247807514 -61578667 -815235370 -264926407 --217889343 -711666718 -416453494 -37209042 -439404610 --417672291 -521376090 -176268088 --801856717 -328703186 -325888132 -675520481 --841266942 -252818733 --348343018 -829535651 -803058813 -589991014 -964059258 --379400581 -248832888 --57530222 -731208062 --318470052 -688751710 -315086522 -982021435 --379381347 -428384676 -219358570 -650434596 -144385726 -904072314 --748168061 -635260336 -822014196 --2533783 -474610734 --655876872 -440647631 --99849292 -539664562 -903110907 -65389788 -566178982 --166587197 -529461782 --37822791 -536736577 -522510291 -966707533 -145923632 -60920525 -768563119 -769308385 -719875889 --416453494 -617185640 -23636034 --365240108 -132512240 -76947267 -717280312 --416766223 -387607700 -846707348 -945543850 --966707533 -211029037 -952762863 -849941548 -927822650 -817677104 -747455683 --328703186 -350857822 -921049530 --30440731 -957862420 --719875889 -291792604 --842829679 -755891486 -357208434 --716970569 -344004842 --37209042 -277754466 --82444643 -569182599 -918757893 -709426522 -334901863 --832568745 -781498810 --285037409 -357841732 --635260336 -598961085 --572328148 -567407527 -719328339 -31853964 -2321348 --733801020 -278252838 -735272429 --833888299 -166074707 --575230088 -554423151 --357208434 -692008377 --228419153 -134878419 --132512240 -908394242 -471987031 --982740870 -171361361 --755891486 -448179250 -669908044 --747455683 -983922699 -608692561 --264926407 -792465530 -755895829 --692008377 -301493426 --964059258 -249164393 -126053976 -164265448 -303761387 --99157226 -615277620 -631406209 -804961784 -598630026 -453141599 -373001026 --717280312 -990300648 -670512601 --249164393 -591602548 -376421819 --731208062 -871508512 --521376090 -150468500 -343771396 --669908044 -635838120 -162915948 -22325057 -738650576 -687354669 -982685000 --2321348 -216679761 -1581586 -859856504 --750018425 -282307518 -659339449 --438247622 -789878053 -449639857 -564576629 --219358570 -652904488 --720389146 -933496290 --334901863 -591819106 --529134627 -214475345 -286581129 -912723483 -829347925 -608968768 --644265806 -527486678 -565750930 -639128856 -137115057 -234759212 --608968768 -394027565 -996749391 -364039659 --768023514 -208362332 -529255717 -185340322 -519438450 --711666718 -153725002 --687354669 -620193245 -71608311 --670512601 -170429256 -739460696 -712808903 -408925106 -954067673 -104009037 -201078796 -307619941 -775068182 -916437197 -835233286 -763656858 -688324823 --952762863 -91600998 -394672743 -713548463 -592472538 --916437197 -89144314 --252818733 -32546873 -687678135 -698222737 -940172693 -131714852 -545109447 -965294435 -701876518 --566178982 -713204757 -366393971 -748112724 --584250538 -476088645 --439404610 -582796015 -545990714 --957862420 -192224350 --781498810 -897824859 -749409421 --343771396 -905420005 -651065546 -946522002 --871508512 -730048956 --264035602 -721507646 --912723483 -892878974 -994227723 -235326670 -594899756 --144385726 -821394159 -185144034 -547229617 -646929276 -500932568 -890209324 -992124270 -768315645 --554423151 -818941399 -726625660 -411737626 -60668912 -503535852 --383481386 -238845162 -817213233 --803058813 -554791198 -317437228 -884638231 --620193245 -804010208 -286053802 -547006945 -39960045 --954067673 -568518965 -96348981 --771607094 -40604997 --539664562 -656260231 --654153814 -734370603 -836052945 --818924105 -345420074 -279047743 --749409421 -108837494 -480517851 -616952740 --688324823 -351724592 -480925109 -723552391 -644019901 -672303221 -484972331 -802966550 -18373600 -222832374 -485002496 -413495959 -737166339 -108854602 --897824859 -783689980 --286053802 -510656157 -781639237 -631771615 -258009346 -34989873 -464299074 -316415067 -31926979 -415302058 -94429054 -4022872 -697702246 -714004615 -35688698 --325819170 -483755547 -250892578 -565845700 -912635651 -170373546 -110324270 -44022647 -413671665 --44022647 -235581875 -114055113 -307557498 -62385070 --21870028 -621244814 --731243229 -391615197 --972206980 -764305585 -933919288 --547229617 -33909160 -176531993 -392304932 -441911682 --192224350 -676839600 --594899756 -189496496 -993193420 --697702246 -387073973 --387607700 -994638315 -160302257 --903680370 -801561013 -965721859 --200354927 -285002499 -653134854 -401378311 -161115422 --315086522 -177357005 -466445052 -189376439 --410956644 -918944897 --592472538 -418756644 --964596095 -897169088 --918944897 -861374961 -673295059 -164461405 --897169088 -220760349 -164964146 -612351640 -78616057 -416570193 --553060320 -168876014 --968939310 -852245212 -215846575 -213756540 --635838120 -36344121 -190225085 -355708104 -150334889 --440647631 -524049921 -21950386 -536523708 -643378570 --849941548 -723416020 -82621129 -240450474 -382561376 -41073951 -88333738 -258903308 -771724861 -978784849 -737057088 --781639237 -82366301 -74856789 -441594662 -547664977 -170679797 -642327230 --522665531 -410108338 -901076502 -537575483 -51871838 -494985428 -295066400 --483755547 -664893208 -974411325 -720106348 -423927332 --723552391 -503619358 -499827522 --189496496 -449679153 -624101199 -489117508 --278252838 -766301319 -576019098 -619413599 -506057741 -235447765 -142236800 -430152817 --40604997 -169830837 -408051647 -238282408 -636845203 -164453536 -383383185 -512605672 -91776001 -438841110 -131540539 -641970529 -480359622 --591602548 -950493491 -646503620 -112432821 -146053872 -250073938 -403462772 -667138475 -284151399 --669305381 -280419646 -554285804 -894144300 -518721855 -360864347 -597327185 -882935221 -815844052 --32546873 -917092658 -723986697 -447221159 -392163002 -202418289 --474610734 -21161121 -166681067 --990300648 -301611715 --280419646 -7028108 --608692561 -289840582 -93442077 -349881774 -568375023 -999066939 -862786850 --713548463 -263890330 -717070521 -11495196 -751253353 -820095197 -745220569 --71608311 -395051938 -534651094 -237859724 -993180006 --732381085 -478486552 --967047756 -932388834 -103713691 -932791925 -740576159 -610195804 --999066939 -476670029 -640006903 -990916201 --390534986 -20237179 --301493426 -74175083 --464299074 -621542383 -711843056 -979897048 -610084109 -770707812 -602906091 --768315645 -248847280 -841499648 -154783893 -37111135 -163597468 -143929495 --143929495 -966379338 --701876518 -430471973 --250892578 -33458429 -407346509 --944642090 -152827121 -661409625 -273293472 -421513215 --285002499 -619362552 --612351640 -982018773 -674352652 --237859724 -573253316 -23180457 --864988235 -485592032 -724515223 -834947189 -334622250 -717562393 -361671783 -954425961 -886077344 --565845700 -476309441 -710399027 -355173825 -85467612 -1867072 --131714852 -350386455 --31853964 -22603366 -562146001 -321012891 -91460361 -283795211 -398436251 -566559755 -24954939 -101658935 -893814018 --529255717 -154366581 -352348949 --76947267 -383457442 -857357057 -386908544 -992342372 --822014196 -613957230 -876856329 -512424621 -421057083 -280701115 -968560716 -248617160 --82621129 -951710019 -127084245 -882048309 --982018773 -189480211 -851325658 -892184700 -667881826 --664893208 -822331999 -720821779 --22325057 -860887805 --719328339 -255059890 --841499648 -979549752 --411737626 -694858005 -75980259 -328822747 -23549247 -664883945 -17213292 -680322410 -897280933 --145923632 -182354505 --480925109 -490851312 --164461405 -435080938 -794901200 -303265013 -598278544 -503911991 --408925106 -436260420 -275119030 --882048309 -784365554 -682456947 -568795494 -783899606 --974411325 -146132603 --775068182 -953446597 --674352652 -41812698 --892878974 -798890512 -749449774 --103713691 -611631586 -310485008 -996086440 -726510708 -887775885 --817677104 -407880245 -241451655 --189376439 -659377382 --756072874 -880005402 -164796818 -943487007 --23636034 -245244535 -924315404 -452553920 -267373785 -471420367 --169830837 -721096675 --503911991 -388281274 -663260457 --101658935 -429810011 --248847280 -69348387 -738621177 -638834117 -81101376 -566124097 --857357057 -807831981 --355708104 -112369183 -2865547 -701853342 -246345192 -797032974 -795713649 -281705474 --880005402 -694351075 -534236749 --476670029 -536360389 -81874860 -283345493 --258903308 -606180687 -217807399 -854431374 -668398792 -291330406 --726625660 -162624241 -608522264 -756822639 -288591604 --512424621 -720333546 --646503620 -440966587 -239843764 -680062376 -759763999 -341459300 --279047743 -529749705 --480517851 -873525872 -21452312 -453667048 -338973838 -155446998 -530459864 -789989481 --566124097 -617993866 -998315959 -697562537 -204830580 --398436251 -446788803 -268689862 -25688386 -478673681 -260006137 -993904632 -229912927 -210846238 --513847066 -157767388 --680322410 -996273760 --424294052 -915970970 -580828066 -665323789 -30406396 -556989525 -22118413 -637999609 --945543850 -339024520 -337170486 -919016076 --213756540 -591327757 -258471835 -380405677 -764674586 --919016076 -680613344 -748740339 -28392206 -620214495 -356451233 -76815889 --568375023 -616821203 --631082492 -235541732 -93963570 -297381628 --965294435 -278090535 -147531646 -84611256 -548249334 -912816657 -105161470 -90568819 -759112602 -537515869 -419139991 --965721859 -927991669 -95616504 --651237638 -309052757 -164859303 -893802698 --35688698 -508104764 --739472028 -631683484 -926627595 -121170027 -982614175 --724267331 -912100620 --691420087 -42928616 -874302043 -123882245 -28625475 --795713649 -24054951 -205976165 -641028370 --918757893 -998779176 -476421337 -381066351 -633595275 -496018461 -315582635 --476088645 -306914024 -584811911 -680865625 -512814040 -306353087 -540304559 -210673223 -955434882 -39787774 -743509623 -87665414 --734370603 -927941134 -166311764 -25274081 -846744331 -671021120 --350386455 -463881843 --667881826 -896578013 -456833874 --631406209 -64094509 -361066198 -973643583 -426689525 -306634720 -791551369 --392163002 -715940748 -700907666 -761947901 -382668421 --792465530 -472844931 -151584684 --932791925 -613299303 --932388834 -271949077 -236735019 -84716498 -256974777 -541903944 -515491453 -149127249 --688477785 -327908673 --36878410 -250515362 -401102611 -367816464 -362521559 -243551832 -114197853 --2865547 -173386260 -115354158 --112432821 -243402904 --351724592 -556358894 -803467150 -97502384 -194952696 --764305585 -197201303 --263788847 -463085690 -661462487 -411957326 -311765645 -184025346 -245945101 -691132153 -525726365 -92157479 --250073938 -822147227 --494985428 -608188088 --569182599 -870039869 --303761387 -358779716 --798344956 -478656649 -569238943 --341459300 -759888598 -76139 -521580105 -306721640 -652332202 --131742335 -289273261 -93872006 --512605672 -171773375 -677502734 -527363090 --620214495 -127127670 -71956281 -340582700 -91350583 --694351075 -399293060 -280950246 -140129279 -321248532 -582363347 --251333945 -729554076 -824581164 --712808903 -749635185 -29816648 -159348957 --236735019 -609938758 -990142411 -161640203 -814140242 --255059890 -234507819 -274177243 --268689862 -804499493 -772331430 -477795027 -131859382 -648356182 --820095197 -886370980 -386961956 --334622250 -410427911 -283751995 --387073973 -978714136 --608522264 -364404219 -879452083 -433688733 --239843764 -378098491 --176268088 -517099681 -565072896 --21950386 -618614245 -618849791 -682536740 -440106542 -419667862 -629561981 --407880245 -918144936 -544846110 -103967220 -962395798 -134842042 -371306150 -374259920 --394027565 -104305896 -860312612 --21452312 -735964969 --530459864 -357113661 --215846575 -648108182 -368795085 --170679797 -96365763 -194871780 -713980844 --766301319 -461345885 --485002496 -900081367 -460238545 -4996266 -54453141 --411957326 -490596547 -252650045 -902273116 --91460361 -36410927 --886370980 -115524341 -235790602 -166287389 -236950252 -605087817 -858820316 -258197032 -667763663 -335691078 --371306150 -731204961 -712061903 --642484537 -464757150 -404904432 -338771895 --110324270 -32111944 --280950246 -613037879 -733758594 -427451628 -844710925 -275776406 -14916611 -690428947 --794901200 -279562319 --211029037 -924623695 -357694694 -475110018 --665323789 -615892028 --105981238 -979276508 -234345612 -216579850 -411860074 --309052757 -820374521 -391133660 --409209778 -658221285 -814577553 -320231730 -142389833 -390192894 --91350583 -613931233 -767606140 -728352289 -832384377 -247659421 -710285111 -815717185 --927822650 -564818587 --547006945 -622304376 -814859404 -698170275 -396690586 -852871053 -516355669 --456833874 -907260278 -849958690 --675520481 -477633073 -690868098 -938231665 --979549752 -946994418 -698224066 -226498864 --271949077 -156241985 --30406396 -906110238 -78327398 --256974777 -767998481 --946994418 -862543368 -164448730 -24904589 -347901749 --95616504 -336473212 -769268522 -821807397 -454774905 -341285871 --366393971 -436824334 -49993949 -300747462 --545990714 -794510252 -772678215 --862786850 -897477485 --761947901 -21426301 -257860570 -478869343 -429610733 --720106348 -909355496 -234472230 -376384973 -765791305 --978784849 -397982419 --998779176 -394954637 --618614245 -668976076 -791744098 --24954939 -371205260 --567407527 -585729567 -928254239 -883193326 -862692817 --659377382 -984273631 -152337315 -167106574 -890326928 -123487333 --477633073 -470779909 -143186997 -865205936 -771363401 --137115057 -448614597 -619993006 -672193311 --373001026 -932911386 -431097318 -950475655 --541903944 -430314823 -841550278 --123487333 -92815220 -272262527 -488182243 -304691636 -851992018 -519661528 -233324380 -468179559 --554285804 -787446077 --751253353 -828840529 --667763663 -97035066 -859753775 --650434596 -729097700 -565875311 --983922699 -640445530 -757248191 --860312612 -960661329 -243485607 -169705115 -947084057 --96365763 -470166703 -968437957 --610195804 -101030223 --258471835 -601711521 -324683151 --419667862 -40912142 -899897759 -831239163 --633595275 -249330631 -710090291 -772224850 -118496244 -644764039 -327889172 -40051175 -110500545 -62674866 -180907910 -137584619 --672193311 -191599136 -46596432 -964013880 -889318179 --156241985 -735785092 -479109681 -117343594 -617758899 -913795223 -249840975 --170373546 -851742248 -102182489 -861414428 -264679946 --408051647 -759837415 --76139 -666380090 -86065329 -286808991 --105161470 -133556879 -784174518 -289132489 --350857822 -87117502 -702838326 -976081542 -250320880 -590001856 -362179642 -244120470 -451581686 -63252184 -613443989 --728352289 -788645573 -482760266 -365683294 -695727034 -475733126 -693036175 -605306938 -476579065 --36344121 -822030803 -400540414 -11087334 -243368448 --440966587 -432401291 --162915948 -422254949 -728005499 -825522414 -569404337 -24402677 -390084293 -171560116 -924907950 -960560048 --680062376 -948546357 -289685199 --427451628 -267914582 -793652417 -739654882 -195834303 --115524341 -185846521 -960637087 -824758013 -84926200 -183839165 -778144504 -865939725 -206566732 -941851850 -821948931 -902970846 -705190459 --28625475 -654787787 --19077224 -810888184 -956475996 -259560925 -661893863 -897325003 -442713579 -722439042 -798909976 --371205260 -965227874 -829596895 -734554376 -320644246 -646290204 --801561013 -701028506 --617758899 -810873258 -266026986 -975219068 -760469247 --383383185 -525983446 -610319215 -579445397 -565753657 -431929771 -684276557 --807831981 -46456976 -631666589 -643699148 --982685000 -872540300 -785799635 -637029394 -67353037 --92157479 -872347301 --20237179 -673540569 --1867072 -167918143 -566733265 -72196666 --249840975 -771675866 --316036020 -923016675 -583472199 -86842630 -496194525 -208363408 --883193326 -229025781 --76815889 -817038297 --140129279 -754832199 -365152707 --882935221 -345181703 -73513113 -946846321 --241451655 -816207456 --788645573 -656353191 -708822592 -843776170 -51737932 -500612828 -66749725 -303954643 -563824626 -677817014 -445712125 -976473830 -151806860 --591819106 -359295205 -884405809 -868255854 -287800502 --89144314 -549617091 -964577241 -302549281 -409345264 -205395456 -901090772 --737057088 -404819175 -593204535 -835379850 --358779716 -335505450 --862692817 -762355030 -723776362 -347636093 -668355163 -465028034 -546988071 -593751193 -5752126 -424378502 --962395798 -802742277 -436121518 -221094138 -642029138 -756323139 -153749024 --289273261 -283055863 -789680952 -608074822 -526525198 -723051487 --902970846 -807183633 -892688123 --184025346 -585968129 -767051541 -37757790 -513194670 -844758828 --516355669 -351494259 -698484772 --873525872 -327140032 --879452083 -4919424 -490188689 -139576255 --984273631 -692908449 --733758594 -331458721 -653576369 --955434882 -389086517 -826290014 -776725606 -28081615 -156672631 -244175552 --784365554 -406657881 -548843456 -296890192 -361016849 -451128553 -667886894 --996749391 -110719179 --246345192 -96320177 -83869390 -975926001 --822030803 -680290385 -311113306 -997317223 --216679761 -884057121 -795084438 -259876253 -545946996 --429810011 -695618598 -635541788 --951710019 -766465423 -647326105 --589991014 -680535608 -270513976 -469485713 -94133241 -660358394 -812792253 -420097027 -401648194 -236225593 --401378311 -168643318 -721471160 --979897048 -721634834 --992124270 -344077931 --431929771 -271958536 -371926222 -243227456 --221094138 -232349977 --5752126 -934090947 -24377168 --503619358 -291423253 -462898111 -471225706 --518721855 -538614418 -77473367 -33108383 -152746720 --416570193 -613749518 -617064204 -528218045 --139576255 -248781984 -123658025 -791526906 --151260027 -982368663 -684498417 -320073064 -310490944 -870616067 -852442878 --756822639 -368283628 -135112858 --243551832 -645027894 --653576369 -215721282 --307619941 -463306326 -84435715 -132636943 -496295804 -645590704 --36410927 -122267551 -814557867 -423020925 -601623552 -342051632 -695021533 -828904951 --797032974 -268012222 --460238545 -353119170 -820171393 -361807149 -384784584 -742652134 --913486466 -871432212 -494696688 -928100087 -272905960 -220507504 --721471160 -193947475 --335691078 -752932448 -773701774 --355173825 -696157232 --555206048 -783380773 -20286691 -577029119 -130152816 -366329035 -136740850 -453320245 -36921316 -537135420 -829981567 -866603456 -916657711 --84926200 -419697939 --435080938 -925921318 -458876312 -439659969 --613749518 -648629027 -586778977 -311347507 -601968519 --335505450 -975765058 --107222019 -939678960 -772139650 --185144034 -209623751 --283345493 -532971012 --621542383 -576116309 -289239272 -220104847 --173386260 -374401384 --442713579 -667865599 -412026581 --755895829 -491921741 -32196013 --84435715 -930883053 --870039869 -942549604 -842690958 -434395083 --449639857 -904308295 -920372981 -813685775 --814577553 -699117106 -886685091 -324163186 --925921318 -78482838 -487219788 --447221159 -383747088 -17916780 -351775059 -233608870 --818941399 -234738611 -807909518 -253498123 -914020781 -443577641 -175691865 -391843149 -351573923 -264300185 --821394159 -284033479 --713980844 -264853580 -138504259 -626970600 -52915405 --593751193 -142723030 -195622996 --548843456 -560560870 -743439869 -537922325 -726354186 -212884100 --990142411 -878776044 -867762003 --436121518 -965419480 -348654567 -106097703 -226443092 -600562085 --166311764 -872970853 --390192894 -269691701 --133556879 -642858142 --482760266 -680287898 -518122994 -971303950 --197201303 -494463745 -843190627 --631666589 -832351816 -658435299 -937328589 --406657881 -728447821 --754832199 -35197044 --461345885 -769595773 -168642342 -821871726 --404904432 -855901955 -125800049 -199700625 -88098229 --813685775 -982012030 -593545137 -727034235 -240649848 -89615905 -65359416 --154366581 -180821919 -851426082 -793809278 -506173707 -521300161 -40802049 -358821988 -244152282 -346826965 -581415764 -6856791 -809580465 -37953581 --841550278 -814870192 --28392206 -118796500 -817002588 --598630026 -259839591 -593426765 --31926979 -756313246 -585397315 -159149499 -988126350 --549617091 -272488282 -266503187 -294348547 -377649328 -639037060 --4022872 -523378357 -275022744 --585729567 -258908239 -775498803 -960043601 -568075358 -487008319 --510656157 -835616306 -588209061 --975765058 -765263658 -140574112 -189325518 --996273760 -240594093 -586986257 --135112858 -757538303 -353208467 -948584710 -293739371 --814140242 -280244784 -758988025 -958290394 -773191540 --953446597 -714790964 -839768297 -963146710 -270390490 --560560870 -119643208 --35197044 -147785012 -702392510 -379009655 -174101307 --771675866 -605475572 -983223036 --565072896 -393474134 --673540569 -329803529 -393694455 -378278561 -95297754 --378278561 -295181595 --646929276 -782613851 --828840529 -307796013 -434231309 --37111135 -886169250 -413477767 -190018453 --131540539 -515765161 -77261949 --114197853 -892407901 --872970853 -468246772 --72196666 -945010326 -87733157 --802742277 -957900729 -502069237 -387524215 -52845145 -241397141 -738274451 --37757790 -325831427 -268589927 -979274413 -609074998 --598961085 -646044510 -145087874 -61430112 -189501196 --993193420 -964915716 -732700019 -658798903 -362621246 -934744081 -895754952 -905071546 -654471924 --938231665 -903146933 -448638461 -525432064 -26973911 --329803529 -595993529 -982106048 -431837035 -281476147 -396635829 --835233286 -909229437 --168642342 -607483899 -559638534 -487102061 -442781247 -958260549 -262994726 -410764808 -834307066 -837405011 -105222294 -555123261 -456422289 -830370755 --430471973 -928024511 --58122584 -941749963 --988126350 -610613263 --320231730 -42980307 -106542815 --946846321 -473845672 -691762328 -599880563 --264853580 -36919747 --795084438 -979770989 -798423516 -914971250 -112141723 --998315959 -537691676 --281705474 -383899875 -52014788 -703806817 --401102611 -474560436 -990745951 --282307518 -252543016 -939096150 -718749922 --419139991 -9322005 --377649328 -709442380 -762472671 --529461782 -37012803 -560046929 -603358968 -555489881 -546186649 -77540392 -434445412 -40363991 -32909767 -107135197 -499691787 --730048956 -694147170 -478034586 -819736625 --245244535 -508640882 -878163672 --351573923 -641780579 --103967220 -446236099 -794005553 -229016249 -556165526 -324173345 -88389897 -802013272 -975852134 -387496574 --691762328 -315349145 --595993529 -543019558 --383899875 -415047828 --913795223 -568626203 -639074577 --560046929 -33169842 -856062681 -86654027 --748740339 -761432855 -623225525 -329071768 -238914796 -502266235 -171434214 -689179762 -856904153 --272905960 -798787059 -204126661 -721812703 -676882810 -667881263 --693036175 -549801095 -169365438 --672303221 -136172268 --170543174 -191142365 -237126613 -808442204 -670172561 -590005674 -179274011 -802926331 -801348076 --629561981 -376181435 --982012030 -980640209 -639841639 --194952696 -739283583 -432418013 -506661688 -801877699 -51680445 -788040887 --912100620 -177316596 --291792604 -748363359 -554094244 -242724078 -634831566 --979274413 -827587365 --700907666 -997213276 -127329929 -753434637 -299154727 -457582049 -521944629 --448614597 -596422239 -712283969 -876604901 -48905213 --448638461 -93304426 -939152472 -411813704 -326448200 -451154601 -547409657 -860322461 -319020057 -822057186 -823194241 -317257260 -418336151 -343683502 -508626440 -681106569 --24402677 -113998078 --726354186 -736162896 -298657576 -580848126 -978149420 -998919027 --714004615 -582540028 -615866503 --137584619 -9679011 -289155485 --868255854 -902227805 -724806438 --421501400 -409537617 -591289954 -961891079 --851426082 -885770520 --926627595 -898012489 -821404818 -396918056 -306997205 -593701073 -221432513 -998572026 -704395179 -886340666 -896741358 -776274327 -647482043 --410427911 -202651444 -691303533 -725102949 -33143056 -849416120 --610319215 -747652286 --299154727 -577367583 --235581875 -581765743 --4996266 -415084359 -953748274 --645590704 -283335586 -40925997 --739283583 -185909532 -618039075 -82310563 -251937184 -760501368 -137979274 -877592619 -990817976 -538773346 -423758102 -268116665 -117212499 --315582635 -688675670 -675540319 -704539406 -706574811 -402497030 -858723318 --275022744 -904029210 --990817976 -623640471 -905262489 -799792490 -737732282 -65483561 -781953432 -248480958 --251937184 -226967834 --680535608 -143197486 -801389692 -642126615 -288404901 --729554076 -313480045 --887775885 -447096499 -243607979 --110500545 -263335274 -353913414 -36980210 --844710925 -920467822 -915030963 -802504537 -126237605 --529749705 -276583897 --644019901 -164235697 -644184489 -210102911 -49944542 -831081077 --772139650 -691685645 --112141723 -751264404 -385725081 -713782121 -45278539 --892184700 -502810053 -503597564 -609674755 -218973130 -883570859 -228207168 -501756153 -574669229 --771363401 -863243278 --776274327 -400461034 -820012049 -172359939 -796871654 -231298327 -490761477 --175691865 -934745366 -380259303 -141668254 -936368462 -54673614 -609264585 -838012781 -629194609 -696061364 --605475572 -900741789 -970844876 -585136354 -767765431 -820554121 --264679946 -964812788 -854741632 --77473367 -124666679 --52845145 -557730538 -367379704 -210947634 -307949615 -58288203 -222888150 --819736625 -281126941 -134786063 --118796500 -602457251 --132636943 -920639170 -840740932 --810888184 -207443164 -201657380 --193947475 -473021466 -857303887 --759837415 -767496446 -830818354 -635135348 -935841292 --751264404 -47344916 -375804958 -290815389 --759888598 -247504966 -372219099 -38992306 -287185527 -244712972 -857444328 --814859404 -880922439 -420668507 -782983897 --61578667 -229056111 -802205029 -863599530 -936576895 --975852134 -93289967 -877645175 -86629546 -819829370 -934071291 -530578121 -969034591 -614095191 -487433105 -404138300 -976988413 --903110907 -376696599 --86654027 -470940831 -225125 --802926331 -904388309 --324683151 -717864360 -188488725 --171560116 -214780216 --311765645 -666460131 -4815536 -721688943 -963060876 -710240708 -708981813 -594061619 -466698605 -428539504 -919732499 -222063208 -748277157 -950255259 -979883440 -774855578 --471987031 -372797183 -220400396 --415047828 -187752815 --692908449 -594513739 -441345227 -464859578 -500695382 --513194670 -989475081 --92815220 -779951409 -779463223 -621844832 -85121287 -566838795 -71053831 -300220425 --386961956 -407259324 --195834303 -623030353 -465179223 -211803881 --17213292 -515488676 --82366301 -485487020 -387975347 --639128856 -710708578 -384057666 --226498864 -981818348 -533076467 -573369075 -171899903 --79970977 -402989049 -818399353 -728564502 -346379188 -877123007 -994328505 -652023316 -472894539 -789863857 -150104520 -972673795 -858927325 -950688348 --629194609 -777784147 -414862990 -558770953 --717864360 -617157037 --102182489 -366406531 -194577677 --191142365 -182939573 -732136292 -529609334 --967634810 -12325672 --283751995 -971971555 -242315651 -663145035 -974573579 -883866885 --226443092 -717547772 --357841732 -920744179 -249873409 -26551688 -686817219 --601623552 -743120291 -760979361 -974963417 -121436111 -695999901 -708265281 -303237675 --376384973 -190440035 --36919747 -873133741 -674345238 -255252977 -533789746 -509508536 -943124695 --202651444 -847962966 -330753627 -650544475 -22305038 -694560798 --737732282 -661723017 -877497092 --326448200 -191913288 --289132489 -389913254 -482779324 --644764039 -889932218 -942864302 -791035341 --288404901 -791722988 -485420663 -774003853 -376095773 -272931066 -695323330 -481769352 -202926690 -797026827 -758210602 -74367964 -239134289 --244120470 -189902486 -273836128 -111941960 -252251987 -641635479 -110849403 --760469247 -523312690 --260006137 -667909863 --26551688 -137948251 --648108182 -33248329 -485347244 -585296734 -168356128 -199711095 -997662257 -706327903 -194522189 -270971568 -262397536 -796444068 -636078904 -549429568 -106176002 -327669846 -752932967 -282846223 -864713226 -862470087 -91602315 -525210256 -150202591 -165480397 -936262 -468450425 -280669230 -780506145 -419660734 -252332254 --199711095 -147107269 -275303701 -160089675 -822248671 -92904397 -834017934 -291281058 -377042435 -961470095 -964722775 --972673795 -952077571 -878669688 -633044174 --601711521 -808045972 -50141465 --688751710 -313842501 -769236297 --20286691 -463273870 --690428947 -593922779 -341706502 -348632581 -951486910 -729760741 -759018029 -289971979 -526302256 -16635884 -306941911 --202418289 -817979629 -130486454 --191599136 -99335316 -785968690 -811598843 -975073330 -582764101 -903142030 -473131442 -898925147 --432418013 -428978210 --776725606 -101254090 --585968129 -555954858 -381849627 --817002588 -400872684 -998509302 --264300185 -694764054 --849416120 -99181317 -196014231 --856904153 -245746036 --60920525 -332289319 -933450402 -836888725 --153725002 -577643753 -359606267 -418718059 -573234280 --253498123 -456935700 -274041588 -797743814 -96704664 --378098491 -258927868 -930096035 -690753016 -40018849 -472973547 --661409625 -725001328 --48905213 -727102059 -757674182 --545946996 -192218549 --757674182 -463931955 -914892609 -35716469 -317240935 -386194301 -182481904 -479553226 -506944186 -347364652 -694317876 --948584710 -653096162 -48244940 -632304658 -805589861 --478673681 -562083287 -24944286 -117536868 --631771615 -477322554 -353137664 -8763034 --626970600 -236352437 -782060065 -791859571 --650544475 -613246315 --37953581 -359977420 --909355496 -261765215 -445792686 -9033107 -91204779 --960560048 -656400776 --822057186 -51267630 --466445052 -253957092 -825074273 -302261466 -747313013 --537515869 -202952816 -224918149 --11087334 -390957419 --331458721 -140445742 --857444328 -333211749 -245415659 -423473031 -996274797 --762355030 -122168947 -31465581 -109082921 -896159103 -736946331 -866273363 -118153432 --946522002 -104358824 -496522759 -64457612 -908183553 -209837006 -415332516 -887285050 --24377168 -400532441 -395278316 -537686401 -234641100 -630279114 -212757272 -822649030 -392846237 --975219068 -27071939 -349766527 -225812696 --843190627 -213097673 --380405677 -509010231 -480832564 -843670030 -358215341 --160302257 -594995136 --586986257 -599368464 --270971568 -665565688 -746892090 -88938884 --194522189 -461057662 -289549342 -565417113 -139819907 -67994735 -169667928 -666285933 -531152646 -828740269 -679729546 -26933777 --327889172 -844241539 -762031795 -821036468 --820554121 -299233737 -618102819 -672878534 -138178962 --263335274 -928113536 -975649672 -604200684 -528072952 --680865625 -740105586 -858954645 -872206997 -349003057 -287383718 --667865599 -397813767 --747652286 -213463656 -796649146 -152886156 --794005553 -913373739 -684617111 -545310186 -390497603 -928706099 --99335316 -426145590 --872347301 -866651086 -2179056 -625431280 -101780355 --584811911 -485106901 -747859173 -716813659 --387975347 -117056292 -817674881 -668170281 -812950338 --533076467 -161638505 -578556490 --829981567 -429853323 --126237605 -210549270 -608689713 --11495196 -670234016 -411398240 --294348547 -790272799 -529582541 --702392510 -553127478 -659071396 -911171713 -530903676 -325137224 -838477494 -45552003 --215721282 -497950326 -777863631 --447096499 -809306581 --97035066 -622413886 -891525200 -355349794 --878776044 -43268833 -395494150 --295181595 -748254639 --725001328 -604044369 --783380773 -994775938 -438303012 -664759525 -643816168 -18214363 --192218549 -132212965 -205599414 --134878419 -581799108 --122591425 -675049299 -845142095 -414467774 -941877693 -403409941 -824315553 -892247574 -772534313 -214054253 -490222339 --496295804 -868793473 -116924713 -695103536 -100537624 -43505334 -882345905 -484503528 -538350758 -907209711 -14540509 --395278316 -884413223 -912129217 -467500341 -278273147 --414862990 -714311667 -485970596 -18857010 --390497603 -732435819 -812085543 -643551557 -454259476 -959564162 -262130258 -772290025 -468866854 -762830304 -73530776 -416855166 -827192125 -787335491 -653835404 -505301183 -38230799 -677246019 -2824817 -211755364 -542549612 -959933305 -799219049 -1750437 -857440073 -23924067 -693666684 -254078838 --396635829 -995456722 --656400776 -923709571 -418743503 -245168998 -912737284 -916192428 -989036399 -359454974 -408602126 --210102911 -563866108 -788419314 -343048165 -560447249 --423020925 -27950879 --641970529 -456075581 -423555904 -837940510 --376421819 -734500472 -500420847 --399293060 -687078769 -861009829 -283240161 -175943461 -860621733 --728447821 -17519461 -320749013 -670108606 -515678499 --168643318 -608112513 -794015696 -272174279 -118374464 -965476719 -750443732 -483392864 -591555276 -655982049 -216305390 -411502163 --300747462 -283756732 --924907950 -446399918 --46456976 -689301960 -979453614 -149974308 -279722236 -830160017 -893496030 -791147543 --319020057 -534813093 -748619004 -608498486 -787283402 --457582049 -348504928 -122801917 -48096499 -295964800 -99866903 -214474819 -686992712 -117524304 --668170281 -215280772 -945331970 -243236488 -958621980 -112586034 -772743041 -988372184 -843527392 -665336890 -480681924 --1750437 -931178262 -92591136 -655690283 --115354158 -930733162 --139819907 -89692032 --734500472 -369400008 -969134409 --311113306 -737223329 --127084245 -609001549 -908458031 -283411884 -170231113 -84243154 --91602315 -345852992 -661233434 --164265448 -98163187 -212093113 -95094245 --154783893 -907956207 -849475598 -950767814 -152069619 -152499484 -884443558 -755932780 --670108606 -508494800 -397539088 -815552301 -32812300 --963060876 -911540239 -59493584 --122267551 -995283297 -577469181 -667847672 -735834578 --976473830 -649906052 -585703542 -900782041 --43505334 -220763576 -422406349 -451288973 -56815381 -479379663 --419660734 -678042310 -436751631 --804010208 -718476284 -268131366 -7658811 -287550943 -252476440 -42287224 -725462902 -371552303 --653835404 -754729505 -51930074 -30153398 --793652417 -212674704 --234345612 -503409198 -759658829 --497950326 -663033820 -633792015 --36980210 -825460320 -552942447 -165602884 --289155485 -517167668 -151832667 --274041588 -969948827 --993904632 -707109200 --4919424 -286661733 -124627978 --832351816 -937345933 -849282876 --263890330 -939824638 --426689525 -518765592 -782287493 -381394326 -88907872 -204509115 -944853376 --346379188 -392660468 -415323173 -696284237 --951486910 -600682913 -700806167 -277823110 -107040915 -119223216 -22943398 --694317876 -149676353 -629810499 -721200153 --577643753 -749028084 --283795211 -304744390 -536958267 --362521559 -457974552 -142356391 -726335672 --413477767 -504685954 -721507262 -521079005 -195586483 -993166120 -339648414 -763633128 -997872567 -985378961 -804023070 -893205533 -597974693 --252543016 -983342853 -264886744 -318120245 -752806441 -518475768 --801877699 -591498690 --283756732 -768592502 -322162344 --783899606 -274027863 --235790602 -689470806 --709426522 -311193722 -270033852 -36171958 --268116665 -32270985 -893760912 -590380730 --916192428 -433734141 --928024511 -510046339 -262719607 -643152944 -292649340 -310459637 -523619146 -285196332 --824581164 -31464923 -855990485 --943124695 -928241036 -281894280 -762483151 -187871519 -786025868 -615164297 -732933921 --808045972 -858042167 --273293472 -850779873 --614095191 -478669567 --431097318 -751794698 -605790305 -497230498 -630208513 --353913414 -38935855 -756144177 -484841725 --767765431 -938400350 --892688123 -647279552 -482451629 -747583373 -618736405 -683452092 -778445477 -577132391 -278860700 -966783434 --508494800 -238606854 -509284186 -833629957 -638955300 -348860690 -356813244 -421973952 -394358004 --353208467 -927683561 -794503097 -142764365 --820171393 -978822897 --153749024 -647838749 -218560400 --863243278 -625884163 --556165526 -593997892 -164103528 -535349442 -625759861 -520250290 -823413836 -186716964 -263677298 --880922439 -889162323 -303668521 -566106116 -23691463 -839367322 -99760804 -982910782 -49568345 -551193590 -941761385 --598278544 -48059079 -579928177 -929319741 -16393476 -822077227 --769308385 -302567758 -975933965 --691132153 -353919760 -920006571 -828634821 --293739371 -140997957 -808602987 --34989873 -661956273 -364346692 --666380090 -655702646 -811716188 -165264685 -260934326 -367541556 -444295416 -958569728 --152499484 -403031094 --573253316 -125181491 -855264036 --262130258 -89689672 -648002947 --585703542 -97208617 -776913528 -906580062 -321226665 --84716498 -335945695 -923156270 -319809717 -39670148 -781930306 --166681067 -238903464 -323388892 -429084590 -94161501 -926349152 -301770043 --125181491 -965461000 -687415839 --49568345 -103523903 -21995703 --640006903 -904257522 -835160181 --404138300 -37710928 --701028506 -79405473 --320644246 -5179621 -453901010 --344004842 -495378882 -341215558 --257860570 -939041794 -951242122 -268756177 -926397286 --518475768 -557793108 --482451629 -4320124 --156672631 -449111549 -308288809 -722546544 -338188507 -226796692 -369316239 --240649848 -976190923 -379391436 -17712031 -940020779 -686022383 -874384102 -75148724 -406185073 --756144177 -192851782 -451018012 -266307666 --960661329 -205748404 -681522972 -497444016 --667138475 -290836749 -732163259 -777363563 -447979840 --529582541 -2611959 -285983488 -881514875 -915279572 -466705606 --23691463 -191772913 --889162323 -150097427 --112586034 -439310679 -607229671 --291423253 -221477822 -32963141 -75219312 -814062843 -39684162 -487249760 -597908743 --760979361 -397059969 -381244382 -148870275 -886313981 --439659969 -580776043 -442662803 -996020186 --149127249 -533770528 -378823845 -266334332 -679125854 -647041866 -170462476 -944451442 -542488961 -118562091 --613443989 -722833552 --919732499 -73251268 --48096499 -355915413 --975926001 -719134914 -411589295 -592121722 -705113262 -642401181 -696729576 -945667840 -444116675 -777008658 -169742329 -804985618 -597627527 -469111346 -93491853 -831359388 -793684328 -155407627 --433688733 -326041715 --574669229 -73567122 -737570783 -711252086 --663145035 -920493114 -626499635 -549110732 -335689266 --41073951 -287857841 -935885622 --14540509 -992158859 -188092767 -95818386 --928113536 -953405244 -572440043 -511130612 --502266235 -532433596 -544092928 -576925320 -879188958 -273717201 --86629546 -531221026 -966772290 -777664485 -720800115 -365046820 -324725384 -648171973 -790765111 -479295692 -926525224 -894057068 --778144504 -752343585 --529021781 -382576860 --927683561 -793161694 -843813363 --947084057 -798642366 -150656151 -680565674 -897842029 -652328895 --384784584 -846281900 -147203474 -278215754 -451966324 -50207680 -401355234 -76801391 -463916942 -191562061 --772331430 -414528988 -374437221 -62641790 --107040915 -53137757 -422666324 -329384046 -494397182 -831338163 -554311176 -658382222 -227576556 -452399718 -83248332 -929626551 -814519287 --473845672 -27693681 -27451878 --568626203 -262603528 -253209404 --415302058 -327723159 --332289319 -768580313 -519766209 -796483772 --829347925 -453813239 -76975516 -354063440 -959674307 --463306326 -457772509 --376181435 -342381961 -331681190 --215280772 -593930823 -155590587 --558770953 -119979378 -649526833 -384065724 -995450508 -338783881 -894076705 -8662126 --127953713 -941786439 -59759716 --630279114 -709134610 -17654973 --235541732 -815736502 -614210886 -556731331 -577665649 -848379027 -302486945 -332513904 -655833627 -202583678 -259970105 -587970668 -972865508 --591289954 -128285385 -362433217 -667463764 -774239599 -415720094 -263674732 --826290014 -473286595 -191592489 -918418702 -390643398 --843813363 -508362491 --146132603 -220778677 -727024574 -961517029 --329071768 -580323501 --233608870 -716197287 -739636777 -66614728 -840487368 --605087817 -424992550 -663006332 -258174353 -290852447 -816853388 -655999306 -587112154 -441520505 -769849299 -781143499 -611194833 -311214308 --392660468 -846801889 -711670342 --342051632 -678542570 -185577616 -514942667 -513056504 -264076917 -766675082 --418756644 -659255054 -205004046 -346432852 -288375109 -941784069 -409908502 -746865993 --964915716 -887557589 -915945149 -803495068 -468952987 -193494557 --843527392 -865066946 --769268522 -961475844 -112321876 -837951295 --95094245 -815414139 -154059287 -940735347 -366008990 -211773763 --673295059 -286990704 -272879195 --705190459 -910660400 -479406431 --822077227 -243608460 -179109730 -802508640 --799792490 -580411989 --906110238 -219657721 -780907911 --981818348 -265889497 -488516644 --94133241 -247582957 -351467007 -111534424 --167106574 -256719326 -847680091 -147400122 -89954255 -953397863 --392846237 -869662509 -879978552 --663260457 -562878875 -18148506 --91776001 -130224043 --402989049 -475563375 --728005499 -780645492 -193771979 -779013671 -757257026 --914892609 -493576999 -781796628 -595278704 -789438414 -918038036 -390640770 --622304376 -191461604 -546134436 --480832564 -98565871 -827190541 -454035212 --73530776 -187234117 -876477514 -300656880 -328067703 -809196077 --952077571 -62185174 --386908544 -952220481 --190225085 -61740116 -716985222 --130224043 -657637196 --926349152 -673440691 -185897267 --39960045 -861928950 -602456290 --503409198 -831340305 -601004117 -191032822 -558260941 -481965009 --808602987 -955616154 -295518971 --676882810 -154128595 -582280746 -904791039 --827587365 -823001091 -882987521 --695103536 -933885253 --710947806 -487815029 -229318093 -263405121 -585322948 -434252422 -390755960 -458729216 --825522414 -917455633 -226429361 -822307812 -259232231 --613299303 -992185329 -746570135 --353919760 -748612658 -322742371 -827627195 -728359738 -879032562 --217807399 -913453216 -41263964 -982082622 -822650559 -265721064 -773232686 -777142097 --817674881 -255765697 --502069237 -949651113 -107894068 -609864909 -821789120 -271413825 -172100 -262940741 --867762003 -546981367 --481769352 -809083211 --748112724 -203842833 -169780026 -231989624 -555659829 --471225706 -108650471 -123240599 --9679011 -332156635 -735271436 --289971979 -755238395 -796045557 -167829669 -952583839 --983223036 -981763781 -514086600 -21380361 -894738687 -182062534 -189047844 --900741789 -792751892 --201078796 -401537698 -345261333 --252332254 -145465614 -956137873 -136284393 -605018374 -498245577 --929626551 -739176180 -321032916 -756386545 -327622291 -125217142 --616821203 -368663859 -777478677 -654510463 -26622009 -476597411 -121921732 -771642228 -948654090 -309899079 -667289738 --553127478 -839465043 -179974486 --393474134 -940145156 --243608460 -752198545 -308735580 -513501350 -607652197 -815131681 -647917652 -347099110 --261765215 -620543193 -24801118 --667881263 -685214273 -263169126 -724681343 -956535642 --205004046 -107769403 --469111346 -402953830 -618947736 --808442204 -242685631 -118425594 -210327136 --861374961 -420205731 --396690586 -537367048 -32940023 -130477990 -176267300 -496712233 -922773236 --691685645 -551704832 -56281953 -828148130 --361066198 -330524655 --923156270 -90822196 -51562495 -114779237 -948146869 -229055032 --211773763 -225644058 -181223532 -133430982 -691905950 --89954255 -875786944 --38992306 -208973493 -75439042 -485452965 -459577552 --485487020 -274821219 -776299404 --566559755 -636267314 -128403399 -444803343 -972757503 -754068865 -108434058 -478766158 -919496330 --728564502 -650285440 -324969720 --894144300 -892713183 --95297754 -70735722 -275241658 -674793433 -849713955 -735928759 -603764391 -654484915 -705140844 -736334647 -959891921 -992601884 -646087654 -734335535 -10610434 -276010884 -546844741 -8123542 -94200789 -938070959 -257780361 -383668091 --994638315 -179362937 -319382330 -423794400 --133430982 -359516383 -578084070 --721507646 -351812083 -584054492 --965227874 -392999448 -155226889 -257270469 -248254147 -270041069 -858989646 -694386264 --475733126 -539508455 --562146001 -717147219 -898676150 --782060065 -487359138 --42928616 -947237135 -395495453 --5179621 -296860273 --934744081 -18110623 --906580062 -779195718 -944328595 -175614 -599118875 -294270271 --930883053 -667835682 --710708578 -634658942 -640492641 -214049788 --537575483 -684685470 -5033585 --166287389 -39643123 -134177066 -601045049 --728359738 -130258492 -542809645 --904388309 -510354454 --538773346 -325037542 --822650559 -619849081 -670718258 -302826056 -306600387 -380575811 -15159702 -531774226 --643378570 -11533016 -418498097 -195258883 -377182429 -998113467 --591555276 -207278639 --684498417 -710009899 -261970068 -872557835 -300718987 --555123261 -107513259 -386014707 --798890512 -148409444 -685801097 -712491543 -227744012 -367655582 -544394134 -180004268 -801829067 -302835037 -898017986 -893696908 -878279823 -653800592 -405978022 -79965842 -395014062 --833629957 -378065191 -197651031 --640492641 -782448500 -93086534 -248601129 -664550824 --698484772 -966299695 --152069619 -88243141 -778448888 -296435924 --666460131 -523618213 -848394503 -803589787 --506944186 -301622105 -556804615 -221596360 --688675670 -121296926 --33458429 -699202496 -513946266 -502309762 --413671665 -968890548 -140798766 -196459703 --368283628 -539952334 -702482684 -810526554 -317342047 -720292176 -561577161 --597908743 -392382606 -777151297 -16076845 --796444068 -546500241 -908022004 -180515748 -438830677 -568122835 -706809942 -647359622 --636078904 -872038789 --485970596 -949018294 --484972331 -680783492 -680336737 -170707984 -794085149 -502186320 -163653866 -134079227 -409592772 -365803255 -625793527 -908910278 -653977795 --291281058 -984114309 -773128233 -265710476 --166074707 -990834910 -559292523 -454137983 --908183553 -963954883 -637554907 -808099936 -620592824 -839208304 --411813704 -791695224 --762031795 -452339660 -727790643 -776257890 -529644441 --892247574 -700874804 -967670099 --711252086 -188381337 --154128595 -83659576 -331849412 -248724029 --341706502 -306998139 -161257958 -329581258 -97869584 -187699383 -272808830 --815414139 -221946400 -988304402 -364550910 --884405809 -858847215 -775967878 -523759586 --540304559 -631140785 -549062665 -839107266 -813268904 -537309788 --664550824 -838832696 -526207101 -538146889 -854245816 -291435743 --926525224 -816592710 -868892 --1581586 -936134949 -946492538 --487102061 -111910278 -285590394 -855475650 -89181131 -286027006 -736993479 -188011626 -162461497 -285313003 -421598110 --889318179 -48305037 -398980865 -202934190 -614948427 --327622291 -448497316 -84071905 -535343169 -288965750 -376097611 -934836105 --549429568 -421841891 -35771313 -77756918 -56005917 -914880748 --673440691 -544392094 -462229538 -791033464 -502623930 -529348509 -516514462 --539952334 -542484477 -370162922 --782448500 -359905308 -217197154 -105026119 -609601033 --936576895 -733649130 --307557498 -849965449 --565417113 -848039377 -416405706 --839107266 -11608001 --272808830 -921820986 --30153398 -495693218 --124666679 -38151457 -239552392 --381066351 -808015301 -441926912 -638208201 --130258492 -128713997 -310486218 -950564334 -594252086 -519557320 -560464336 -39345940 -337478124 --147107269 -186754942 -302118875 -994955602 -618758275 -816456243 -669546027 -820803965 -473541932 -531753916 -305613727 --372797183 -812140045 --810873258 -316763763 -478054694 -681443179 -287759974 --864713226 -525951184 -790291212 -76513374 -1224015 -526686229 -865687007 -881312797 --415323173 -241073440 --861928950 -801888723 --942864302 -464928769 -880303115 --941786439 -742422033 -551405228 -109406247 --330753627 -95489838 -504733188 -961475252 -747906833 -307566408 -293959235 --892713183 -971353718 --940172693 -319976873 --617157037 -867690731 -582709374 --928100087 -584089914 -486980428 --827627195 -62999273 --421973952 -799014004 --424378502 -418999833 -369178722 -122347754 --790291212 -685306753 -604185540 -214773459 --716197287 -867111344 -728247789 -366676190 -622682664 -907091697 -399799834 --361807149 -978134515 -813454660 -927214429 --506661688 -88724630 -744985767 -582655480 --879188958 -36796834 -367775547 --136284393 -300988088 -903379644 -925972370 -922204890 -497859842 -448705114 --21380361 -167608250 -464541770 -9522372 -531571934 -251216118 -133506713 -241713977 -170543760 -435126553 -620370171 -443498455 -869968355 --74175083 -488920193 -392700747 -888647582 -995232891 -513943796 --836888725 -92987557 -798145678 --892407901 -792314374 --758988025 -507599816 -783769198 -892216376 -338322730 -485248571 -419750643 -454851750 -471945001 -921225966 -909480613 -881462161 -372967840 --341285871 -964195945 --927941134 -82774689 -950180537 -109412766 -113713401 --594252086 -648024677 -959644657 -992169588 --446236099 -178791532 -106470348 --404819175 -891190080 -591962073 --487359138 -70127999 -359036955 --150468500 -343795585 --220507504 -253433137 -200436462 -141012575 -813360660 --799014004 -272132829 -519042561 -360608983 --104358824 -407113903 -421106696 -421426970 -62302863 -735060790 -354993452 -342375001 --480359622 -777970920 -367429697 -631380659 -697733091 -252083568 --743120291 -58720171 -189141893 -938422653 -949859306 --998509302 -525478666 --787446077 -243242917 -202391359 -910416817 -983069378 --658382222 -447561094 --707109200 -882149908 --117536868 -597771368 -4369619 -672056546 --791035341 -284398318 -724790914 -771740507 -627397490 -572468480 -23192089 --446788803 -400531854 --618039075 -245425644 --528072952 -117047171 -631265335 -729284863 -925466495 -601615554 -586489999 --607652197 -691429760 -271506126 -193635719 -373480007 -816938375 --879032562 -730195769 -745440526 -993763746 -774812988 -735952610 -97047572 -902969444 -165974705 -351028622 -256668790 --566838795 -30705672 --411398240 -900994357 -640630100 -701441298 -123139281 -924352148 -122619744 -288737187 --661893863 -23734916 --295964800 -691361774 --739654882 -558597795 -372332339 --241397141 -68603434 -915247116 -801621085 -77862376 -598095849 -779237669 --387496574 -505649609 -736320603 -672257247 --522510291 -883072056 --243402904 -675048755 -422071640 --956137873 -807758087 -652464070 -890411026 -389240939 -119239284 -851640905 -724894828 -447368213 -877239285 -43842351 -178631671 -922730251 -650815334 --349766527 -112404249 -385055573 -617853329 --679125854 -264761387 --514942667 -355100957 -260902473 -547735110 --190018453 -401177179 -508827898 --776913528 -170709854 -481569750 -486679698 -213388419 -309543261 -694416734 --609001549 -62273323 -75609362 -187164407 -144486372 -113414589 -93881134 -493292584 -104890743 --831239163 -905328290 -899137516 --178631671 -421034329 --982082622 -411911204 -171540947 -228906143 -857987345 --643152944 -239574750 -423698223 -627563510 -972697385 -93611089 -370103656 -166451065 -360718985 -339115853 --771740507 -902574347 -2758317 -105112345 --531571934 -661161103 -872773932 -343552851 --626499635 -487408679 -892048919 -592865800 -977767996 -74221293 --736334647 -366311744 --453141599 -728483741 -954180400 -372626272 -322132679 --623030353 -620639636 -362344860 -474523557 -685328545 -234740203 --169705115 -566868911 -717260744 -623396911 --428539504 -170915076 -205786653 -400787490 --83659576 -405868287 -421876399 -582460046 --210327136 -415616219 -467952747 -601785802 -300313834 -595255920 -955082710 -878228236 -563885233 --713782121 -308254228 -901166704 --702482684 -653061760 -432966838 --847962966 -75231948 --402953830 -101714269 --476597411 -774421694 -186900680 -194006883 -896548827 -286314771 -429098435 -820640077 --283240161 -830902004 -254434907 --422254949 -992494093 -391381749 -426452223 -996574138 -245046059 --773232686 -614973617 -350255348 -995237894 -460409318 --935841292 -401960080 -514028163 -639742448 -239079132 -858562794 --687078769 -976977366 --209837006 -206240807 -432461240 -331168229 --9322005 -351824049 -309215416 -37232834 -569545021 -514724361 -96720862 -526592055 -156049175 -272877682 --236950252 -618580218 --486679698 -971698073 --710240708 -371708943 -652000089 -728183783 -600089508 -523280553 --495378882 -560482611 -921586987 --920372981 -341087242 -435479919 --141668254 -567920490 -435068821 -399816676 --54453141 -520739205 --272931066 -74472646 --772534313 -632855132 -693985711 -73177187 --933885253 -680718122 -189775507 --149676353 -943913197 -207335432 -249267261 --17519461 -597409852 -120101570 -99965641 -137096104 -811312220 -162179666 -743451922 -47335802 --40912142 -300918080 --68603434 -239763157 --144486372 -355522553 -519741996 --362179642 -849071759 --488182243 -845168531 -743731839 --735928759 -805349737 --523618213 -314017600 --291330406 -216069311 -743884035 -574416855 -103373034 -633438236 -821553035 -352420417 -386234655 -270602590 -323930800 -144133516 --807909518 -892563279 -854769135 --876604901 -504165198 --883570859 -494598824 --4815536 -918894670 -301730969 -995618635 -843799012 -505539610 -163454670 --686992712 -85119427 -77282036 --424992550 -114576433 -586064844 -396993371 -32208385 -850858433 --244175552 -490446658 -175850722 --716813659 -917286380 --695323330 -296588731 -303615680 -547510857 -546417647 -182252161 --914020781 -2028200 -242056214 -144397249 -915052263 -660915862 --958621980 -588538228 -595693487 -992745537 -362399709 -29423428 --777478677 -11902360 -149978328 --87733157 -557957149 --487249760 -974986113 --40925997 -988227385 -898367831 -662851848 --462229538 -606668936 --445792686 -859502412 -279771249 -763577738 -880762446 -758507467 -391891648 --523378357 -336616043 --953748274 -566382230 -408343577 --681522972 -247936493 -758435676 -334804356 -308855893 --154059287 -772218565 -244127529 -641961124 -579279678 -549587312 -708920240 -480079172 --517099681 -638784249 --907209711 -676910830 -561975075 -987038834 --305613727 -134190881 --641635479 -736519501 -330564073 -537213894 -842464646 --809580465 -586366704 -652103966 -169529905 -798475956 -279365580 -697037627 --122619744 -757912996 -287499639 -661936370 --316415067 -559857077 -856041272 --118374464 -675896116 --165264685 -168856276 -590565392 -952843490 -105973121 --959674307 -302987396 --351467007 -415692246 -397200064 -533064750 -816579011 -654886034 -26441869 --544092928 -425572771 --976190923 -742631827 -274717566 -927176131 --451128553 -965083999 -726678582 -339651753 -761644605 -209319240 --270602590 -917154900 -710513215 --332156635 -330064891 -353069211 -494709034 --796649146 -847805068 -213143884 -644071419 -580995704 --704395179 -323151825 -265879579 -192707665 --605306938 -179025066 --652464070 -624722964 -367534019 --354063440 -120051531 --195258883 -604696954 --713204757 -78637628 -176818182 -132498061 -959186784 -225380138 -615189501 -104678648 -930941966 -667844540 -625317452 --477322554 -8128104 -893209733 --367429697 -694296593 -168609016 --554311176 -560867314 --128403399 -860643799 -319749887 --159348957 -881330095 -253928180 -463798816 --358821988 -241252544 -941201324 --534813093 -252818391 -211190892 -622782938 -980337757 --777970920 -208719193 --939096150 -377377094 -290444022 -941521354 --367534019 -919307583 --200436462 -656703346 -582097516 -487591179 -204114197 -990443219 -512688862 -475948592 -80905661 --271506126 -594457860 -84558546 --188092767 -211774431 -453986908 -212359895 --485106901 -249280592 -642442848 -782007076 --809196077 -359117834 -701989302 -221227650 -155215537 -356629582 -445246506 --366406531 -826216135 --119239284 -78066807 -596868157 -422117138 --797743814 -809471992 --810526554 -323537964 --258174353 -6032365 --428978210 -733449539 -671941734 -986024510 -749368109 -286272801 -384513246 -730206195 --323151825 -646161661 --736162896 -721080865 -217310194 -568782485 --766465423 -646163479 -708341124 -924705805 -920373258 --266503187 -31591024 -816866540 --992342372 -936352798 -512629727 -741614936 -913375014 -171155960 --791526906 -33956683 --904072314 -38777142 -242617069 -48536791 --478669567 -578795897 --566106116 -390569950 --689179762 -749732582 -18742156 --210947634 -920803667 -828709669 -777279370 -737997438 -750108188 --307949615 -441240738 -650595048 -653840226 --827192125 -993353500 -398463132 -261255513 -190489713 -110853463 --322132679 -510862966 --368795085 -12106287 -945084738 --574416855 -119508778 -386257563 -386168995 -782438278 --284151399 -291991469 --736320603 -716298662 --53137757 -314148114 --918894670 -20042915 --161638505 -938860877 -583655625 -87926616 -60146112 -458056270 --24944286 -320806687 -362606255 --169529905 -465270766 -632457417 --669546027 -493335644 -677491204 -500649652 -400200709 -483779865 -713306768 -234463132 -600469439 -570358299 --213388419 -379029010 -706602644 -826289680 -212837137 -720261395 -594792099 --988227385 -194554739 -780930128 -911055355 -163895177 -518389444 -460063964 -158454842 --249330631 -167523595 -561026549 -680617421 -777163149 --936368462 -93591412 -31634683 -483253876 -411199024 -622701011 -764736391 -646722805 -475539540 --820374521 -879181973 -98078390 -445677974 --451154601 -857531106 --794503097 -427878499 --655999306 -300856409 --225125 -891746723 -634066412 --380575811 -378529994 --473541932 -847968846 -434149656 --249280592 -394642226 -455585446 -438677583 -788764579 --850858433 -529697005 -253855545 -583495921 --203842833 -822834143 -833882018 -189683259 -212428717 -212974690 -725749171 --187234117 -751278757 -425560160 -784403670 -399628507 --369316239 -901655758 --253928180 -191464740 -894650969 --779463223 -842959884 -515065790 -430888073 -129873703 -783330025 -931039289 -274470837 -2387314 -654350194 -941062616 --752932448 -118225649 -251677180 --431837035 -171846082 --576116309 -494444585 -106381342 -65964362 --226796692 -217315273 -617739965 -309991058 --788419314 -562643010 -179011423 -31580298 -927408274 --837951295 -126911983 -20648445 -863269749 -88919299 -426597004 -605141386 -582229282 --856041272 -180222041 --791744098 -605402610 -473852142 --812085543 -71622244 -281880827 -313166204 -417009167 -576224963 -797206052 -427935077 -838216411 --258927868 -762551250 -746520797 -166131023 -70304661 -261873028 -729536539 -55125259 --348860690 -11844290 --289840582 -761878178 -2758906 -875730570 -317086229 -113475443 -244153376 --495693218 -59175279 --287800502 -949785600 -32047669 -326319182 -264159469 -413955551 -19659123 -966067028 -779247566 --507145997 -390828499 -929442912 --353119170 -326653703 -931591136 --909480613 -750459420 --828634821 -665976306 -669664431 --642401181 -272324044 -696049257 -839795396 --234463132 -625909317 -572430043 -810589352 -257539302 -520638454 -950881718 -3066506 --927214429 -184872509 -65082241 --860322461 -614907559 -671174392 -717148981 -771540072 --243236488 -395702645 -132031439 -845598754 -698229961 -87162224 -723682057 --268589927 -696974946 --111910278 -125392794 -695443698 -933841814 -435788673 -386755281 --229912927 -33409772 -509348948 --177316596 -322795338 --894057068 -315775794 -976899038 -924731897 -277413669 -831518290 -608449512 -961365254 --660915862 -524463906 --479379663 -947763927 -924926527 -919381713 -145246917 -685991032 -192340215 -907448606 -598289616 --11902360 -906714965 -548152228 -705016801 -691783703 -994450604 -870909010 -346501337 -785284966 -45123065 -503890423 -925915126 --471420367 -124413174 --674793433 -617476712 -134777384 -939218590 -560417976 --384057666 -72857081 --737570783 -644648301 -544828964 -81818374 --536523708 -809534673 -448043578 -807674541 -873717845 -228311366 --189902486 -915857465 --917455633 -954951729 -293742357 --675896116 -314282719 -106953934 --945667840 -976990676 -970618438 -242400250 --457772509 -112580434 --59759716 -263051354 -279704631 -196544269 -446470160 -929305748 --505539610 -827046590 -533680293 -744922760 --454137983 -865133807 --642327230 -599935727 -394043575 -662207590 -312491117 -420797217 -301294195 -311410308 -137976113 -465601773 -597401998 -190918173 -814384295 -742854334 -990350119 -601504428 --59417002 -616617126 -158839291 -578081239 -465948958 -115873518 -587400424 -908930955 --547409657 -303640058 --722439042 -815634119 --158839291 -436721430 --369400008 -800569351 -696812329 -199830624 -554151898 -405496033 -57874828 -149320026 -33098902 --248617160 -386940762 -204398611 -821613645 -583036779 -457851783 -913848727 -803711121 -714454830 -817047006 -93402152 --866603456 -565253535 -120990769 --544392094 -924164576 -115946654 -667836598 -137123817 --103523903 -929681966 -105468840 --249873409 -859260574 -948539536 -335145891 -666252624 -488312970 -282029064 -371399631 -833796635 -198464170 --35771313 -627455748 --855264036 -604620008 --591962073 -695592067 -710809843 -608660963 -266956558 -291110742 --390084293 -743592636 -883133875 --90822196 -709213054 -382292110 -399162717 -75832902 -722559417 --933496290 -474638012 -52150279 -35371840 --498245577 -535072335 --302835037 -939790015 -237229867 -843172544 -287258570 -48729918 -632238500 --378065191 -299891773 -23755851 --32270985 -671895469 -164223090 --831359388 -262577123 -138709942 -645011147 -910826409 --134786063 -797236648 -531323086 -671552375 -829648319 --789989481 -556116327 -961134700 --32196013 -610068661 -254772206 -822598494 -682502096 -604688346 -687651610 -681807600 -140592016 -634058733 -348866266 --604620008 -942855862 -480772798 -637165807 -702388510 -356741971 -599984648 -165357121 --667289738 -4016877 -343284267 --483392864 -810531857 -78173109 -112381614 -709487852 -247719333 --293742357 -404195087 --555954858 -501601141 -376107825 --487433105 -835109 -676459678 --594792099 -310514734 -343941615 --990443219 -161809636 --546844741 -84443694 --576019098 -615649666 -402253329 -64239855 --861009829 -615301665 --122168947 -940122166 --189141893 -554890621 -826112835 --755238395 -38329305 -1232478 -550013295 -249024456 -361852026 -580394971 -651148156 --386168995 -109445165 --794510252 -308770901 -438722866 -492347466 -262985614 -234883855 -451028182 --672878534 -740756164 --868892 -357653583 -807670492 -822259451 --491921741 -295231220 -823898103 -174042919 -474085822 --287857841 -402320889 -703632795 -456553733 -275046735 --797026827 -880114184 --281476147 -629708715 -957691287 --77261949 -856354844 -824785506 -932071489 --346501337 -833339949 -756264175 -721345894 -646631615 -433358223 -739712538 -689896205 -441501153 --610613263 -506859349 -31589651 --833796635 -478182480 -696008499 -120710247 -186319646 -286971269 --769595773 -158069660 -739055041 --355349794 -632677479 -444389490 -746081878 --643699148 -367540594 -755552643 -685975623 --793684328 -86440794 -501028608 -962153493 -395473808 --613957230 -475930250 -79191199 -676931341 -164998173 -865000327 -69274885 -99445349 -553077307 -753123890 --496353881 -693861888 -318102111 -940427122 -803249608 --661723017 -949590806 -602642983 -394554113 -825630816 -678253181 -81500523 --618102819 -25184449 -681425494 -85349441 -884115078 -271340144 -502950467 -499822147 --558260941 -169469019 --735272429 -645696568 -478211230 --299891773 -181902849 --483253876 -116649207 -899496856 -980921810 -486443208 -34490380 --563866108 -773946991 --779247566 -314704731 -830120419 -811959506 -397998327 -822731531 -11833832 -18479630 -871143142 -891402673 -170858431 -204427734 -994291309 -948877245 --924705805 -620333209 -209356683 -355177915 -290936550 -793963180 --440106542 -531340589 -128850308 -286702578 --21426301 -746769352 --992169588 -600733963 -427805595 -434446873 -993345846 -74489669 -710799612 -720831818 -291677741 --893760912 -959856892 --429610733 -692933167 --961365254 -842779406 -706182857 --456935700 -803466167 -427768080 -892588820 --599118875 -761546923 -979527564 --254434907 -321817687 -993383579 -448312171 -982879002 -507957220 -879987370 --854245816 -358103920 -619614978 -377758324 -997180089 -411786016 -248783748 -747439132 --649906052 -799234852 -379581527 --816866540 -440120813 -328615482 -954784999 -165234867 -415896390 -352489753 -70946684 --210549270 -182696537 --609864909 -408951565 -543057912 --776257890 -790422646 -460930821 --257780361 -407494490 -366216717 -467172052 --569545021 -148302878 -521291520 -808200077 -436142372 --515065790 -405628318 --6032365 -549645833 -771946688 -807643526 -772568503 -194354916 --157767388 -446669017 -727773716 --992494093 -449443916 -523178463 -535030088 -776735090 -532351821 -157197129 -762825228 -250940240 --692933167 -612699986 -500779496 -979827472 --897842029 -839119243 -27525838 --48244940 -439589078 -841601181 -820048601 --830902004 -215968865 --989475081 -310052876 --982910782 -514431444 -525264128 -743516220 -872714359 -429714698 -90340437 --180515748 -374224273 -109744812 -944987451 --243368448 -114224114 -234022504 --376097611 -967046636 -676307950 -954742900 --912129217 -456742624 -322173933 --429084590 -977134233 -662222413 -685143832 --236225593 -740711320 -358593377 -469550325 -806485825 -147576401 -288653959 -42867086 -897358915 -792919542 -717367501 -779059910 --421513215 -705321905 -179271571 -809625457 -433494147 --555659829 -856737204 -561359106 --400200709 -221413730 -238075241 -738082458 -804106277 -171869585 --642858142 -427539746 -582434757 --974986113 -281298650 -90674883 --12325672 -858657438 -834958505 --994450604 -911170795 --507957220 -625974064 -104154111 -304976763 -803567693 -428726678 --347636093 -627028579 --231298327 -683064068 --873133741 -482499775 -358176955 -162698138 -226150910 -768947156 -757945823 --144397249 -474589782 --959891921 -264293616 -493389461 --453986908 -314459092 -191145827 --633438236 -378887573 --631380659 -615573702 -824441086 -473777938 --303615680 -518648655 --191772913 -209298686 -669647445 -391511405 -118410818 -631043084 -290080375 -704548779 -787198247 -523072127 -655574421 --886169250 -690558300 --137948251 -987919726 -264832326 -1037516 --394672743 -621092591 -743635266 -564904288 -15792209 -989933576 --451288973 -872165435 --533680293 -975842731 -38832631 -442054401 -880481424 -711926078 --502950467 -258963906 -122826078 -480314889 -353216265 --808200077 -212169035 -534577316 --537922325 -681798180 -735308270 -755770991 -314124642 -361310324 --330524655 -447780569 -75872693 --950688348 -13478005 --905328290 -107670199 -940330453 --93304426 -741378000 -812934261 -472715902 -585258632 -933661696 -487061982 -827356214 -917046872 --578081239 -847071369 -952318822 -704907892 -794479023 -42029081 --253957092 -587278206 -977763572 -269936631 -959408876 --814557867 -605952486 --561975075 -687039112 --536958267 -750757260 -766521898 -189840395 --251677180 -712253869 -872999935 -370270490 -364391619 --376095773 -615851856 -723466365 -665430623 -568305314 -482388023 -878505720 --822248671 -775467772 -734592746 --664759525 -305008291 --433494147 -818130733 --661956273 -430973530 -916254211 --427935077 -502646236 -64215330 --816853388 -274363593 -671759535 -596739113 -782020764 -528547277 -408293560 --302567758 -802871082 --977763572 -435556894 --554094244 -491390224 -151108956 -218041127 -432766626 --616617126 -143177301 -69099112 -615607144 -823511644 --235326670 -306057365 --436721430 -685891212 -466973089 -527174972 --33409772 -878561119 -629386927 -843148236 -537847063 -898355339 -86239059 -818705301 -439175299 -865084370 -879552962 -75927855 --899137516 -834360479 -586304746 -527191356 -735306497 --42867086 -946332765 --301611715 -218463006 --898012489 -124215211 -482121013 -351537328 --35716469 -412737489 -782072398 --263051354 -917418507 -876699854 --205976165 -307948295 --619413599 -492329262 -873118117 -175454626 --738621177 -910395925 --504165198 -634030386 -372693291 -213695860 --729284863 -261273573 -380034481 -945466832 --72857081 -563727633 -326653794 --313656334 -394774417 -507596138 -892490915 -361420219 -726227755 --804985618 -966031446 --296890192 -893957080 -874010777 -992125034 -440139758 -419155557 -18180676 --210673223 -304574248 -901892155 --321226665 -774692539 -649596279 -448647441 --300656880 -114118742 -791528979 --830370755 -705705083 --699202496 -606324604 -585990826 -61592004 -9288286 -581295697 -923452640 -461038142 -491536985 -508769519 -162814105 -259908411 -284980814 -121966881 --521079005 -950657336 -564007502 -372956815 --878279823 -267107589 -357709092 --992158859 -360644149 -977529064 -435157458 -441190831 --348866266 -235678863 --27693681 -115595795 -569411963 --649526833 -397749685 --18180676 -316733286 -85675662 --395702645 -617992215 -923068312 --261873028 -131462306 --104009037 -620182251 -878597144 -530157939 -3312785 -829787002 --339115853 -908683392 --267107589 -998434913 -423273825 -421103676 --670234016 -212757668 -825965180 --967565251 -654387033 -355736401 --687415839 -106800332 -101498672 --123830376 -677233032 -419224140 -788985694 -298325772 --325037542 -132607088 -219492643 --29423428 -146965130 --925972370 -237410899 -450630665 -576477403 --790765111 -745094030 -257124480 -880248121 -892784333 -366133169 --119643208 -481642988 -106723266 -327511175 -245010469 --165234867 -577039643 -53777793 --964013880 -333355026 -817586120 -245529393 --637554907 -636655218 -247440954 -968248484 -277280045 -680100235 -898445961 -997669468 --975073330 -296393170 --599880563 -133050463 -292339560 -540132391 --106800332 -243714229 -306769412 -995231015 -75167111 -162652510 -324049403 -495501975 -470712476 -708336907 -230362921 -473832359 -231478911 -959975906 --253855545 -739864888 --667909863 -305886633 --448497316 -303061812 -197627188 --269691701 -483286610 -956030366 -194339257 -323454241 -576139035 --235447765 -146481672 -189319301 --184872509 -604077648 --924926527 -228618812 -543585819 --186754942 -907053608 -29069594 --802504537 -37592181 -264232388 --300988088 -249368758 -553538170 -956433645 -272370136 -962366896 -398783922 -953263468 -345657706 -838999643 --441594662 -666536594 --470779909 -840072719 -532582671 --546981367 -594871959 -135439534 -116805354 -94657975 -806713596 -113948457 -286780239 -497192799 --155590587 -883423602 -638969414 --262994726 -982603899 -798514131 --803466167 -992129896 -984821349 -43884263 -913679533 -297522512 --897477485 -617216217 -3750137 -144445172 --194554739 -211029773 -709957541 -207657818 --428726678 -733581788 --234738611 -106797113 --166451065 -278484953 --880762446 -110758561 -789583968 -204551278 --357709092 -187029069 -137751051 -781053420 --93591412 -530009209 -890076286 -534359689 -180599334 -36458926 -688482932 -982562473 -972285253 -999244149 --423698223 -257881248 -845783772 --418718059 -47732132 -112068224 --179362937 -968471719 --969134409 -951886124 --367655582 -641044218 -623180271 -546840177 -309848767 --266334332 -866766178 -142787068 -886021264 -307822012 -150351513 -299912812 -944237956 -746946567 -351399738 --917418507 -761316415 --701853342 -181562907 -494113525 --35371840 -892529759 -531092701 --359295205 -409873884 -117635259 -863632549 --532433596 -805747051 -924795683 -934185051 -886516559 -198560384 -278000194 -447225292 -158669671 -66382544 -105323093 -17548443 --164235697 -112668335 -296851686 -871251080 -173086503 -896452555 -866814400 -964133960 --598095849 -992469493 -132085510 --508362491 -105813399 -425337276 --461057662 -28637410 -261493722 --746520797 -795260049 -825441401 -854292814 --73251268 -952956586 -959414368 -418201795 -574422779 --520739205 -923706969 -11862842 -653058249 -473732552 -943664817 -155817661 -999871269 --105222294 -986720527 -357102194 --807183633 -629887604 --465028034 -300243036 -724339318 --997662257 -912895219 -445801274 --407494490 -527889499 -329874 --118153432 -558151223 -343034291 --356629582 -288443528 -505775029 --206566732 -631939858 -207740224 --979453614 -428265987 --734554376 -363012933 --660358394 -853272018 -540844475 -396877304 -327185618 --877123007 -213292002 --316733286 -993432991 -524501100 --961517029 -486361893 -160397950 -179483719 --244712972 -401274170 -448539703 -715260671 -997376617 -499892898 --859260574 -319918233 -964809050 -599420826 --272877682 -965307249 -836978190 -331942846 -963074584 --112068224 -754952831 --612699986 -92791365 -801314067 -228754452 -312248991 -4212648 --384065724 -577958845 -563356395 --54673614 -701089541 -462831831 -503200691 -598932626 -441036565 -792492210 -45520655 --141012575 -675994430 -416664806 -575436340 --934836105 -623067997 --289239272 -58773944 -995963438 -772923825 -475391141 -528045303 --695443698 -656585465 --473732552 -690200882 -220447046 -664982304 --615866503 -44594383 -808996910 -486825779 -747093007 -823236459 --716985222 -690053631 -705836631 --490446658 -263859574 -230297099 -799283682 -201122654 --268012222 -566628116 -128377509 --995963438 -669627284 -413103467 --331681190 -331012591 -512084545 -240642475 -433497297 -916390304 --908683392 -431170382 -900781762 -460838862 -108010989 --429853323 -690094431 --130477990 -339605450 -13826188 -479550815 -901835915 -616920853 --181562907 -462564430 -165155527 -947191703 --759112602 -196319888 -731786022 -883284667 -703737875 -911129351 -318083349 --243607979 -136595811 -194799554 -44284648 -41916806 -439544205 -955556545 -744207247 -779957595 -116366728 --635135348 -77268037 --643551557 -172938875 --52014788 -3203771 --676307950 -393054759 --247582957 -990929771 -543294759 --302486945 -334257547 -601353828 -601411090 -571050936 -950314786 --509348948 -571847420 -176133945 -513903527 -293306192 -994671334 -511420800 -186521060 -39268096 -2727104 -441593428 -3232822 -966995740 --639742448 -498857129 -267497573 --97047572 -763930022 -83708501 -190645779 --785799635 -475540933 -926775360 --435556894 -161066769 -511504146 -273032763 -697053808 -783505378 -201115462 --460838862 -511967596 -588539935 -162857555 -143176069 -656826834 --879978552 -187153761 -55193154 -572125800 -144387210 --799234852 -97403298 --543585819 -971892482 -448742736 -852786779 -124939933 -573835880 -481773996 -249482536 -382780177 -425676404 --926397286 -960569902 --58288203 -617821643 --374224273 -297185674 -41378445 -592499876 -474237090 --969034591 -757766635 -595403301 -553296937 -782923462 -60137267 -556437011 -213860568 -7822368 --98163187 -427586395 -639641956 -492640431 -777302595 --606668936 -289457153 -501388332 -7744904 -686143889 --514028163 -540945309 -117162121 -672152631 -484131589 -777086846 --123240599 -551522204 --846744331 -579666165 -462361452 --793161694 -473098643 -480872342 -976787743 -840491213 --605790305 -300394617 --625759861 -135639064 -396680305 -975307246 --920803667 -812693501 -805552854 -343050147 -364326379 -59497938 -525200139 --372626272 -717306776 --304976763 -484721355 -228802331 -473528699 --474085822 -114456714 -173061912 -202321519 -371877203 --884115078 -3013567 -653610646 -993299338 --229016249 -57688706 -560298248 -277513910 -89617586 -939895315 -1573146 -553455372 -137756152 -651972561 -195100011 --112668335 -220732409 --912895219 -918894795 -729578215 -107775654 -118970299 --618849791 -725172106 -731075014 -618838536 --296435924 -186765317 -814932144 --597974693 -97513928 -649578841 --768580313 -997724923 -444454331 -226307996 -980583735 -622054096 -700492824 -537590327 --948546357 -21547214 -176766964 -153885902 -382441034 -770658212 --133050463 -156914856 --954951729 -561987534 --609074998 -390934318 -902582134 -498044609 -614911454 -866375340 --706327903 -563318846 --638834117 -468141548 -477208055 --805552854 -708736124 -4708492 -464857108 -919718596 -8649794 -625716920 --23180457 -778195440 -934555862 -297834451 -483927529 -751976346 -483171212 -203586253 -98427475 -803722582 -72552834 --36458926 -486889073 -508397180 --8662126 -594760623 -725120799 --849475598 -796070668 -142395332 -10006369 -315736574 -842484 -217382523 -417377759 --87926616 -438541766 -127568831 --354993452 -627374799 -318724574 -574349941 --575436340 -625334302 -45075054 -822325058 -13885129 --456422289 -937292041 -896812411 -394593665 -277087851 --164103528 -773839244 --681106569 -681576150 -131479122 -797417108 -427698904 -992379228 -294492040 -632675455 -899929595 -776436910 --318102111 -659010160 -579971709 -623191034 -793760015 -769399193 -75525587 --355177915 -172134468 --20648445 -332910607 -613352484 -528583209 -924876690 -709512925 --523619146 -506293601 --92904397 -130630435 -154204169 -452688992 -444208204 -534253718 -407173026 -537499793 --871432212 -491841080 -154663077 -45693516 --475930250 -954345644 --890411026 -564813503 -765816075 -701858708 --435068821 -960809702 --743516220 -811539311 --460930821 -720570814 --532351821 -104858375 -126024582 --396993371 -140811389 -441561274 -672597677 -140130826 -660137905 -696002531 -274510375 -474344492 -601301782 -863147985 -592362681 -577470264 -665887979 --151108956 -79297497 -210292068 --525200139 -754022337 -511230497 --839208304 -346650289 -976302825 -814932593 -680288741 -471156602 --983069378 -461923213 -9073971 -247868762 -637863261 -552437006 --835379850 -372773812 --611631586 -921286279 -960010845 -747820714 -739231796 -805602648 --281894280 -850548431 -894806244 -644804250 -220929079 -524816304 -707540525 -517327295 -292426147 --685801097 -118746902 --825074273 -901120981 --835109 -353643520 --503535852 -127986605 --79297497 -589485895 -128556373 --106176002 -562441629 -857100397 --952318822 -65545567 --677233032 -570561714 -388839124 -561118254 --855901955 -415491063 --971353718 -977090070 --775498803 -719012301 --386940762 -195932932 -739448179 -400260954 -217194636 -901504718 --284980814 -248616656 -27675280 -273617286 -373706418 -578054774 --877497092 -348235725 -311501847 -922614553 -844435907 -152752914 --58773944 -112047848 -609014516 -552563560 -754093090 -724096894 --448312171 -550862534 --685328545 -622622057 -634712895 -952469 -987795987 -967505239 --735308270 -737510087 --498857129 -911026450 --62302863 -327548069 -586876894 --656260231 -524839746 -339493717 -835569762 --563727633 -637841848 -13639795 --762483151 -877624342 -45614406 -348827903 -617257933 -799477877 -45988707 --83708501 -641557994 --186765317 -444954770 -103481474 -248067707 -295103359 -148337703 --757766635 -868294320 --624722964 -478362289 -926643007 --549587312 -120648857 --565750930 -47267617 --867111344 -697211248 --310052876 -657365771 -947212101 -8669529 -320469877 -625280112 --816579011 -507024299 -328877729 --997872567 -226005704 --142787068 -677799815 -948793134 -576615588 --75832902 -254635168 -487931442 -105550197 --556437011 -959716259 -856042636 --592865800 -866408382 --851992018 -607201831 -820058222 -879130460 -582877498 -521575142 -810920083 --531323086 -564792790 --659010160 -968558621 -984165445 -632499725 --320469877 -317955358 -416347049 -338564771 -348988747 --343552851 -374881549 --143197486 -144674254 -687528674 -876026122 --950765395 -221774119 -620946846 -428551368 -156006579 -355318097 --265889497 -506553905 -215786731 -60713261 --529348509 -979195850 -505157442 --642442848 -694287918 -776348195 -823770614 --398783922 -92649281 --261493722 -903465313 -422063642 -871411451 -160367444 -534910531 -404863908 -52112985 -826768850 --9522372 -183146607 -574013002 -227441025 -529060132 -48201297 -466589535 -965693883 -296683386 --258908239 -606569581 --892216376 -863165535 -911996099 -476087934 --16076845 -728933394 -924309170 --639037060 -893950614 --749028084 -129337889 -623906350 --464928769 -284110255 --823898103 -483818702 -544927931 -219246017 --89689672 -398124750 --595255920 -372714239 --226307996 -683355383 --996574138 -550665772 -499498144 --18148506 -844245052 -992324580 --351028622 -65052853 --432401291 -726777976 --350255348 -357290739 -592135911 -513990081 -270169860 -437928684 --947237135 -9961830 --390643398 -375244139 --124939933 -111005457 -837269515 --636655218 -94009609 -671025890 -264073153 -851176257 --579971709 -490282367 -874090384 -918159052 -943736672 -126885061 -380070603 --944451442 -233531344 --921225966 -113513494 -380848285 -733048885 -708957234 -666979640 -740349840 --782438278 -874880119 -218863076 -422982400 --772290025 -429216712 -298724519 -556324628 --292426147 -182885510 -480048654 -916309506 -251133113 --779195718 -383095317 -252006413 -78426364 --524839746 -777650199 -452891894 -619470433 -469721367 -600039870 --661233434 -816478272 --598932626 -114511917 -923278053 -561360493 -445089841 -126572114 -437807489 -666965315 --560447249 -8561267 -783636345 -509116694 -684990631 -222696660 -286534620 --466589535 -247194034 --565253535 -602236940 -484172181 --358176955 -409218798 -986777185 --740576159 -454162526 -810179184 --390640770 -118049194 --738274451 -755749271 -453331457 -808420958 -548744636 -887513204 -200706769 --148337703 -171533221 -491292676 -668328037 -645457377 -650737351 --941784069 -698224387 -562931020 --782007076 -648919259 -111941821 -402458375 -544269041 -811797822 -355943274 -140220114 -649877976 -558169613 --38230799 -849735593 -661832249 -339969322 -233664320 -790699660 -949076794 --215786731 -342637397 -104612033 --764736391 -982628891 -763622027 -599960505 --208719193 -869731734 --929319741 -674347037 --644071419 -848184461 -542266208 -388254080 -771326412 --737510087 -778944392 -828591064 -253584443 -875318671 -944888504 -694757994 -788667382 -663310154 -353821785 -434236974 -73210852 -646627113 --594457860 -649901660 --894650969 -835263340 -811157440 --8123542 -39208031 -524141900 --602236940 -237194120 -929707090 -70031760 --928706099 -162181024 -106113088 --403462772 -824551520 -146969634 --328615482 -677996089 --423794400 -642669072 -451406868 --667847672 -145649480 -171240390 -849130306 -234767877 --282846223 -844716085 --695021533 -94505031 -218394942 -386082012 -542547315 -834109281 -206164551 -532727675 -393760804 -123105268 --723466365 -87230594 -361096543 -501502706 -231625285 --136172268 -266042551 --444954770 -306830058 -160366597 -771745992 -798469691 -814238146 -266651515 -48483415 -188827369 -321975899 -118640948 --329874 -910517843 --188011626 -711868627 -367745136 --585296734 -633416203 -291244948 -386271426 -759527567 --711843056 -521252437 -505531947 --695618598 -889927695 -485619840 -865923330 -546193244 -321968528 -83786013 -382015838 -271115965 -463912606 -945633371 --434395083 -825478962 -648231704 -604529817 -367006551 -454692002 -479334706 -826394626 -957863660 -683555291 -300506215 --450630665 -132890181 --710809843 -664502821 -428850226 -21518547 -737259841 --382561376 -380795985 --1573146 -159064783 -815485152 -339378477 -124927021 -413054124 --828740269 -504554018 -983398376 --78482838 -676374400 -395058404 -713313528 -208116593 --643816168 -237225178 -988069339 --193635719 -465943855 -424232660 -429902067 -592913130 -475318593 --767606140 -36431247 -852318346 -984086635 -969204789 -609347255 --915279572 -728679619 -889858565 -93488743 --944853376 -190367477 -175439271 -517479966 -246286045 --82310563 -868777617 -981668671 -4637976 --34490380 -943054765 --803495068 -362954492 -770246319 --697037627 -606773859 -833663377 -351549877 -436632753 --479553226 -788501487 --577029119 -523592971 -944655736 -460577444 -225914813 -647580197 -305420618 --52150279 -257091301 -58836449 -37841840 -984570332 -58633751 -71760227 -407513799 --513943796 -797652703 --142764365 -408095562 -344868807 -897847722 -412683941 -501662577 -110841603 --647838749 -527527672 -109897075 -886498115 --583472199 -680269958 -361511346 --822731531 -709181728 -230822632 -681198054 -488413322 -36767669 --507024299 -262464840 --950475655 -629001245 -111914299 -694098929 --574013002 -622901080 -753904775 -713314363 -209653963 -567201339 --914971250 -452903739 --244152282 -985973424 --956535642 -512915412 -649438431 --649438431 -215865168 -163997267 -846220110 -637455614 -581172349 --480048654 -631272813 --751976346 -798642369 --363012933 -49594787 -289820755 -818941821 -36070846 -63489834 -680824987 -266446057 --327669846 -292238568 -785066164 -19410495 -92282789 -754794634 --112404249 -81533224 --247719333 -601821261 -632855976 -795738793 -16423193 -664091756 -206620793 -377621776 --168876014 -885514202 -690398631 --759763999 -269466529 -164563529 -924481630 --460577444 -315652451 -163403604 -227339804 -425537318 -37220930 -165654023 --111914299 -464371108 -931717130 --441593428 -262040956 -24874942 -169551149 --48483415 -638553102 --305886633 -646226195 -243405923 -203908843 -670818728 --8649794 -807166826 -599903626 -264536036 -975577550 -388590378 -434593515 -257141897 -464290155 --886313981 -925746735 -760610600 --380259303 -901145306 --973643583 -111230188 -376132438 -18411625 -327781343 -266191979 --901504718 -978902221 -979199833 -609780012 --516514462 -341495247 -965520975 -67398838 -269178788 -784724598 --593701073 -373842498 -246672984 --143177301 -421291398 -65661010 -941378522 --796483772 -757615157 -57292166 -855790568 --696974946 -541346294 -715746304 -226785256 --265721064 -690165689 -503286056 --945010326 -895928799 -34998955 -233036671 -96771710 -907544727 -77188594 -280271807 --582764101 -418506389 -447401634 -818952990 -849824549 -130691852 -743641260 --782287493 -435149495 -279669858 --777863631 -385399353 -584787229 --474344492 -610459591 -407832946 --849824549 -620774913 -480133939 -666635492 -611586507 -276424491 --854431374 -409158669 -747090488 -178007029 -61157622 --442781247 -864124825 -903897528 --110841603 -58905174 -956816509 -781293539 -17991405 -539617946 --447401634 -675983609 -615911467 -991020500 --85467612 -192210878 -840388299 --875318671 -848546774 --264073153 -628831402 -874548735 -817383893 --508827898 -106694965 -971040174 -224618935 -154283886 --278000194 -556622299 -422802727 -993127331 -306079673 -74204533 -49494295 -136128232 -953829038 -269456098 -927677271 -764902680 -850967997 -972988588 -640868136 -439468176 -435494119 -736387733 -607894559 -35705896 -57280031 -971107013 -527058733 --165480397 -158688706 --536736577 -663091620 -742558677 -577282874 -3149063 -475349424 --815844052 -476770154 -951246904 -66087744 -721287185 --421057083 -907474405 -580948150 --118049194 -519628310 -743142967 --882149908 -128230160 -446346065 -282910939 -290413666 -176677608 -959752660 --694296593 -202377370 -216970650 -749906547 --452339660 -19308977 -912778661 -579147420 -127297530 --578556490 -68708207 -327799397 -506637262 -194096466 -270909612 -166481297 -334460796 -832142405 -730382753 -827949442 -66464475 --422063642 -935309006 -740295537 -869436667 --795260049 -267731854 --892588820 -155757428 --293959235 -695931309 -909965225 -649304648 --95489838 -514556635 -820049439 --892048919 -339822395 -42240591 -180189836 -782994601 -272973459 -62934291 -377335971 --965307249 -141899678 --521252437 -451078074 -819929235 --106542815 -944789276 -697010105 -830039905 --78327398 -823369515 --797236648 -984299417 -506838935 -164927081 --158688706 -715133071 -537564261 --356813244 -688521350 -40019691 -422399466 -348382397 -6396937 -501226744 -104211279 -653739200 --694560798 -536582779 -539338153 --303061812 -498486929 -883647664 -974328819 -629993233 -853482227 --66382544 -70815793 -710165440 -724581832 --477795027 -448358161 --912635651 -209616213 --45988707 -127477425 -995124963 --581172349 -849170913 -187152765 -543287309 -311130087 -854875069 --275119030 -9057446 -93891299 --51267630 -556267059 -686285816 -45441947 --648629027 -477860422 --412737489 -322863787 --697053808 -676357901 --479406431 -95067262 -526619224 -896896482 -949434981 -203212651 -532791690 --772224850 -772111529 --866375340 -596709564 -319282904 -317158632 --372773812 -289435623 -282151251 --807758087 -793297377 -771326058 --904791039 -150309837 -582001026 -166046919 -818160815 -706380261 --137096104 -43546697 -110129511 -792918479 --446399918 -257742931 -237792549 -630854926 --602456290 -238793279 -831227898 -291027373 --283055863 -609339956 -129502135 -823440037 -269001060 -513031902 -757189184 -892037654 -762086442 --57280031 -555275654 --965520975 -845574657 --747090488 -940909657 -75065670 --892563279 -53800964 -806226474 -267097986 --632304658 -480258930 -971511576 -890619483 -571322937 --590005674 -569462245 -131898899 -40678834 -240626469 -94316090 --249024456 -122026979 -108003082 -10105321 -954692554 --693666684 -810027171 -649090938 --107775654 -579494243 -35627759 -409381555 -288419942 -944163779 -976675097 -41410887 -384869634 --601301782 -349424866 -816562571 -772474812 -395212354 -567189774 -408006505 -490492447 -790063857 --509284186 -493568813 -339639211 --524816304 -206861832 -233692432 -606925307 -587760060 -513748484 --929442912 -491355631 -867608635 -647515822 --448358161 -31844544 --132212965 -170737488 -663596158 -124765420 -877745353 --186319646 -780290529 -957028620 -350136492 --915857465 -818542991 --991020500 -399305142 -713427932 --596739113 -701951791 -914525735 -148257567 --478869343 -78825076 -823058948 -683120807 -781442713 --666536594 -224318419 -287863884 --914880748 -325593268 --75872693 -341943058 -619982110 --288653959 -292003948 --943054765 -940512040 --863269749 -234704600 -599880309 -826025252 -243888557 -203792039 -991357581 -569055419 -772028936 -487271271 --254635168 -532751243 -30376183 -46204781 -575048188 --858562794 -216450942 --131859382 -312905750 -906802130 -107520609 --649901660 -79807008 -307837125 -875687582 -683484023 -139180189 -834429942 --382780177 -950524082 --466705606 -457763309 -881991256 -71562908 -183419362 -392029775 -751056805 -478511468 --382668421 -450995047 -366254101 -618379162 -340250146 -562720441 --347099110 -652539385 -663755594 --245945101 -259814162 --527889499 -367555450 -655839968 -473480636 -71366605 -664280564 -605544864 -921242683 -126686799 -985303923 -109432530 -205137460 -232238775 -467107982 -140443714 -16490219 -48204482 --297834451 -235165193 -957676302 -113425868 -978465637 -508956107 -403876759 -230189457 -181290884 -470013277 -704965851 -423357887 -582987346 -369538476 -473595158 -948921776 -852774426 -908584048 --508640882 -69188203 -815318994 -959514831 --665336890 -368006834 -782046587 -705235229 --21518547 -965281800 -657300893 -791171683 --115873518 -366110707 -723782902 -266620861 -305726 -595985319 --112321876 -579483066 --544927931 -340334421 --30705672 -515758370 -395971134 -814113938 --26622009 -269809527 -571308191 -785901774 --309215416 -726567271 -857216571 -87559453 --506838935 -270901525 --735060790 -703179832 -864223040 -808374369 -924850039 -377183265 -510206295 -632774960 --623396911 -868875015 -173105023 -677632043 -576408275 --539508455 -847483431 -671803124 -764168300 -282659628 -521681009 --984821349 -376486806 -45966384 -632291857 -461784001 -493004832 --473098643 -448700579 -216738906 -872434747 -595691329 -784071914 --436751631 -832122443 -332717924 -150914743 --650595048 -749323975 -72537307 -308921229 -211254430 -715441959 -365668986 -298217672 -155843769 -537973678 --531774226 -432789414 -991799468 -867488989 -352698019 -587572489 -758172492 -459823416 --527046655 -837795236 --476309441 -913660752 -700872310 -324259608 -449395636 -773611533 -261532915 -465230351 --838216411 -565954458 -451222731 -830271288 --975933965 -766756167 --619470433 -554522653 -863881113 -950611772 -574033532 --118496244 -347716789 --123105268 -885452999 -966971291 --362606255 -508470399 -649191724 --932071489 -722305362 --859502412 -352866466 --784071914 -767313416 --830271288 -615807299 --26973911 -989779161 --941851850 -71677455 -741944666 --369178722 -318657563 -862379272 --38832631 -708076261 -15879254 --106113088 -658972509 -556456509 -350739211 -178101370 -520087013 --542547315 -74709940 -85113523 --842690958 -919558034 --762825228 -539702671 --749906547 -789952773 --160366597 -181153906 -243158316 --157197129 -895316906 --24874942 -822293598 -863775893 -53322230 -504594235 -731477446 --919718596 -354072885 -895663799 --482499775 -685868447 --533064750 -65545874 --916390304 -315464355 -494412507 -846950919 -145236017 -628831821 -929272294 --328877729 -760204407 --787198247 -329942609 -110774478 -822817103 -57956785 -111288292 -562155269 -820092204 -875746577 -969583718 -228073357 -439404685 -161120864 -666000113 --136740850 -631937573 -105362846 -198604858 --960809702 -710435747 -260497717 --561118254 -497302471 --747906833 -147221760 -834666489 -861186057 --691429760 -558254430 -881061511 -693363715 -955333536 -388367210 --476579065 -188245405 -33996085 -334153568 -658322608 -154897439 -75822697 --482121013 -140022162 -551576526 -298679527 --863775893 -698348816 --992185329 -367572910 -621530839 -338112191 -533013836 --397200064 -999070494 -772280661 -568354469 -23723308 -655910829 -281212805 --392700747 -866374838 -826514131 -501521924 -906034973 --627455748 -548423362 -521874801 --812792253 -323457258 -5565284 -981589543 -333139241 -141284343 -503191634 -35895564 --739712538 -170269606 -824774122 --706380261 -435487019 -476527164 -373712363 -673912768 --636267314 -381912740 --771326412 -811060496 --652000089 -198054013 -605636229 -120318587 -60138798 --629386927 -920231925 --113998078 -436770498 -734907208 -239607721 -545492592 -521796453 -386525700 -846229870 --226967834 -322382160 -928556307 -246736524 -575437088 -544784472 -312202807 -227725648 --367541556 -679701423 -703323815 -718375844 -902064358 -473480202 -914759832 -496037491 --695592067 -165333601 -648139003 --656826834 -754606733 -93532442 -593275009 -288219385 --647482043 -478833850 -690240811 -786590085 --472894539 -512643720 -887135887 -738390894 -137699530 -694312935 -255417622 -126570312 --613037879 -205228841 -458078882 -573430075 -673171333 --743142967 -53807868 -863027159 -631677145 -809250276 -703980409 -894186691 --535030088 -973219741 -888100089 -723768318 -826995252 --683120807 -707389098 -969912720 --206240807 -43033949 --259839591 -340719023 -604794557 -323164678 -260448352 -848647958 -389910956 -841372324 --146965130 -253712401 -227848803 -678127151 -880372104 -297429988 -93092814 -320758115 --99181317 -187190152 -234977507 --407113903 -969988319 -787105375 -227473595 -761090662 -827532662 --109445165 -570163933 -102155844 -585421611 -7713022 --658322608 -343088611 -428853763 -509720298 --587400424 -427747276 --145087874 -41982343 -769313063 -745290363 -88959186 --114576433 -276660186 -335733030 -473082862 -410778919 -645840918 --635541788 -235180888 --118562091 -891261567 -943636274 -925614856 -179210425 --266307666 -777457790 -907085797 --617216217 -383505177 -858515037 -314572437 -502690010 -771770875 -314816664 -552265458 -881572661 -167233602 -133104360 -51011140 -251270350 --722559417 -359313240 --229025781 -353352163 -764263991 --270169860 -792049745 --213097673 -930161514 --109406247 -910058425 -517510782 -830436303 -809595522 -457684068 --333139241 -331589558 --780930128 -401149405 -688839797 -906363928 -778980571 --269456098 -529200901 --286702578 -461411165 --630854926 -250131778 --661832249 -182024651 -958406787 -63436492 -796882013 -43087850 -769423200 -746376582 -422167220 -505998787 -759674104 -154807536 --319976873 -306553195 --538350758 -910111922 --571322937 -541127801 --600562085 -272915032 --691303533 -528372044 --757945823 -691041902 -225795874 --421876399 -536872154 -389702790 -15953448 --2727104 -713097155 -336047020 -431867991 -794125685 -451333069 -985266256 -84981879 -953342966 -528447164 --77282036 -45515968 -784517999 -827743108 -317244017 --757912996 -384444034 --65545567 -530904221 --917286380 -861522401 -961349433 -623700820 -260091727 -750626742 --843799012 -62238823 -95093351 -236796521 --897358915 -391611117 -978583192 --94505031 -405204405 --364326379 -9520785 -112107303 -782968928 -499731887 -390582105 -252314331 -376838245 -608763760 --783505378 -896181541 --776436910 -733533391 -595027411 -189766067 -605410450 -222646063 --451018012 -347207045 -971848508 -70107115 -556211819 -940226218 --907474405 -457361393 -275454887 -479689619 -6117093 -187580833 -234787503 -968686758 --547735110 -978216032 -790360796 --79807008 -239606853 --353352163 -75124309 -103196431 -792133935 -204570069 -407530559 -798617560 -3292012 -13872518 --706182857 -844499576 -333911539 -917544492 -494279431 -266674476 -573061213 -740887095 -730260900 --515488676 -836666085 -231331940 --845168531 -684412566 -54743086 -442295112 -234019110 -480432168 -66158239 -705492879 -967334662 -501938352 -500093672 --996020186 -407000863 -551289479 -908339126 --761644605 -89878368 -439982115 -638366492 -215061881 -818074314 -367221152 --938400350 -311669550 -382685492 -583981935 --338973838 -911327477 -835539680 -886596726 --395971134 -618115708 --694858005 -142685740 -235863960 -59770208 --152827121 -450071856 --287258570 -259589538 -715522826 -600213207 --975842731 -785749130 --648139003 -812481217 -491127637 -671944544 --330064891 -888556917 --44284648 -550887615 -859468181 -383359897 -255256956 -257538887 --934185051 -92323390 --272370136 -370593683 -11391595 --512814040 -840767507 --194799554 -836468186 -52998787 -21800995 -583667510 -204210302 -785096987 -425505576 -179223891 -316752374 -615449986 --227744012 -553092586 --144445172 -382243536 --606773859 -432005672 -532178037 --185577616 -789771782 -677063489 -513006552 -350511758 -368545165 -992855858 -748424154 -923414855 -560325251 -453082212 --27950879 -36270002 -86280859 -31290669 -407597614 -339055071 -94601652 -814815783 -160577716 -951574882 -157873644 -995729304 -89590749 --903379644 -956368622 -901450777 -47936281 -991455740 -192064948 -889483100 -924585647 -113079804 -819640870 --995232891 -629630305 -591971817 -416618140 --463916942 -495716271 --789680952 -952290591 --340719023 -754008300 --574349941 -923156973 -916163943 -168467367 --613931233 -59794706 --900782041 -922371268 -931073076 -834464917 -558281917 --480133939 -748120940 --977134233 -648446542 -842533127 -149756983 -123177466 -205131300 -638125580 --108010989 -504216174 -475015374 -811006790 -265354206 --708265281 -439944146 --396877304 -199604825 -384707997 -361560164 -726889811 -474736925 -610977557 -714845998 -932072370 -273742808 -91329802 -810206710 -857929006 -487786012 --343048165 -659691424 -373308615 --351812083 -877322121 -679379931 -230018825 -82923977 -205821452 --211755364 -207714960 -586870625 -92143261 --959408876 -447125151 -235507351 -37460085 -592575708 -403254730 -927394773 -194060132 --525478666 -335615894 --499892898 -978204245 --586489999 -36307217 --690053631 -927654524 -256694269 -746059678 -857816191 -902545995 -676851773 -4815937 --403031094 -581925261 -320330412 -248100758 -551570944 --771745992 -30965260 -350871067 -876708126 -371775286 --232349977 -511725627 -422312482 -19352082 -72865128 -479121306 -45622082 -217244336 -137369009 --744207247 -887951418 -305876949 -255367396 -664963473 -672687408 -208511711 -327052578 -931376933 --743635266 -460182475 -784812251 -64289250 -446766201 -767255758 -261105219 -333300520 --381849627 -207847001 -405956465 -490750506 -622051825 -748542219 -656262134 -898424007 -686368518 -632253032 -207348423 -309672294 --365046820 -264350340 -645019174 -632171996 --742558677 -163910087 --721080865 -553020461 -967210908 -750840227 -696972515 --587112154 -317319484 -136247690 --379009655 -621978268 -903736908 -65543495 --849170913 -775125332 -418495918 -505852318 -136090224 --481773996 -464568207 -378348801 --728679619 -79214874 -103909081 -357051649 -339091860 -400652609 --874880119 -986975676 -308752907 -471023950 -73191554 -407418123 -923801867 -869821846 -790498439 --847071369 -551999340 -831400003 --714311667 -345571985 -32363723 --593204535 -304132128 --992469493 -107001097 -474741181 -67279969 -894650716 --798642369 -353032361 -109186498 -50359789 -623679500 -916165781 -957384694 -336894254 -478025524 --508626440 -31661620 --879552962 -923286026 --825965180 -279249608 -916326429 -37588267 -668001124 -948563103 -822079254 -708345182 --685868447 -649903601 -296370992 -438734216 --892784333 -921304668 -142810681 -418345147 -828509873 -170990689 -28667727 -843161125 --441240738 -279118596 -828973765 -994916951 -257745727 -255098204 --272488282 -650205046 --719134914 -442366446 -109261199 --451028182 -428174561 -600528135 -713936845 -597086267 -373864416 -98582840 -970622269 -426576200 -177959278 -438571284 -583765717 -961255237 -660699251 --305008291 -524835204 -998202557 -209662529 --758435676 -413029402 -249535374 -885298317 -175725320 -572156158 -94883519 --381394326 -530563960 -490906331 -466867649 -452990486 -105733153 --81533224 -289292507 --814062843 -185508689 -490879760 -515495171 -714999853 -173670697 -621398218 --102155844 -99804729 -729623421 -321146901 -119840233 --37588267 -962793530 -986392509 --732136292 -941770120 -866744927 -421026176 --248832888 -989669239 -723873153 -491711116 --107520609 -622380679 --669647445 -309224940 -588713322 --410764808 -490022287 -182896965 -264863434 --195100011 -308501590 -537448243 --829648319 -166632500 --639641956 -319913008 -827404226 -407502193 --313842501 -378234855 -841565744 -22279757 -398382943 --526592055 -568626812 -231182852 -222038819 -522918601 -154095194 -456595933 -848373865 --769423200 -85733544 -544749493 -645520801 --994671334 -474999074 --254078838 -803586227 -544829619 -525003759 -558000675 --967670099 -225352822 -848870373 --225380138 -41966187 -215435533 --863147985 -376201040 -192783296 --558000675 -960860240 --55193154 -642425698 --142395332 -450326399 --667463764 -902878742 -185074627 -505456616 --629887604 -315378698 --252818391 -903082939 --632855976 -837544982 -358742618 --728183783 -49422983 -699959780 -498948067 --227848803 -191296671 --677632043 -592644485 -446016367 --478054694 -414735013 -734062184 -733503457 -985621720 -929185385 --456075581 -752712251 -637048617 --852442878 -774435672 -207778253 -416125316 -623882324 -419995448 -222979507 -624974886 -998780210 -914165560 -963309691 -578269718 --136247690 -695965794 --856737204 -482581002 -26065371 -326883454 -269988707 --170707984 -266685706 --990745951 -227779743 -348399099 -329346717 --610459591 -338128191 -514412576 --590380730 -479959849 -901477050 -461891873 -700698486 --521300161 -46779078 --713936845 -995548744 -950517355 --800569351 -420648601 -60944847 -251856979 -660039205 --418336151 -447690729 --202583678 -196547390 -706019131 -503678874 -516186985 -496706783 --764674586 -245021193 -425040660 -790415482 -502563685 -444685487 -110636460 --76975516 -893371119 -282383988 -91700733 --811060496 -543689389 --155843769 -833425264 --277513910 -29822070 -949654512 -643070437 -722230803 -852623880 -668237000 --844758828 -398287265 --152886156 -185523639 -836371552 -373608816 -673677810 -466065585 -409060948 -496891556 -645080644 -481353005 -942302583 --600528135 -561473357 --202952816 -827954803 --878163672 -491797681 --857816191 -74687200 --338783881 -440410722 --464757150 -482524750 --241073440 -186648546 --144674254 -353137186 -599097224 --995237894 -353308549 -521323697 -376133017 -146300092 -124344002 --866408382 -692383734 --435494119 -900368661 -511765593 -957885737 --93442077 -217145965 --288443528 -424506093 -607066078 -249750109 --42240591 -522526640 -588205418 -733754828 --71366605 -250976263 --554890621 -898136401 -805807536 -740211443 --212757272 -502574486 -735462462 -456642615 -141408045 -878885558 -146359759 -296457136 -789062760 --938422653 -452776063 -106494390 -641500819 -628478441 --453082212 -421425197 -61005963 -680987283 -646766441 -583761272 --16635884 -984980512 -313155101 -413509733 --400540414 -889436496 -795071511 -16198158 -163041924 -117752628 -175726783 -775625281 --773701774 -586136285 -307241017 -231983288 -555309481 --178901261 -84119989 -936334025 --708336907 -502557161 --192707665 -416185425 --428853763 -18867301 -197241188 -803759841 -770286380 -80577401 -586145226 -423686117 --886596726 -632046988 --890326928 -487744080 --824551520 -905502636 -410373061 -270454119 -552097965 -655519238 --420797217 -512395780 -191209274 --68708207 -286219333 --117635259 -24853381 -380913910 --194871780 -629037403 -83523113 -568676959 -825109713 -248838241 --486980428 -259649513 -891173737 -559146202 -740793561 --219492643 -31825750 -236630667 -600188547 --393054759 -65972636 -807466955 -283702763 --824758013 -588165004 -142998704 -454873713 --943636274 -375789322 -452131758 -57473124 -165583484 -438851611 -994320792 --831518290 -446028015 -329437956 -70217552 -867256260 -829777382 --329437956 -338158363 -610449715 -348755321 -950927983 --24853381 -844257707 -584722108 -847033894 -674146858 --274821219 -992664719 -512922475 --640630100 -591467569 -643072392 --995231015 -89352493 --61740116 -916820653 -217908419 -385532860 --93611089 -419385078 -262388073 --644804250 -817499404 --586778977 -488064621 -637776988 --820640077 -652983549 -654548891 --138178962 -748809238 --428384676 -907116786 -928395396 --869821846 -873477490 -287851595 -962838105 --833882018 -459187085 -905919097 --725120799 -547345778 -239449917 --593997892 -464143083 -313360873 -972644129 --490596547 -417644428 -119953461 -819089148 -799672486 -210794763 -126145921 -613790873 -667215504 --620592824 -388725873 -714582930 --891402673 -663746272 --609674755 -677261244 --870616067 -719870394 -413715395 -287271256 --77862376 -490042424 -36835876 -945474277 -958295568 -848628795 --78426364 -781605545 -507837112 --848546774 -193610221 -859240669 -931374180 -679348545 -845920119 --490282367 -268871470 -514190302 --921304668 -997053540 --849965449 -323735952 --355522553 -397996428 -565716983 -13597549 --886077344 -406558984 -461253986 --843148236 -468931157 -117680436 -790343332 --811959506 -225506430 -676411959 -294066173 --251270350 -824408604 -880548999 --216970650 -443415332 -258635919 -855002889 -109703574 -814945557 -25755331 --435788673 -387557545 -184801880 --212093113 -545137108 --744922760 -451724497 -769235204 --980640209 -716002268 -264484833 -230495159 --897847722 -958244920 -67340172 -380556860 --833425264 -783602122 -533252806 -456489843 --582001026 -762266286 --175439271 -120924573 -546542782 --913453216 -365660324 -136080066 --99760804 -859128370 -772489260 --148409444 -874123029 -535944372 --641500819 -57721553 -443347206 -287520306 --772280661 -840378078 -489637629 --31464923 -709931936 -566047523 --977767996 -587768069 -771107855 -989170015 -27071216 --366216717 -949690887 -615905188 -235945050 -534524533 -976055323 -685183244 -466827553 -913829517 -123004599 -777066402 --881061511 -385210295 --486889073 -109115725 --552563560 -301399319 -28742086 -183089622 -837241201 -781665080 -266875321 --131898899 -175593283 -161741345 --267497573 -243861191 --67353037 -705473307 --581799108 -174071760 -410505708 -515169644 -502222464 -623915982 -418268130 -855474228 --703632795 -975849913 --179223891 -861427905 -913666731 -349011664 -904032348 --571308191 -24996570 -618070141 --155817661 -642137117 -94659155 -120797073 -77305335 --823001091 -26038192 -668239516 --392304932 -418300232 -165625599 --81874860 -846428898 -289445195 -428867806 --817979629 -287835245 -758467875 -923056574 -453507921 --360864347 -503793640 -504412264 -59993859 -574533299 --882987521 -454077021 -161137910 -903297408 -409562217 -831717781 -678380168 -451174015 -701721927 --868777617 -223309412 -524312076 -670717722 -894050648 -340867652 -841257734 -103520425 --352866466 -234845172 -773939627 -184874511 -372023685 --283335586 -939570836 -359129071 -932859463 -483166159 --444389490 -975458450 -739299258 --240594093 -697069607 -189621570 -526768754 --351824049 -974342444 -180730103 -141836652 -834351589 -562100702 --348399099 -557860046 -993250903 -857590243 -36007002 -482401696 --345420074 -171813021 --982603899 -451232210 -962814654 -274016686 -260662535 -83451231 -627966845 --479121306 -813808969 -619040231 -336633605 -140278888 -147889748 -353744120 --427539746 -424288574 -554535058 --49944542 -124834645 -429518828 -654874701 --970844876 -250083358 --787335491 -257844136 -470969489 --269001060 -341895426 -72466703 -263612818 -210095181 -736437133 -11041465 --341895426 -55898564 -111796430 --351537328 -301236295 --377621776 -191722333 -139529552 -63632875 -122108633 -753371056 --388590378 -559932928 --276010884 -13397909 -457725700 -370432064 -709071028 -411691760 -160867517 --908930955 -592521124 -311099440 -446673538 --839367322 -646797088 --264076917 -53165161 -735735664 -425722144 -15035536 --511725627 -945854242 -8003708 -956560297 -860322343 -88229983 -975068490 -427677486 --405496033 -988785023 -772249398 -908208611 -238415494 -957119681 -672572780 -61512154 --264886744 -421330405 --270390490 -263478507 -138050584 -214661250 --708981813 -807090745 --495716271 -465219596 -10295756 -937211843 --754606733 -164058090 --108650471 -623963254 -469473940 -573355206 -972542261 --166131023 -887999706 --314459092 -874126302 -969069244 --473528699 -380830433 --175454626 -635616926 -615783025 -837385460 -413387947 --568305314 -980654784 --556804615 -713348149 -469587917 -626561039 -182468785 -592405362 -266161301 --645840918 -698237276 --869436667 -280602925 -269003149 -178867246 -487136752 -789491403 --701951791 -460737859 -774678167 -964599472 -391346913 --190645779 -100959918 -225514986 --188245405 -193533884 -982132400 -759811184 -468270591 -202023695 -104023702 -66292516 -400050860 -826964853 -778599918 -891931617 -204204931 --310485008 -745506875 -462617994 -475016715 -143123608 --360608983 -465652448 -946546127 -493646167 -673550854 -319239984 -595742054 --975458450 -146495663 -135698265 --5565284 -53492392 -911577710 -966006238 -893777677 -69722790 -682536996 -928441833 -169494532 -481684038 -206462419 -536720004 --781143499 -60940953 -865120452 -496337734 -820170570 --23192089 -642520900 -582894489 -433160306 -893242189 -212392733 -300680817 --472715902 -517220905 --181153906 -850551638 --910058425 -319228603 -962765578 --562441629 -99265560 -906499454 -776737719 -119612583 -614338910 -174836747 -665912641 -696758711 -334008760 -321665048 -496146081 --822259451 -199302042 -881783521 -551317763 --88919299 -625401161 -159878353 -951887331 --758210602 -277333322 -909198957 -554166951 -345446172 --353308549 -477175177 --925614856 -429645851 -14695913 -938390469 -668703416 -773821008 -446414570 -976959953 -73892143 -74307471 --739636777 -263614680 -929013465 -971414466 --944655736 -856096940 -192386216 -431936970 --821789120 -600640788 -110553692 -51374166 --494279431 -420393570 -891521765 -797671889 --613352484 -96734166 --262397536 -302816394 -691494329 -882132731 -503571400 -285782476 -966458042 -522734516 -886102866 -717583714 -858138039 --304132128 -374866959 -909258553 -853235929 -268194835 -514469324 -450719560 -283287459 --382685492 -56191639 -455032683 -683174661 --73177187 -431186952 -740684993 -381376086 -655037325 --186648546 -171373146 -611026829 -487318118 -982234720 --180189836 -653622890 -581141934 --458876312 -565938270 --436824334 -19207245 -557605347 -382073508 -888319540 --872773932 -720267259 -290723234 -200886451 -69180833 -795896315 --404863908 -396873641 --940020779 -16019479 -550866678 --192210878 -959981072 -60747941 --860621733 -572356172 --478025524 -36459207 --57473124 -560305004 -461007388 -494631957 -345279237 -945068229 --582540028 -531918545 --751278757 -49384918 -600667811 --638208201 -469013147 -444522260 -456440605 -89793290 -269062199 --290723234 -663676512 -204601553 -786367260 -457049833 -775252661 --36307217 -676656558 -674378570 -447342438 --805349737 -633832264 -796605462 --549801095 -157833830 -176873018 -283565393 --796871654 -970060153 -246164627 -48763953 -525039411 -15038726 --418999833 -993491660 -860326840 -783258481 --694764054 -410065489 -878247821 -882621181 -937143178 -903277003 -730670278 --143123608 -113109723 -174773069 --427698904 -854595474 -86278551 -979960155 --689896205 -510860192 -395133669 -994869808 -576564305 -534793934 --833663377 -614632403 -900391674 -513800998 -805652548 -338353928 -44717282 --267914582 -452303182 --740211443 -14218567 -221284593 -124968059 -916803810 -529703207 -900563647 --869731734 -830101350 --453813239 -577862132 -257195545 -905941616 -370153062 --580948150 -442029316 -421134433 -524090904 --784403670 -620987123 -260848260 --15038726 -714163727 --409060948 -519441445 -352893644 -753785743 -198192509 --502563685 -359342562 -804277432 -234083408 -602652170 -94316354 --485592032 -741311387 --292003948 -545314342 -237721737 -683914975 -156618731 -948011744 -642279253 -781700580 -549848301 --254772206 -302275753 -387348880 --583036779 -396375466 -86252313 -548486854 -772509248 -837584667 -105340260 --351494259 -867008512 -480592035 --900368661 -702264093 --175725320 -690648377 -521033926 -747115112 -376457743 -561329746 -38523269 --401149405 -722809553 -998444998 --478833850 -418644594 --744985767 -199569885 --979883440 -495784248 -15041088 -833560437 --546500241 -459700689 -97702270 --631677145 -610311953 -451222293 -343435736 -948909352 --832122443 -901203774 -948012655 --810531857 -898583418 -797753096 --982368663 -954491694 -119674397 -711697047 -637774766 --31661620 -897779008 -19507811 -896117347 --74472646 -160227426 --434445412 -301578912 -247627021 -760174197 -482811448 --697733091 -113551447 -559872567 -422309773 -967222648 --964195945 -105928833 --24801118 -629899527 -718175423 -186885918 -699062733 -402253335 --327799397 -252507964 --718476284 -27049231 -740983202 -167776074 -742557467 -755268147 -121894430 -619167871 -70409878 --523280553 -984762910 -487451577 -566951266 -638742397 -113287616 --456595933 -197308742 -846640302 -493217689 -491172440 --381244382 -868507823 -651701690 -454055303 -82301023 -209430274 -49812379 -627990172 -714087648 -418249894 --196014231 -68822532 --944163779 -241176733 --641780579 -91096595 -929536714 -488459249 -821676898 -367794868 -490326150 -214927784 -874881178 -270310743 -448193927 --79965842 -521190454 -739380036 -635285684 --985303923 -657065472 --158669671 -257185194 -589415831 -718300336 --685991032 -805907589 --671552375 -687646423 -503225977 -468257254 --564904288 -21678971 --699959780 -577059312 -717930571 -138327564 -749515103 --61512154 -782414979 -686794349 -799816330 -515373357 -38649733 -130111912 -546823467 -655426293 -172071505 --660699251 -433746214 --971848508 -381453828 -603196299 --943487007 -82846596 -956238166 -886769173 --944789276 -756372920 -442999901 --220929079 -647296910 --3149063 -426150796 -147486711 -252451744 -892870920 -327994243 -547676722 --294066173 -855125631 -960503865 -240824489 --755932780 -859348998 --648171973 -215659041 -645201705 --500612828 -170586540 --217145965 -698244016 -373330190 -934293633 -864214248 -499464406 -294723558 -40299958 -450736963 --646766441 -724872146 --644184489 -600817303 -964761928 --941761385 -781377055 -603356566 -736866772 -688484977 -247526928 -124763423 -85090872 -902502253 -463783529 -780738342 --286219333 -672598370 -878135279 -807495302 -461850919 --486443208 -227659666 --36767669 -519879445 -361951649 -930206205 -508859916 --357102194 -310448414 -278949536 -390992194 --221432513 -335719600 -782249075 --36070846 -836002918 -410269271 --90674883 -403525882 -100981394 --66614728 -539361439 -61936520 -344043357 -621148845 --881572661 -89105453 --544269041 -852195502 -970826253 -600566349 -352592695 -840710198 -953220603 -393009350 -519692372 -846871861 -18756311 -836513848 --505649609 -888940078 --105362846 -153696777 --112381614 -811767838 -61217124 -806828520 -994538148 -366289242 -678220996 -946870753 -473733523 -412948737 -759209363 --886340666 -606896463 --844241539 -822236135 -97157063 -187929502 -851191502 -561160608 -242301175 -985816826 -943708734 -97431196 -507921651 -44008314 --585136354 -351606954 --582655480 -542877168 --484841725 -330431701 -628847066 --928241036 -671066080 --374866959 -60764640 -142389701 --731204961 -197342431 -173801007 -881596735 -502018144 -15968508 --766521898 -792094002 -913347795 -517324691 --84071905 -121489936 -889595844 -945191223 --247627021 -133732905 -169399299 --110758561 -559805819 -123536770 --857216571 -713773295 -135855304 -527152990 --2179056 -102662768 -740711955 -740380615 --150104520 -967263156 -686337343 --741614936 -372772158 -706287290 -989011540 -536677017 -84681742 -676298558 -359763272 -835895233 --907544727 -180751972 --948877245 -691048234 -279506177 -136375640 -952581277 --954345644 -895757215 -136893828 -544704100 --237410899 -145295780 -244715144 -987408578 -286606907 -524787521 --521796453 -818435988 --442295112 -506556228 -595185564 -979471699 --826289680 -193437457 -305782154 --231989624 -636231527 -847571096 --662207590 -305449807 -816976370 --452776063 -483944731 -837698652 --994955602 -667206661 --795738793 -242046504 --299233737 -846556274 -992556881 --852786779 -913121378 -225354861 -490442880 --778599918 -81215829 -541572903 -472441504 -844870573 --525039411 -578922705 -619769904 -4573809 -799379422 -330267482 -883075859 -145567787 -969442976 -743643756 -683511472 -955068805 --71956281 -65874313 --49494295 -409775507 -197108700 -119059796 -438542024 -1551415 -480220147 --132085510 -543648787 --207847001 -172647187 -464106789 -708951918 --523072127 -231824558 --310448414 -2250227 -14795247 -960087116 -802963403 --619982110 -46026696 -340969333 --924315404 -662254166 -768029324 -861391375 -643174032 -851042772 --568122835 -856097635 --16393476 -182041702 -491119818 -131600193 --301236295 -780317839 -425855772 --913666731 -750164748 -503056368 -544818640 -724517010 -984406466 -970789918 --706602644 -304971724 -265607854 -612370349 --17548443 -858613370 -117432049 -463555537 --178867246 -956039599 -171908880 -446703102 --570163933 -172979132 --262577123 -519113909 -241961296 -917963142 --204551278 -53802113 -883265342 -358016243 --306634720 -436380897 --145295780 -584764320 -441597227 -903476582 -695295100 -897129007 -326645568 --992601884 -853730668 -926212540 -538531988 --611026829 -553819095 -553283953 -525282292 --67340172 -257210647 -324800974 --316763763 -552534581 -534747556 --260497717 -285980032 --221477822 -565168491 -694895069 -22746406 -404147236 --573234280 -816409643 -4010827 --749635185 -780078956 -372806290 -325810386 -81509200 -188862410 --848184461 -368012758 -121724769 --933450402 -326564978 -121848185 -591817056 --128230160 -211847236 -817141626 --814113938 -44378292 -517443761 -709382792 -771583174 -366506401 -833562545 --74709940 -107578481 -770569719 -708089253 -57087259 -506440403 -597582323 -967746868 -450612073 -692505962 -756707242 -457646599 --585990826 -451444055 --839768297 -482945649 -198383932 -306974537 --778445477 -247171623 -846220991 -562996676 -715387951 --936352798 -700726698 -141481649 -493609675 -390574690 --110129511 -662404770 -896537523 --99965641 -777758619 --371926222 -991232449 -874901633 -870121204 -15473422 -513777022 -764403615 -739506780 --371775286 -322702043 --478034586 -830423700 -965194478 --974328819 -408913871 --582877498 -724819890 -492632955 -61175664 -989881930 -972385296 -947075592 -755086750 -943521837 --45614406 -251708619 --786025868 -351886201 -509612997 -497484663 -386278163 -198867467 -450670044 --614632403 -375954678 -933463317 -976866372 -589389119 --965461000 -7605359 --304744390 -911845322 -136913792 -114893245 -874429802 -270728786 -314674009 -810831946 -10443242 --530903676 -20502436 -455703775 --422071640 -876012601 --140811389 -372340490 -595645262 -699231178 -140876141 --16423193 -72459810 -381861491 --922614553 -631007548 -391186795 -741779289 --599420826 -904551790 --151584684 -322302429 -182055463 -926766387 -861360802 --302987396 -205882710 --828148130 -229128250 -8863065 -759576087 -255035253 --138709942 -14485607 -891380320 -311295127 -700899570 -504119930 --306830058 -40961934 -321035161 -838526172 -336858040 --815318994 -354051974 --614338910 -651387016 -765987755 -31505644 -77806928 -768798934 --96734166 -899282369 -664611753 -324701295 --576925320 -649841207 -583597829 -817560244 -215836057 -569297861 -80217988 -309135767 -83473258 -381783526 -103302596 --204601553 -642915959 -532202950 -711110289 -134392050 --903465313 -89825959 -419414261 --898017986 -602501408 -962467811 -191286260 -364635802 -991781359 --646797088 -186273868 -374762340 -400603100 -27901745 -410927749 -549667595 -797438649 -102666653 -42949833 -621323809 --623191034 -543558562 -825970167 --592405362 -797310217 -66240910 -3856002 -916862471 -968701634 -298151696 -466195754 -348553649 -617235633 -813997861 --461923213 -279038853 --691048234 -971357117 --452399718 -208977429 -669216861 --988785023 -245060062 -805753932 -583732926 --52112985 -292310518 --617185640 -245311267 -843262259 -839026108 --512084545 -171487751 -720869569 -55708777 -791421082 -602158065 -65659143 -95523332 -61965890 -813375999 -283752268 -161420460 -639601420 --894650716 -992700751 --359977420 -739422848 -690325717 -652474844 -357125253 -60743650 -921388371 -674302700 -78138860 -335160442 -166765724 -156556247 --793760015 -92468905 -930238258 -28582884 -688909003 -324157479 -938940323 -638565414 -782373370 -197415004 --683452092 -475847410 -431731859 -128175952 -489240379 -130823349 -139933694 --260662535 -316620412 -317131591 --517220905 -634069848 --867690731 -383340939 -843668481 -411596816 -376326796 --669664431 -227391600 -783252908 --668703416 -954146475 -799742163 -230336812 -348728653 --370593683 -714603082 -84035771 -27890889 --441190831 -577383549 -343234534 --102666653 -719297472 -232198780 -582871536 -140304673 --475110018 -680425815 -628116767 -662813205 --463881843 -162113593 --964133960 -705021763 --113425868 -649499466 -484919881 -432108475 -635917631 -554722432 -41861411 -58108423 -661748624 --185340322 -37310761 --748809238 -547297899 --239079132 -746233683 -175076619 --693861888 -838146803 --656262134 -520469138 -873505246 -907889903 -666703233 -635980087 -699723335 -665760560 -206733114 -993901946 -745212317 --588539935 -183183433 -274465183 -573030960 -126273713 -370458110 -761311849 --490906331 -575002241 -645077344 -807549267 -881894367 -869389909 -475946893 --228073357 -778853499 --302816394 -111546596 --297381628 -939703975 -845777193 -722138161 -485569447 -622761684 -891679657 -401443663 -85295931 --134842042 -2706811 -235562579 -247308562 -505986700 --31825750 -71655219 -299148109 --548423362 -823314933 --690868098 -145701887 --109744812 -340508121 --648231704 -621521157 -701056026 --944987451 -644570078 --848379027 -195138658 -216320936 --813808969 -781165488 --939152472 -268823809 -129926932 --11833832 -614333518 -988088179 -301258974 -743612990 -374735211 -861609729 -641029250 -631583018 --647326105 -316386602 --908339126 -237940435 -635561632 --950611772 -449651674 -14292756 --262985614 -891389787 -837713311 -94631003 -273526206 -262866159 --586870625 -900114462 --855002889 -368905912 --390828499 -98432664 -525765393 --425572771 -419860793 -814988801 -523437398 -405829386 --422312482 -352633894 -819028469 -753181243 -336493641 -732549284 -57144680 --426150796 -602165901 -14724733 -91942048 --326653703 -347417730 -947788260 --885514202 -281421736 --369538476 -431268138 -407808293 -396113643 -458725281 --475540933 -256760853 -749401436 -463147665 -572321039 --2250227 -564363368 -128404927 --215061881 -656227336 --155446998 -176698988 -655542859 -278129842 --597409852 -420868054 --438722866 -637383889 -479314552 -584140528 -575929384 -768661920 --876699854 -574171161 --787283402 -256695489 -723803659 -166698933 -542993484 -387003535 --355736401 -329299207 -658198626 -246371658 -716279630 -548732519 -286961082 --301294195 -94254690 -933521756 -947568401 -777865029 --799379422 -102859061 -940845663 --634658942 -853817756 -777198185 --67279969 -940828736 -458182820 -751702134 -326692552 -290023023 -116915824 -434844456 -25665253 -692046685 --388281274 -565986487 -583502505 -337903748 -655984750 --496037491 -293155500 -142036540 --923156973 -158002853 --257210647 -452992804 --726777976 -514330606 -431297402 -893882131 --256760853 -987610756 -954351403 -910325235 --418506389 -141041615 -96784529 -241212905 -91606806 -469121238 --479689619 -458737358 -258862961 -300092219 --713314363 -504143404 -907566602 --357290739 -379946949 -843916492 -258451888 -749041351 -589760944 -747575353 -485307241 -361907571 --252083568 -988542317 -544412589 -785861186 -915729472 -906615137 -599237417 --344043357 -463656884 -638103854 -288097445 -580427289 -257821831 -82026547 -550981149 -584059628 -848211069 -408094754 -210488234 -947990703 -269443394 --901892155 -898382073 -831843499 --725102949 -501540167 -1637503 -541800779 --322795338 -603969512 -111769864 --907091697 -766824216 -316102248 --483818702 -766309083 -111492695 -508562372 -48171407 -143026623 -962700791 --507921651 -730940517 --494696688 -455752546 -189228235 -214539704 -974220909 --324157479 -992950856 -946669218 -702728086 -785257516 --975307246 -513947455 -998540652 --927394773 -626062223 -482599174 -826186831 -235997534 -749731655 --367379704 -815953587 -976612994 -814552631 -528547029 -643168000 --404147236 -558435207 -162012760 -383233778 -760414945 -303185906 -410605409 -339427158 -823845997 -576765502 --32963141 -645789824 --827356214 -114039573 -788185511 -609544824 -612838056 -351882008 -182491112 -168321115 -662519544 -920496641 -548869329 --534651094 -463540504 -663835846 -132458449 -596399321 -354310873 -440303383 -4279137 --17654973 -464556192 --311214308 -571481210 -351318449 -419224345 -176042945 --617821643 -845741469 --95067262 -762076186 --865923330 -100113384 -661997654 --746233683 -589999580 --37841840 -870829686 -603352068 -839897819 --974342444 -202392617 -480738660 -439484935 -824403781 -167514117 --698229961 -354957049 -37618662 -80069943 --517167668 -439713605 -355049354 -431152734 -170582807 -286803017 -145864224 -938624469 -325449460 -524335610 --684276557 -530658083 -560450926 -357965195 -548556045 -367268681 -956017622 --708076261 -217598617 -982823198 --708341124 -190023250 -852171437 -661538822 -485486810 -662967616 -513051907 --881312797 -914212393 --978822897 -159760960 --777363563 -700195923 -455864057 -439064118 -965968080 --867008512 -490498718 -339654883 -354232541 -829596569 -869148136 -632339982 --739055041 -933521862 -746618222 -350498430 -919428742 -583845517 -125867484 --792751892 -592720712 --92282789 -482477472 -973095234 --266956558 -30757503 -738620076 -508443630 -474363932 -841154794 -615822059 -493263968 -961785712 --690094431 -515739189 -384510501 --806485825 -353476596 -669904464 -260440345 -469394954 --686022383 -156738108 -421810115 --236352437 -69783242 -431286617 --9961830 -887115281 -923456682 --530904221 -649809794 -426325239 -680794432 -218658590 -589043922 -626923738 -559054609 --130630435 -113832869 -154488797 --994538148 -36480276 -900115636 --492632955 -304119317 --754093090 -924594279 -996164545 --708920240 -318129600 -933995971 --843776170 -136157761 -832694733 -164250392 -925713061 -992067178 --561987534 -270845505 --257821831 -202634820 --491711116 -230436 -285531521 -290025118 -895987617 -408951466 -745249739 --950314786 -938660051 -207167339 -948304087 --865133807 -201146298 --144387210 -507283525 -469734768 -654842345 -115719450 --608449512 -401330108 -979052810 -453516305 --845777193 -836020598 --533770528 -758871489 -515295833 --980654784 -222914587 -590944284 --491390224 -135916571 -674383471 -702264103 -500576403 -361995532 -955464965 -852282161 -932893641 --475318593 -839624671 --216069311 -573074704 --598289616 -127515190 -973648373 --248100758 -259887843 --504143404 -974839500 -336112330 -172439441 -104127041 -729351435 -67000550 -21052183 --992125034 -446610383 -971401923 -654658785 --740793561 -218586984 --322162344 -434330339 -560381552 --445089841 -192772231 -324231224 -814197096 --998540652 -644872520 -321915648 --94657975 -959308672 -546693515 --698224066 -257216666 -233952262 --972542261 -173155303 -422898721 --714087648 -463963953 -888179607 --803467150 -674618422 -853790798 --348988747 -31841110 --979052810 -1389495 -644557952 -882995567 -464259173 -364730012 -340912985 -869709935 -991648940 --954491694 -821769134 --2758317 -974803658 --61592004 -33819922 --572468480 -820288604 --494444585 -105104743 -579224156 -231206701 -805150522 -100294156 -217287685 --921242683 -164294119 --230362921 -107888503 -998237909 -55064081 -627481382 -163534422 -458308437 --652474844 -996266003 -502682417 -555486178 -190315183 -903956798 -625229985 -599614241 --761878178 -470720979 --699231178 -563501929 --62674866 -696939252 -448682984 -911298576 -270566742 -476329696 --724096894 -88821739 -359909951 -715959418 -229532401 -599331258 -681094931 -650469750 -791061448 --817213233 -132328014 -487461564 -472016749 --551317763 -377428927 -763700514 --608660963 -66344522 -720728752 -62053798 -63573276 -816665302 --559146202 -988096771 -73937399 -335123138 --247308562 -970377694 --599960505 -828514217 -971230130 -591445983 -68673321 -586221271 --126911983 -631115922 -293139121 --859856504 -612092540 -181231899 --233692432 -560580796 -179896037 -66113069 -962804334 --175076619 -362075209 -523825793 -106618037 -839931285 -3660799 --308770901 -637400541 -516488051 --747313013 -893611402 -873566075 --304691636 -460373492 -963389033 -608358568 -741791684 -789750010 -483225247 --558281917 -964516809 --399799834 -356206185 -289531211 -148405217 --111492695 -793136765 --217382523 -40645439 -780085997 -33345591 --266674476 -241493785 -960516155 -347359334 -797188482 --617235633 -177607326 --168609016 -917840276 -172100580 --968686758 -27787597 -256708137 --421330405 -211897874 -207657246 --988304402 -995023886 -364513013 -342720071 --937211843 -627950149 -484046000 -427469685 -79353194 -929229521 -898139432 --661538822 -420025841 --233952262 -728822086 -146291595 --420025841 -28958741 -665925158 -993015682 --156738108 -425364448 -125670247 -387824589 -460269154 -133835543 --72459810 -734282630 -910042987 -502395961 -378146741 -937610631 -379914097 -202186584 -534032908 -24376025 -313203281 -383280858 -404672268 -14021708 --901145306 -268820335 --544818640 -416244511 -628851442 --927408274 -16781759 --723803659 -948047380 -438486374 -55903886 -452828225 -137049182 -499110246 -985958319 -23878249 -252320649 -876190950 --494598824 -607024705 -990864945 -238353009 -555985202 -146790830 -827699733 -654259231 -435737698 -175866766 -132850832 -909265698 -832279357 -631121357 --131600193 -129125014 -305674752 -79783170 -186751167 -598955348 --735735664 -230223351 --389702790 -700763264 -709299813 --58905174 -532523716 -251372664 -322054219 -733283968 -778543284 --891190080 -588421008 --851191502 -521482212 -943723975 -598932002 -804465832 -928603880 --206164551 -167156107 -287445403 -12916121 -835676298 --426145590 -412671075 -831318474 --820058222 -86505572 --810179184 -596315510 -659514180 --704907892 -586490945 --85675662 -18534977 --83248332 -151189264 --675595637 -386522202 --243861191 -724822098 -618099790 -462497472 --876856329 -937056989 -130446752 --73892143 -655456437 -435084795 -258254012 --866766178 -97856871 -700683031 -460022853 -879504666 -605170581 -605122576 --797188482 -207011193 --278484953 -594671305 -914245958 -798118253 -811227317 -984606932 --22305038 -269059254 -924087295 -225790635 -247486227 -2407237 -520277279 --600213207 -323486124 -8902177 -766418519 -537557000 --61217124 -510438356 -625961664 -337097220 -536388419 -441035531 -592791865 --352348949 -767621178 -132061526 --15035536 -119705193 --836002918 -144101072 --366133169 -740204387 -601497820 -908292540 -931886165 --227576556 -753622278 -308512288 -601550245 -934607616 -201558341 -354188007 --167233602 -506334492 -312365873 --405868287 -122702326 --875730570 -355164962 --723416020 -587764882 -955646683 -910182638 -451173819 -649014857 -383941847 --812950338 -366557307 -362155590 -770157254 -243318741 -664711391 -101602539 --112369183 -348896793 --817141626 -276884295 --45622082 -869974453 --905941616 -880441608 --104858375 -274584221 --712491543 -614980123 --437807489 -38646296 -365708010 --784724598 -491305325 --694386264 -91875227 -806111925 --447225292 -327995857 -297472518 -590411539 --780085997 -132795910 -168106556 -968682404 -330616446 -759905842 --163997267 -516159335 --817047006 -760009559 -751086375 --650737351 -36753367 -388934576 -20386663 --285196332 -650621408 -883811275 -41153304 -462798010 --136595811 -964066755 -62142596 -484611790 -463513930 -607682095 -736394420 -955467606 -746292568 --523592971 -195892397 --702264093 -292349124 --122108633 -437769395 -991770222 -587105848 -794891824 -16229829 --247868762 -723959794 --762086442 -652548291 --956475996 -721513859 -494100431 -777890524 -344830995 -400764436 --126273713 -172414467 --186716964 -417040800 -451400476 -8673086 -703753136 -823254582 -422024608 -13914048 -333091096 -445210608 -575437029 --226005704 -429748177 --241176733 -767283844 -79810785 -899187570 -856862106 -807386998 -943153454 -77997543 -81804548 -34658838 -857273801 -289394902 -406283114 --872038789 -397470132 -561095442 --113414589 -914127945 -428968572 --457684068 -435864845 -364745955 -873959044 -480649572 -965319759 -23564046 --308512288 -189451671 -238060082 --48305037 -105983 -140637568 --777457790 -683302813 -645884877 -241466320 -731086426 -676140379 -116430427 --987408578 -551606947 -522435434 --918418702 -649034920 --637863261 -865659354 -582110876 --696157232 -223557934 -31240274 --340508121 -963436925 -560213590 -145684341 -368165000 --672597677 -756403936 -135191646 -932949816 --647296910 -217074384 -369329414 -35539247 --503056368 -123094671 --789438414 -888165042 --638103854 -856531162 --823440037 -823552346 --373864416 -181909011 --609264585 -378529776 -91982063 -82012169 -611618486 -51594582 -273217206 -192068748 -271228947 --644557952 -365340198 --112580434 -474342744 -142828698 -298923891 --266651515 -6730454 --672572780 -240799995 -71293198 -763875003 -816616511 -599226360 -706513250 -362305661 --79214874 -522939101 -353524256 -878306954 --463798816 -386504071 -876813313 -555830483 -407616755 --331849412 -790564303 --289820755 -468227709 -603451196 -648581379 -65459307 -425680165 --81215829 -519525808 -384057436 --615301665 -968937904 -554774553 -198938451 -126277658 --189451671 -600494901 --64457612 -154881759 -223029966 -194600949 --26933777 -223018837 -473830435 -761084661 -488133577 --318083349 -696590771 --835263340 -129406514 -640557711 -952792932 --600469439 -665922934 --23924067 -698157404 --923056574 -705126946 --950881718 -837569137 -812276604 --420393570 -339388487 --978216032 -782752502 -142467349 -700035951 -579890006 --482388023 -256731510 --441501153 -250405509 --643072392 -326481319 -391181937 -224616926 --8561267 -304132271 -238397748 --202934190 -921868793 -23523160 -359757767 --509612997 -66925096 -893488366 -341047219 -938928314 -750228587 -785452459 -67405609 --263859574 -300613248 --439064118 -210572732 --567189774 -414980654 -781938410 -831516729 -324957510 -684094693 --535072335 -685278114 --75165813 -983347301 --293139121 -438182444 --506553905 -509279458 -191391395 -186154186 --104890743 -952656908 -390303181 --55898564 -44320609 -318191113 -590311382 -630225867 -566316881 -844411130 --597582323 -831070698 --447125151 -508333706 -341586642 -847129528 -130952033 -729688789 -165335170 -980757870 -929597041 -206073853 -823082164 -785325554 -624617263 -929893075 --527486678 -328457358 -408291985 -3321074 -132859363 --414980654 -181851917 -590620954 -520762696 --395473808 -591841845 -193843540 --618099790 -721525886 --964066755 -421563558 -417372158 -161466127 --306914024 -866222423 -668863226 -238788169 -625414149 -190186518 --457974552 -241480706 --655574421 -274892065 -577956828 --441911682 -893147805 -26905509 -993370301 --231983288 -472039324 -730688710 --922773236 -408697448 --930206205 -815945716 -647880208 -888559586 --872557835 -225035390 -513175150 -934399250 -146595457 -959538708 -273224623 --846229870 -742128389 --15473422 -648290140 -419233654 -117061271 -255805859 -914901428 -645009699 -986640437 --600667811 -325508250 -315571023 -365746106 -658944505 -233656728 -497199483 --717562393 -379317086 -509772983 -908405660 --717306776 -246556970 --811312220 -176357937 -446160038 --187699383 -306712775 -927359365 -840129119 -943226987 -9807556 -355228923 -597756762 -275092260 -594350989 -390770856 -308547268 -649329541 --966971291 -483953600 -416956865 -175983449 -484777624 --971401923 -247067625 -167743860 -77210702 -870731971 -405445540 -896586083 --307948295 -968938720 -502975846 -747956348 -71537050 -402250735 -565429704 -528706256 --319239984 -3016322 -192945788 -395664811 -309655224 -362380150 -674117631 --175866766 -165437208 -79882310 -522738557 --671025890 -87179994 -459934928 -856355200 -586955499 --930733162 -928737619 --476770154 -230825564 -579775600 -970910570 -169264290 -32672277 -813394510 -161598821 --327994243 -951213538 -679137725 -312736685 -953500047 --995450508 -205012751 -94786119 --802205029 -103658047 --693985711 -482530818 -945223719 --140443714 -358646143 -510807610 -815567308 -153906556 --666000113 -944918122 -253174361 --999244149 -584049638 -488474711 -955308835 --534910531 -25484087 -724358844 -32460774 --978204245 -750754668 -173615870 -26783113 -29564537 -788155049 -226148704 --510438356 -61129938 -487726809 -414092230 -198510222 -583445903 -935367556 -895229706 -436147804 --161640203 -604547610 --313203281 -773106153 -43219603 -518740605 -122794711 -974861532 -69293514 --98078390 -73750575 -516672711 -912835406 -111938917 -430897484 --893242189 -190269684 --531753916 -928098367 -433378941 -491715561 -831385276 -588755241 -678165281 --451232210 -92228941 -671670029 --590565392 -9315279 --127568831 -774558872 -292421097 -169762893 --610068661 -583758558 --915729472 -718088944 --119953461 -418829146 -752622468 -688707315 --653061760 -459941438 -251434948 -335382428 -813370137 -309164825 -514247522 --534253718 -502106465 -883408481 --831717781 -50154703 --569297861 -230067110 -144334405 --729623421 -596296595 -583864444 --225812696 -228513562 -13510975 -635499120 --956238166 -245959059 --471156602 -458419608 --491841080 -171915791 -855381915 -516426994 -320640667 -874159427 -548810042 -28401900 -417592780 -210547150 --532727675 -595565639 --182468785 -158319277 -679969264 --789952773 -483702171 -688454405 -321483210 -441664423 -842682331 -101342025 -274249942 -387789871 -305800431 -584060656 --908208611 -556744465 --906802130 -835621646 --814932593 -247389156 -691541496 -226578788 -728696942 --883284667 -871673897 --264232388 -141688054 -364378359 -879723157 -917246935 -295935387 -434699379 -925801004 --185897267 -524342163 -389394831 -411449264 -777100514 --887557589 -260455319 -511978163 -192652260 --659691424 -53171139 -499751403 --683555291 -76373010 -489193859 -150536261 -434395539 --152746720 -422305661 --710009899 -861549361 -192277656 -710820075 -236311966 -699332998 -525565333 -684366668 -450810872 --253209404 -719856241 --701056026 -517483216 -192698780 -545301799 -966490200 -446760147 --410108338 -246981193 -73529151 --791695224 -727742367 -491361879 --242685631 -120686017 -745127396 -149220521 -502681617 -216887330 -970473965 -23880562 --910517843 -506724826 -542967348 -383310727 -349349763 --224918149 -28873667 -340670190 -157774961 -590394961 -38366770 -677264992 -820050680 -244730918 -103414972 --113713401 -906899619 -76168510 -274048708 -261473076 -702365466 -887833111 -661061297 --480649572 -526876546 -825106269 -709211496 -281630503 -148121907 -263642838 --620987123 -247168490 -299892619 -868697783 -778688628 -908987365 -909823629 -758198096 -455263426 --578795897 -188424291 -755208280 -772201537 --872165435 -970320327 -997195993 -371173892 --550862534 -117395250 -120428928 -493730916 -281680805 -112227618 -117907695 -443231281 -848740574 -442329887 -135890912 -215803373 -663874739 -198180581 -919759982 -783903035 -262836759 --317086229 -501712501 -620283282 --479334706 -272013663 -205953793 --170990689 -443645502 --24904589 -365029508 -706503608 -19980779 -579465909 --177357005 -348187827 -76132935 -358458551 -542815079 --482477472 -558204294 -789918966 -17180042 --395133669 -245974547 -427540493 -369520866 -186415547 -578053440 --530658083 -858181558 --951887331 -43654724 -826330117 -496829674 -956687546 -697009892 --727773716 -540656224 -985513480 -264597339 -361148907 -855385016 -437148831 -430346409 -887789054 --826112835 -30458214 --714845998 -954271251 -673534449 -613798233 -923007646 -678972902 -887620818 -845122644 --580394971 -648872205 --389394831 -855548908 --955308835 -471260755 --680613344 -283030913 -801114529 -439537677 --475391141 -686711833 --643070437 -745177599 --974573579 -520799710 -693003416 -18946765 -822565205 --501540167 -706841666 -183854665 --548810042 -379989589 -962518561 -607975664 -539151217 -724152440 -523632796 --30757503 -270108989 -776536115 --643168000 -621288316 -466849336 -84721857 -651895519 -987700492 --516488051 -6118871 -729000049 -16911501 --959933305 -79980954 -775018367 -88102124 -693064717 --124763423 -922584151 --191286260 -715397632 -74932642 --771583174 -709468194 -31731439 -608214702 --132061526 -417825539 -677853643 -983991993 -862868306 -245423163 --37618662 -761048952 -130867486 --317319484 -887461734 -948655904 --693003416 -78281840 -572236418 --120051531 -483923163 --51562495 -838839860 --125217142 -312280051 -754153058 -490956959 --559805819 -601957666 -732013793 --41982343 -560727630 -229603255 --670818728 -237155076 -273588860 --845574657 -503069174 --92987557 -732603138 --332717924 -659034399 -41631160 -142354512 -241461152 -193755610 -62324734 --84611256 -70393439 --953342966 -365472096 --950255259 -842319745 -561691858 -585644956 -137199630 --14795247 -138211350 -654740555 -873316841 -19450682 -500126331 -268449866 -496302486 -620119860 -592739289 -569084205 -355186163 -713615016 -455868169 --878306954 -786099439 -536472621 -509949962 -164334626 --637774766 -84246618 -129107133 -654117223 --910325235 -891536821 --669627284 -36527584 --41378445 -107822146 --856062681 -710778329 -619165435 -353110760 -702580325 -582781462 --945068229 -177417378 --76373010 -712435537 -210208436 -109863629 -556145662 --757257026 -901247843 --136090224 -781952946 -920051480 --524090904 -424900558 -614223206 --69188203 -232367478 -709430535 --705492879 -66289987 -324922834 -992649240 -243334229 -369723033 -933637459 -332740798 --523312690 -480620297 -691965575 --555309481 -655346533 -692207186 --638565414 -279903288 -138008041 --339055071 -392763400 -880013038 --542488961 -887915332 --537499793 -971758970 --344868807 -804128323 -943737147 --777302595 -681747084 --281630503 -412009623 -928066752 -965864915 -904785988 -419497546 -631407336 -14512405 -914744823 --964516809 -93864902 --247168490 -878784633 -282425521 --337170486 -954004576 --684685470 -416505596 -8157024 -755702307 -309396972 --484721355 -197902403 --259887843 -401632008 --749732582 -216717615 -929103011 -192877781 -242920149 -10355903 -518862057 --893802698 -880668364 --649304648 -418396531 -775525478 -784404696 -839882897 -535452768 --496891556 -506564505 -198091956 -793521610 -119387599 -606138897 -844809101 -380779404 --13397909 -96771425 -236199810 --726889811 -846678028 -793785431 -536576503 -201253111 -537050888 -907760765 --943737147 -671171810 -722349023 -987778986 -291523968 -590701742 -493848343 --962765578 -915347719 -958671487 --180004268 -583061734 --778980571 -407898404 -796893529 -968994804 --128377509 -645639851 -325470611 -655730542 -357238309 -471392435 --84243154 -859985418 -488314449 -303893343 -104813747 --433497297 -128720355 -87457744 --418300232 -504931539 --908910278 -38955971 --86280859 -777917803 -494730120 -53864268 -627248336 -867796898 -448655513 -390753649 -710565110 --564818587 -49874444 -271493183 -158449942 --150309837 -470671703 --315775794 -794274998 -515370854 -937849944 --32672277 -722469651 --614911454 -952960596 --625414149 -674909300 -515122382 -266148733 -442250309 -615734391 --224616926 -84486152 -448102235 -749168832 --247526928 -311694507 -885429783 -695863631 -815427946 -656340496 -269714768 -675508085 --758467875 -213307415 -352214175 --62142596 -931315020 -963487794 -271245602 -695853613 --502681617 -355564996 --105340260 -197166823 --468450425 -268736907 -344832929 -531879373 -879919668 -887821223 --609780012 -858506105 -757460089 --391381749 -96441234 --655702646 -297916694 -177906224 --545310186 -835856631 -434144193 --327723159 -253181621 -817765369 --957863660 --95523332 --232367478 --364635802 --22603366 --990834910 --491127637 --1389495 --16781759 --454873713 --581765743 --826186831 --798118253 --53165161 --924795683 --7658811 --592521124 --23523160 --255805859 --207348423 --258451888 --146481672 --92228941 --49384918 --183419362 --498044609 --988069339 --10355903 --786099439 --158454842 --953500047 --544784472 --923068312 --142389833 --160397950 --264597339 --390992194 --748424154 --64215330 --26783113 --854292814 --309135767 --145649480 --954271251 --193437457 --242920149 --995283297 --349424866 --646087654 --220400396 --470712476 --491536985 --182896965 --750459420 --556324628 --189683259 --554791198 --473832359 --394642226 --419750643 --929185385 --637383889 --711697047 --473480636 --615573702 --416125316 --772028936 --412683941 --965194478 --973095234 --569404337 --241252544 --739299258 --227725648 --937056989 --805150522 --14512405 --731477446 --191145827 --836468186 --674146858 --680783492 --405978022 --570561714 --311295127 --759658829 --983398376 --906363928 --503286056 --619993006 --320073064 --730382753 --598955348 --902878742 --169494532 --875746577 --741791684 --205599414 --222914587 --131462306 --234019110 --764168300 --147576401 --90568819 --169551149 --580776043 --623225525 --992855858 --571050936 --208362332 --843670030 --84443694 --487136752 --386504071 --756403936 --322173933 --880441608 --137979274 --416405706 --610084109 --932072370 --847968846 --218394942 --224618935 --246672984 --348382397 --682502096 --226785256 --89692032 --604794557 --423758102 --505986700 --790343332 --274465183 --234083408 --404672268 --903897528 --135639064 --703323815 --327140032 --881991256 --556744465 --513800998 --958295568 --235507351 --850551638 --579483066 --586876894 --123094671 --207657818 --400260954 --899187570 --308921229 --311099440 --435737698 --438303012 --865939725 --33098902 --968560716 --230067110 --376457743 --659034399 --234641100 --717148981 --323164678 --524342163 --245529393 --780078956 --833339949 --848740574 --503069174 --333355026 --181290884 --530009209 --521874801 --487931442 --839931285 --873717845 --390569950 --473830435 --355186163 --270909612 --476527164 --629993233 --55708777 --204830580 --706513250 --199830624 --177607326 --524312076 --749401436 --997724923 --942302583 --463147665 --182354505 --142236800 --121848185 --624617263 --326653794 --156006579 --361310324 --54408206 --35627759 --746292568 --964577241 --590411539 --788155049 --44378292 --451333069 --672257247 --506724826 --189047844 --903297408 --585421611 --972644129 --705016801 --952960596 --89590749 --501028608 --753123890 --993015682 --13826188 --173061912 --502682417 --368165000 --607894559 --286027006 --755702307 --473021466 --343050147 --294270271 --985816826 --458737358 --217197154 --318129600 --572236418 --365708010 --757615157 --906034973 --21052183 --205882710 --934399250 --837405011 --325888132 --810206710 --811157440 --553092586 --880013038 --520799710 --75927855 --213307415 --678542570 --710399027 --377182429 --924850039 --515765161 --904785988 --717147219 --681443179 --197108700 --4016877 --66158239 --93881134 --635616926 --84119989 --419414261 --383310727 --858657438 --172359939 --105112345 --773939627 --855474228 --465179223 --487591179 --306712775 --896181541 --687528674 --147889748 --41966187 --22118413 --354232541 --710565110 --3856002 --660039205 --124627978 --621148845 --250405509 --323735952 --755552643 --336112330 --217287685 --5033585 --982562473 --45520655 --835621646 --844809101 --659071396 --341087242 --820803965 --623963254 --230822632 --217598617 --18373600 --359909951 --584764320 --25688386 --629630305 --2824817 --767998481 --401330108 --226429361 --466195754 --824785506 --37310761 --680100235 --537691676 --407259324 --634069848 --805589861 --660137905 --21678971 --826964853 --661997654 --700874804 --601615554 --970377694 --670172561 --53864268 --520250290 --477175177 --490492447 --976675097 --537050888 --572356172 --327511175 --298679527 --829596569 --792492210 --233656728 --554522653 --158319277 --520087013 --955556545 --29069594 --801348076 --441036565 --916657711 --234977507 --413054124 --557730538 --909229437 --391186795 --247936493 --220732409 --132498061 --561329746 --558597795 --415491063 --314017600 --597756762 --709382792 --314282719 --425676404 --129107133 --793809278 --967505239 --324173345 --202392617 --561577161 --745440526 --916326429 --297472518 --859348998 --383280858 --301258974 --921286279 --949651113 --594350989 --315839527 --353137186 --753785743 --817765369 --995729304 --555275654 --230825564 --541572903 --460269154 --858515037 --771107855 --13639795 --910826409 --365472096 --207714960 --885429783 --273224623 --579445397 --731786022 --746865993 --631115922 --229603255 --778448888 --263642838 --324259608 --911026450 --309655224 --785096987 --922584151 --898139432 --75065670 --88907872 --417825539 --315349145 --345852992 --464541770 --193494557 --22279757 --283565393 --175614 --70304661 --376107825 --367555450 --42949833 --845783772 --732933921 --613790873 --813454660 --683302813 --649034920 --335615894 --192945788 --627374799 --896812411 --962793530 --700195923 --427586395 --475563375 --307241017 --763656858 --464859578 --31841110 --376486806 --955068805 --417592780 --592720712 --780645492 --296393170 --795071511 --546542782 --694416734 --529644441 --241461152 --823511644 --506293601 --554774553 --207011193 --423273825 --140798766 --516426994 --27451878 --347716789 --171361361 --480592035 --394554113 --931591136 --855381915 --662967616 --216450942 --747583373 --636134836 --96320177 --940226218 --141481649 --97157063 --35539247 --864223040 --966067028 --355100957 --590394961 --680824987 --124413174 --762830304 --317158632 --582097516 --735306497 --39208031 --923286026 --279249608 --642669072 --425337276 --491715561 --462898111 --234767877 --953397863 --323930800 --491355631 --774812988 --93086534 --359763272 --891173737 --199302042 --911298576 --286534620 --260448352 --543648787 --421841891 --945331970 --531340589 --655519238 --88959186 --577469181 --626062223 --197627188 --416347049 --142723030 --607066078 --580411989 --582894489 --225506430 --705126946 --620639636 --375804958 --703737875 --493335644 --510860192 --124968059 --840487368 --113109723 --848628795 --826394626 --814932144 --358215341 --136128232 --492347466 --550887615 --525726365 --688909003 --785257516 --791421082 --312736685 --228906143 --791551369 --543689389 --147531646 --761546923 --671803124 --430152817 --621244814 --143176069 --393694455 --423686117 --652103966 --119508778 --763622027 --443577641 --619362552 --353137664 --560450926 --494100431 --29816648 --142356391 --883408481 --709211496 --93402152 --929272294 --444522260 --646290204 --466065585 --356206185 --773191540 --98427475 --738650576 --547345778 --504733188 --419385078 --880114184 --111288292 --321248532 --618838536 --252476440 --993127331 --931073076 --96441234 --421563558 --683064068 --858138039 --913373739 --434252422 --775525478 --632253032 --447342438 --117432049 --986392509 --827190541 --260902473 --231182852 --998444998 --384707997 --257844136 --700683031 --107513259 --507837112 --156914856 --953829038 --770157254 --320640667 --912835406 --261970068 --579890006 --642126615 --698348816 --893488366 --183183433 --960503865 --962518561 --140592016 --831340305 --916163943 --678042310 --653610646 --178007029 --741378000 --710165440 --527527672 --924731897 --132850832 --846678028 --36527584 --182252161 --737223329 --364404219 --239449917 --78637628 --943153454 --31731439 --632457417 --807878729 --246981193 --753904775 --403525882 --901450777 --600566349 --952220481 --671895469 --813370137 --546417647 --347364652 --366329035 --485452965 --443415332 --975849913 --94200789 --698244016 --968682404 --259232231 --396113643 --956030366 --998919027 --987038834 --539702671 --247067625 --148257567 --279506177 --112047848 --398124750 --483927529 --902064358 --687039112 --892529759 --77268037 --985958319 --420668507 --62934291 --264863434 --997669468 --454259476 --846640302 --651148156 --591445983 --586366704 --276424491 --353110760 --119223216 --901166704 --766309083 --868294320 --400532441 --546988071 --279704631 --490750506 --191464740 --593545137 --172647187 --4708492 --559292523 --559872567 --27525838 --511765593 --846220991 --891679657 --778853499 --832694733 --552097965 --947990703 --546186649 --777100514 --625884163 --503793640 --162179666 --572430043 --407418123 --747859173 --676140379 --253433137 --368012758 --352633894 --441345227 --591817056 --583765717 --1232478 --579279678 --583502505 --438486374 --330564073 --720821779 --831070698 --959716259 --230336812 --565986487 --333211749 --543057912 --211190892 --164796818 --729097700 --907085797 --641557994 --485248571 --817038297 --408343577 --858927325 --48536791 --799672486 --825970167 --911540239 --960637087 --106381342 --529697005 --732700019 --124413888 --568626812 --641044218 --415692246 --931376933 --364730012 --349349763 --48201297 --924585647 --283287459 --201122654 --384444034 --992379228 --634058733 --474999074 --674378570 --721513859 --525282292 --541800779 --445677974 --986720527 --854769135 --408602126 --266191979 --352893644 --604200684 --566047523 --198054013 --943521837 --794015696 --214049788 --590620954 --453320245 --569084205 --329299207 --822565205 --791528979 --647580197 --957885737 --313480045 --898676150 --243485607 --332740798 --423555904 --314572437 --601497820 --93491853 --586955499 --493848343 --746618222 --536360389 --681094931 --506173707 --910042987 --236630667 --352698019 --976055323 --9520785 --527152990 --207657246 --365668986 --613246315 --714163727 --30376183 --380848285 --547510857 --982628891 --720570814 --663033820 --623067997 --398382943 --187752815 --198938451 --369723033 --866374838 --295231220 --186415547 --7605359 --147203474 --27890889 --350871067 --430314823 --391891648 --648024677 --548152228 --783252908 --43087850 --439404685 --328457358 --519042561 --438830677 --366008990 --599903626 --878228236 --339091860 --446414570 --245060062 --217194636 --504931539 --896578013 --279771249 --683914975 --328822747 --518862057 --398463132 --156618731 --668001124 --134190881 --962814654 --645884877 --893957080 --998572026 --144334405 --908405660 --24376025 --562083287 --578269718 --274470837 --425855772 --296457136 --652983549 --676411959 --227473595 --840388299 --270454119 --325831427 --72552834 --948654090 --562155269 --371877203 --220763576 --869968355 --503571400 --608074822 --339605450 --842959884 --589485895 --721525886 --709430535 --145465614 --94316354 --694757994 --823845997 --605141386 --203586253 --683355383 --245974547 --606138897 --305726 --972697385 --830160017 --515678499 --992664719 --389086517 --140637568 --39345940 --586136285 --508104764 --453516305 --48059079 --260848260 --295066400 --204509115 --100113384 --167514117 --720728752 --25484087 --654350194 --37220930 --635499120 --18411625 --603352068 --96771425 --771540072 --572156158 --679379931 --954146475 --410605409 --785452459 --548744636 --388934576 --674909300 --190269684 --446766201 --585322948 --110553692 --708345182 --493263968 --797438649 --495784248 --18479630 --118746902 --212392733 --220760349 --582229282 --487815029 --264350340 --211029773 --602457251 --928603880 --147486711 --849130306 --715746304 --235945050 --218560400 --552437006 --117343594 --869709935 --818074314 --88389897 --664711391 --603969512 --948012655 --524463906 --572440043 --451724497 --575002241 --429098435 --93891299 --358742618 --373330190 --183146607 --60743650 --39643123 --898424007 --662813205 --525765393 --878561119 --91600998 --191592489 --814197096 --986640437 --197342431 --979276508 --62273323 --105813399 --48729918 --914165560 --799283682 --484172181 --43643435 --394954637 --697562537 --529609334 --831338163 --950517355 --359454974 --122794711 --483923163 --688707315 --61965890 --976081542 --790415482 --62641790 --837241201 --644872520 --763633128 --53492392 --478766158 --341943058 --160367444 --159064783 --560381552 --483286610 --336493641 --790498439 --137199630 --677264992 --371708943 --927677271 --537213894 --431268138 --149978328 --290936550 --769235204 --944888504 --203792039 --203908843 --301730969 --268871470 --990864945 --600817303 --461850919 --169264290 --422399466 --883866885 --7028108 --645457377 --664963473 --592362681 --515495171 --96720862 --508956107 --592644485 --338322730 --933841814 --317131591 --423927332 --599331258 --32111944 --646722805 --650621408 --680288741 --479550815 --505775029 --986777185 --92791365 --652332202 --67398838 --265607854 --648446542 --217315273 --814519287 --808099936 --460409318 --826995252 --851325658 --190440035 --812276604 --384513246 --18946765 --228754452 --842464646 --886769173 --751056805 --185909532 --664611753 --837713311 --556145662 --450612073 --317244017 --319749887 --603358968 --948539536 --286961082 --899897759 --995618635 --991357581 --788985694 --16911501 --631265335 --667844540 --438677583 --436147804 --605122576 --14695913 --367775547 --674383471 --461891873 --895754952 --515169644 --449111549 --919381713 --657300893 --65052853 --869389909 --85119427 --109186498 --676459678 --40051175 --375789322 --7822368 --946870753 --509772983 --694312935 --390753649 --502186320 --700726698 --237229867 --197902403 --929229521 --638742397 --74221293 --274584221 --42287224 --153906556 --405445540 --864124825 --39670148 --712253869 --295518971 --640557711 --765816075 --611586507 --838012781 --29564537 --262940741 --821404818 --708957234 --101602539 --361420219 --351882008 --740684993 --686143889 --219246017 --103658047 --820170570 --847033894 --298217672 --865687007 --679137725 --367572910 --952843490 --458182820 --767283844 --959538708 --841257734 --964812788 --451406868 --989669239 --425537318 --319918233 --279365580 --593275009 --195138658 --130446752 --49993949 --748363359 --406283114 --754153058 --194354916 --268345981 --65082241 --634066412 --138211350 --836666085 --179271571 --311130087 --463656884 --486825779 --120797073 --635917631 --589043922 --330616446 --806111925 --822236135 --121489936 --279038853 --180599334 --9288286 --46596432 --306600387 --591467569 --934071291 --381376086 --652328895 --904551790 --301622105 --713097155 --451581686 --474741181 --653134854 --104678648 --768947156 --432789414 --86065329 --120648857 --680794432 --366557307 --481684038 --169399299 --421598110 --546693515 --432108475 --252320649 --474363932 --753181243 --658221285 --321968528 --878885558 --392763400 --36431247 --622782938 --544394134 --195586483 --413387947 --335733030 --469587917 --688482932 --358458551 --246164627 --965968080 --671670029 --631407336 --844411130 --440139758 --647279552 --517443761 --276884295 --615734391 --418396531 --267373785 --321665048 --994775938 --462831831 --460737859 --568075358 --280244784 --697211248 --406558984 --437769395 --961891079 --710799612 --690325717 --661462487 --633044174 --494463745 --245746036 --128404927 --847129528 --710090291 --19450682 --333300520 --993250903 --947788260 --373308615 --829535651 --750626742 --183089622 --241212905 --458056270 --15879254 --394043575 --338353928 --85733544 --852318346 --426597004 --303668521 --984299417 --945191223 --460182475 --296860273 --525264128 --382073508 --327995857 --537367048 --289435623 --922371268 --91942048 --797652703 --109703574 --565954458 --877645175 --802871082 --965693883 --464106789 --132890181 --326883454 --297522512 --880548999 --750228587 --826216135 --879181973 --290023023 --448682984 --277087851 --420205731 --338158363 --774558872 --694098929 --279722236 --403876759 --36921316 --854595474 --729578215 --664280564 --488474711 --564363368 --198383932 --259560925 --497230498 --829787002 --334153568 --418345147 --627481382 --767621178 --715397632 --872999935 --474560436 --844716085 --958244920 --583597829 --348896793 --164294119 --991455740 --565429704 --82923977 --839119243 --384510501 --759811184 --239763157 --248254147 --402458375 --594760623 --632291857 --921868793 --948793134 --390303181 --451966324 --817499404 --191032822 --847680091 --676374400 --106797113 --512395780 --86252313 --634831566 --602652170 --473777938 --922730251 --582781462 --237940435 --881596735 --573369075 --750840227 --462798010 --503678874 --875687582 --582987346 --213463656 --845741469 --848394503 --41631160 --327548069 --1280893 --962366896 --679729546 --918159052 --400652609 --341215558 --782968928 --192783296 --446669017 --923278053 --176766964 --593922779 --383457442 --650205046 --941877693 --578922705 --778543284 --519692372 --76513374 --683484023 --491119818 --374437221 --446703102 --950524082 --791722988 --686794349 --993383579 --540656224 --506859349 --191296671 --228618812 --514190302 --534524533 --993345846 --170269606 --551522204 --642279253 --748612658 --620543193 --610449715 --782020764 --170543760 --212674704 --117056292 --658798903 --362433217 --410778919 --976302825 --782983897 --494709034 --49422983 --73513113 --98582840 --138504259 --816616511 --51011140 --815131681 --667206661 --577367583 --155215537 --648872205 --75980259 --309899079 --781377055 --841372324 --761090662 --150351513 --350136492 --663596158 --342720071 --859128370 --681747084 --221227650 --813997861 --299148109 --273717201 --780738342 --19308977 --501502706 --200706769 --620333209 --877592619 --525565333 --162624241 --815945716 --492329262 --56191639 --924352148 --807549267 --964599472 --807386998 --72466703 --652539385 --196319888 --244715144 --55903886 --237194120 --245959059 --928395396 --767255758 --33909160 --981589543 --145236017 --59493584 --284033479 --557605347 --742557467 --739422848 --908022004 --965083999 --878247821 --809625457 --435487019 --565875311 --923452640 --843916492 --742631827 --105323093 --510807610 --917963142 --719297472 --386014707 --107888503 --218973130 --292349124 --409775507 --378529994 --172071505 --279118596 --314124642 --89352493 --837544982 --108854602 --252650045 --51680445 --162113593 --824774122 --287863884 --828514217 --772111529 --503890423 --645639851 --682536740 --158449942 --829777382 --83869390 --876012601 --608358568 --97869584 --167523595 --803249608 --737259841 --745290363 --801389692 --149220521 --834958505 --321975899 --129125014 --622051825 --865120452 --888100089 --353821785 --348654567 --891525200 --257141897 --348632581 --306941911 --120428928 --758172492 --483225247 --173801007 --458308437 --317437228 --607975664 --676931341 --601004117 --811006790 --528218045 --584059628 --510862966 --796882013 --68673321 --760610600 --852171437 --944237956 --189228235 --442366446 --159760960 --117212499 --225790635 --109432530 --765263658 --543287309 --452688992 --414528988 --569411963 --431152734 --625974064 --455032683 --229056111 --577059312 --848647958 --359606267 --488459249 --274249942 --958569728 --300680817 --16229829 --782414979 --434699379 --75822697 --836371552 --571481210 --335945695 --64239855 --727742367 --285782476 --459934928 --696729576 --269466529 --257185194 --621398218 --685975623 --631007548 --75219312 --948304087 --241713977 --924481630 --370270490 --858506105 --231625285 --20386663 --329581258 --703179832 --457361393 --852282161 --108003082 --888179607 --273526206 --215968865 --354957049 --542993484 --560580796 --676298558 --345279237 --494113525 --627397490 --963954883 --513175150 --142036540 --217310194 --607682095 --837269515 --365340198 --727034235 --822293598 --255252977 --489117508 --482530818 --193533884 --987700492 --115595795 --209430274 --65874313 --654740555 --553296937 --820049439 --407000863 --775252661 --286808991 --473131442 --702388510 --145567787 --475946893 --114456714 --287271256 --501226744 --956816509 --32940023 --173105023 --849958690 --348187827 --617992215 --241480706 --65964362 --715260671 --442250309 --612092540 --837698652 --413103467 --235997534 --651972561 --782373370 --172134468 --820048601 --429748177 --452303182 --316102248 --392999448 --640868136 --579928177 --928098367 --450736963 --677853643 --637999609 --353643520 --469013147 --14916611 --824403781 --305449807 --508859916 --740105586 --315736574 --665887979 --434844456 --61129938 --212428717 --684990631 --306974537 --139180189 --772201537 --465219596 --952792932 --962700791 --821769134 --43842351 --97403298 --160227426 --165357121 --262836759 --300243036 --132859363 --692383734 --699117106 --865205936 --894186691 --419697939 --469550325 --313360873 --677491204 --307796013 --963074584 --361016849 --552942447 --978465637 --799477877 --217908419 --706841666 --303954643 --498486929 --527191356 --324800974 --641961124 --354188007 --959414368 --305782154 --41153304 --658198626 --296851686 --422117138 --131479122 --257745727 --50207680 --288737187 --678127151 --146495663 --413495959 --733283968 --957384694 --391133660 --164927081 --543558562 --485420663 --482581002 --938940323 --757248191 --603196299 --780317839 --964722775 --467952747 --230189457 --154095194 --354072885 --779013671 --430973530 --680987283 --450995047 --884638231 --405829386 --151832667 --759905842 --442662803 --724681343 --905420005 --995548744 --722349023 --856042636 --531221026 --853482227 --586490945 --514469324 --669904464 --287520306 --218463006 --322054219 --446028015 --186154186 --514412576 --390957419 --709957541 --893496030 --831318474 --773839244 --179109730 --776735090 --438182444 --524335610 --671021120 --2407237 --963146710 --987919726 --367816464 --615905188 --419224140 --943736672 --548732519 --205131300 --242724078 --197651031 --164453536 --858723318 --687678135 --932893641 --934090947 --818130733 --544704100 --654471924 --409537617 --546840177 --246371658 --309396972 --780290529 --959514831 --818705301 --816976370 --563356395 --434330339 --923456682 --339378477 --244127529 --730688710 --451444055 --308254228 --843262259 --694895069 --116649207 --201558341 --3292012 --851742248 --839882897 --497444016 --587970668 --427677486 --323457258 --48763953 --461007388 --91606806 --892037654 --243405923 --475016715 --15159702 --879723157 --586221271 --632238500 --539361439 --506637262 --872206997 --531152646 --685278114 --376696599 --732013793 --803567693 --917154900 --182491112 --708736124 --159149499 --546134436 --916165781 --263478507 --737997438 --705473307 --860326840 --372967840 --457725700 --815567308 --97208617 --716002268 --288419942 --142389701 --719012301 --57956785 --197241188 --16019479 --723782902 --408697448 --269178788 --45552003 --45441947 --275046735 --87230594 --859468181 --783903035 --125670247 --38649733 --845598754 --262719607 --324922834 --272324044 --576139035 --246286045 --649809794 --86440794 --708822592 --961349433 --560482611 --677817014 --107670199 --317342047 --317240935 --857100397 --673912768 --520762696 --909258553 --326645568 --827532662 --252251987 --305420618 --853272018 --472039324 --632339982 --347417730 --793785431 --124215211 --566868911 --858820316 --884443558 --990916201 --104127041 --600733963 --87559453 --247659421 --69293514 --952581277 --811716188 --653739200 --70127999 --160577716 --216738906 --563501929 --461411165 --309543261 --878597144 --695863631 --597771368 --723873153 --245415659 --176042945 --303893343 --769236297 --615911467 --361995532 --96784529 --535349442 --222063208 --736993479 --154897439 --705836631 --709931936 --215435533 --910111922 --1637503 --225514986 --69274885 --904032348 --642915959 --772678215 --769399193 --685306753 --882345905 --581415764 --425364448 --801114529 --691361774 --464143083 --558204294 --668355163 --580995704 --637776988 --655839968 --40678834 --380795985 --887789054 --227779743 --567920490 --938624469 --834109281 --85121287 --273742808 --917840276 --202926690 --608214702 --493730916 --671944544 --106953934 --617064204 --436380897 --311193722 --592499876 --945466832 --940330453 --66113069 --422024608 --533789746 --777066402 --846871861 --337903748 --890209324 --705705083 --992745537 --51871838 --106494390 --873118117 --603764391 --671759535 --453901010 --852245212 --970789918 --961785712 --489240379 --27787597 --255098204 --355943274 --23878249 --4320124 --963436925 --695295100 --450810872 --911171713 --415896390 --840072719 --595693487 --861414428 --669216861 --270566742 --304971724 --298325772 --970910570 --788501487 --676851773 --965419480 --308855893 --838526172 --693064717 --804106277 --478511468 --448539703 --167776074 --772474812 --292238568 --866651086 --790564303 --759674104 --410065489 --736394420 --449395636 --307837125 --713615016 --249368758 --69180833 --63573276 --587768069 --439544205 --984114309 --170582807 --556456509 --163895177 --582363347 --777142097 --431286617 --377335971 --192277656 --146790830 --42980307 --212757668 --632774960 --713348149 --182696537 --281126941 --550866678 --226148704 --120924573 --915052263 --407513799 --748619004 --976612994 --649028220 --171869585 --470940831 --176267300 --284398318 --740711955 --725462902 --456553733 --63436492 --931886165 --645201705 --427469685 --345261333 --74204533 --987778986 --923007646 --15953448 --454774905 --359905308 --901203774 --701089541 --358646143 --432005672 --399628507 --603356566 --65483561 --933463317 --163910087 --112227618 --514086600 --680425815 --739864888 --576224963 --541127801 --529200901 --453507921 --945084738 --222696660 --524835204 --407530559 --586145226 --524501100 --421425197 --247389156 --619167871 --353744120 --913121378 --573074704 --325810386 --488312970 --717260744 --595691329 --379946949 --622413886 --867608635 --272973459 --924594279 --390582105 --649499466 --968994804 --996274797 --71677455 --297916694 --47936281 --286780239 --238397748 --679701423 --776299404 --352420417 --989011540 --414467774 --473595158 --264293616 --747093007 --111546596 --378146741 --146291595 --467107982 --994916951 --285531521 --416505596 --684094693 --119612583 --442054401 --490022287 --192386216 --809595522 --162181024 --546193244 --298151696 --535343169 --303237675 --341495247 --300718987 --545109447 --273032763 --84721857 --249267261 --208363408 --66925096 --77756918 --649578841 --334257547 --551405228 --135439534 --646226195 --434446873 --896586083 --401177179 --901477050 --926643007 --805907589 --432766626 --720869569 --728933394 --513777022 --444454331 --733449539 --576615588 --880668364 --626561039 --524787521 --150202591 --188424291 --532582671 --746769352 --825109713 --987795987 --400461034 --560867314 --470671703 --290025118 --324231224 --607201831 --816592710 --550013295 --264484833 --893209733 --989036399 --741944666 --887513204 --520277279 --213292002 --978583192 --653800592 --967210908 --140997957 --198867467 --781053420 --350498430 --602501408 --991770222 --749515103 --797753096 --961470095 --789062760 --943708734 --689301960 --435084795 --171846082 --905071546 --681198054 --706019131 --349011664 --202377370 --966458042 --761311849 --609938758 --209623751 --137756152 --514431444 --383747088 --472844931 --572321039 --95093351 --409345264 --930096035 --528706256 --615449986 --950180537 --187190152 --969912720 --81509200 --726567271 --126053976 --297185674 --182024651 --952583839 --632675455 --155226889 --303265013 --807090745 --822331999 --938390469 --876190950 --883647664 --449679153 --94161501 --323486124 --424506093 --496337734 --164859303 --905919097 --10443242 --858613370 --163534422 --579494243 --881330095 --186751167 --464857108 --6856791 --755268147 --647515822 --324701295 --401355234 --27049231 --35705896 --3203771 --367221152 --554535058 --37710928 --599368464 --665976306 --162652510 --409592772 --242056214 --282383988 --874429802 --405204405 --621092591 --851176257 --740349840 --210208436 --412948737 --408913871 --430897484 --606569581 --808996910 --407616755 --28958741 --794085149 --745249739 --483702171 --547664977 --441520505 --343941615 --22943398 --610311953 --977090070 --941201324 --318724574 --825460320 --861549361 --150097427 --199700625 --323537964 --187164407 --79353194 --727102059 --147400122 --854875069 --170915076 --452992804 --793521610 --932859463 --401443663 --943664817 --238914796 --513903527 --937345933 --898136401 --840378078 --234883855 --984570332 --599097224 --972285253 --516672711 --368905912 --966783434 --427747276 --80577401 --942549604 --467500341 --370103656 --245046059 --192851782 --366110707 --493576999 --571847420 --738620076 --647880208 --939218590 --955333536 --499498144 --973219741 --595645262 --892870920 --956560297 --692207186 --909265698 --65545874 --508333706 --361560164 --292310518 --229128250 --971892482 --657365771 --475948592 --576765502 --981668671 --207443164 --66464475 --417040800 --148302878 --374735211 --111230188 --411502163 --270901525 --195932932 --68822532 --877624342 --27675280 --778195440 --427878499 --97702270 --849282876 --754068865 --955464965 --114779237 --902227805 --3312785 --4010827 --772743041 --654484915 --873959044 --362344860 --518740605 --362155590 --984165445 --214539704 --971511576 --102859061 --340334421 --327908673 --948146869 --61005963 --583761272 --223029966 --73529151 --916820653 --696002531 --350739211 --836513848 --508562372 --648919259 --574033532 --488133577 --154807536 --478656649 --126572114 --868875015 --165333601 --61430112 --135191646 --625401161 --773128233 --720292176 --631043084 --338128191 --973648373 --777758619 --610977557 --464568207 --210292068 --742854334 --127515190 --766675082 --896896482 --752198545 --302826056 --730940517 --401960080 --865084370 --418743503 --514330606 --582280746 --305800431 --75124309 --44594383 --322302429 --577132391 --43268833 --809083211 --374401384 --500779496 --654117223 --366311744 --377042435 --269062199 --166632500 --715387951 --689470806 --300394617 --554166951 --912816657 --762551250 --956039599 --31465581 --700806167 --949690887 --828591064 --782613851 --163454670 --282425521 --804023070 --639601420 --461784001 --204398611 --99866903 --60137267 --344830995 --693363715 --257216666 --635980087 --923706969 --114039573 --261255513 --594061619 --379317086 --614907559 --997376617 --777784147 --43033949 --717070521 --6730454 --36270002 --641029250 --595027411 --422982400 --190367477 --384057436 --957691287 --196544269 --774239599 --186273868 --752712251 --164964146 --960516155 --653096162 --53171139 --887115281 --36007002 --659255054 --880303115 --218658590 --23723308 --445801274 --963389033 --653840226 --845122644 --422898721 --962804334 --372219099 --714454830 --525003759 --954742900 --2758906 --333911539 --720261395 --614333518 --756323139 --863881113 --223309412 --386278163 --419233654 --84681742 --622622057 --599880309 --117061271 --785968690 --663755594 --522734516 --292649340 --91096595 --370153062 --655982049 --691783703 --314148114 --357653583 --542809645 --987610756 --705140844 --666285933 --903736908 --404195087 --667836598 --677996089 --804961784 --490851312 --847805068 --283411884 --88724630 --21547214 --65661010 --883265342 --366289242 --897280933 --852774426 --672687408 --230223351 --735271436 --192698780 --235562579 --244730918 --569462245 --13478005 --621288316 --966006238 --94659155 --11391595 --662851848 --474523557 --974861532 --903142030 --978149420 --300613248 --982234720 --882621181 --12106287 --844870573 --941521354 --715133071 --335689266 --41410887 --485569447 --140220114 --695931309 --768029324 --97856871 --3013567 --920006571 --888647582 --746570135 --801829067 --671066080 --77997543 --934293633 --749168832 --222979507 --250515362 --741311387 --363345735 --736946331 --121894430 --625716920 --365660324 --901120981 --958290394 --627028579 --89825959 --668239516 --978714136 --960569902 --459823416 --485347244 --1224015 --908394242 --794274998 --781165488 --984606932 --756707242 --104305896 --475015374 --513501350 --207335432 --269714768 --484611790 --837940510 --380556860 --887135887 --622054096 --192064948 --686285816 --543294759 --545314342 --903082939 --243888557 --70393439 --209616213 --70107115 --703980409 --702264103 --288219385 --141284343 --900563647 --911170795 --880248121 --109897075 --62999273 --179011423 --10610434 --741779289 --357113661 --523178463 --172414467 --516159335 --951886124 --429216712 --819829370 --22746406 --269003149 --457763309 --633792015 --245021193 --933661696 --355049354 --821553035 --681807600 --652904488 --628847066 --319282904 --496146081 --80069943 --861391375 --587105848 --397470132 --137751051 --908584048 --270108989 --33108383 --676656558 --971303950 --50141465 --873566075 --396375466 --718375844 --473852142 --268820335 --831516729 --762472671 --424232660 --851042772 --558169613 --182885510 --956017622 --792094002 --146359759 --416185425 --311669550 --948563103 --338112191 --448705114 --53800964 --310486218 --697069607 --436770498 --26065371 --910182638 --343435736 --793963180 --75167111 --118970299 --303640058 --937610631 --6118871 --362954492 --994320792 --984980512 --493004832 --171533221 --126885061 --205953793 --164448730 --653622890 --852623880 --128175952 --904257522 --951213538 --986024510 --656585465 --368545165 --870731971 --439175299 --129337889 --211774431 --160089675 --876026122 --505301183 --805602648 --211254430 --615164297 --164998173 --649329541 --797417108 --968437957 --33819922 --639074577 --415332516 --238060082 --749041351 --367006551 --243227456 --386522202 --816409643 --674347037 --615189501 --426452223 --359313240 --839624671 --341047219 --710285111 --743592636 --85113523 --986975676 --536472621 --619165435 --519766209 --969069244 --240799995 --98565871 --604185540 --917246935 --189766067 --265879579 --310514734 --953263468 --804499493 --509508536 --86239059 --35895564 --722230803 --860643799 --225644058 --280271807 --57688706 --205137460 --268131366 --444116675 --838146803 --89617586 --234022504 --815736502 --401537698 --819640870 --782249075 --64289250 --544846110 --826514131 --325449460 --979471699 --956687546 --718088944 --817586120 --855475650 --764902680 --172100 --459577552 --563318846 --836020598 --514556635 --395664811 --429902067 --347207045 --402497030 --312365873 --177417378 --233664320 --332910607 --485486810 --646631615 --887821223 --425560160 --897779008 --933521862 --372693291 --268756177 --759527567 --627966845 --493292584 --906714965 --683174661 --358103920 --664883945 --831400003 --699723335 --237126613 --588205418 --496018461 --870121204 --282910939 --908458031 --7744904 --655833627 --379914097 --967263156 --103302596 --703753136 --666965315 --559857077 --413029402 --931374180 --878505720 --927654524 --709512925 --729688789 --899282369 --462564430 --922204890 --604077648 --211897874 --903277003 --802013272 --440410722 --668237000 --825106269 --494730120 --569055419 --212359895 --595185564 --417009167 --435157458 --136913792 --137699530 --125392794 --583655625 --765987755 --50359789 --902574347 --686337343 --279562319 --809250276 --15792209 --146053872 --976990676 --988088179 --821036468 --427540493 --19507811 --991799468 --256668790 --438734216 --905262489 --386525700 --50154703 --434231309 --410269271 --843172544 --207278639 --591971817 --262388073 --891536821 --145701887 --607024705 --584060656 --397749685 --500932568 --192068748 --391181937 --628116767 --701441298 --39684162 --882995567 --506440403 --402253335 --872714359 --104612033 --487451577 --609544824 --594513739 --400531854 --585644956 --155757428 --949859306 --326564978 --306769412 --74687200 --955467606 --335145891 --354051974 --287851595 --509279458 --564813503 --213860568 --648290140 --380034481 --507599816 --894076705 --910395925 --968701634 --204570069 --714999853 --952956586 --417644428 --701721927 --317257260 --883811275 --886498115 --645077344 --44320609 --188862410 --431170382 --353032361 --81101376 --93963570 --43884263 --968248484 --208116593 --118640948 --307566408 --220778677 --934745366 --390574690 --463085690 --894050648 --566733265 --672598370 --628478441 --177959278 --32047669 --876708126 --142998704 --377183265 --602642983 --161809636 --651895519 --527058733 --709299813 --189621570 --109115725 --412671075 --891931617 --813375999 --717367501 --395014062 --308752907 --376201040 --249535374 --545301799 --357965195 --605410450 --94316090 --863165535 --225035390 --887285050 --910416817 --28667727 --45123065 --484777624 --417372158 --837385460 --464371108 --857987345 --622901080 --269443394 --529703207 --509116694 --951242122 --504685954 --763700514 --965864915 --979527564 --937143178 --179210425 --848870373 --530563960 --971040174 --886516559 --901835915 --790422646 --171434214 --935367556 --886102866 --990350119 --891521765 --966031446 --590944284 --137369009 --914525735 --51930074 --361511346 --119979378 --430888073 --771326058 --234845172 --863027159 --2028200 --959856892 --272013663 --63489834 --866222423 --583732926 --536582779 --416244511 --342375001 --270041069 --414735013 --60764640 --478486552 --221413730 --4573809 --502646236 --924087295 --790272799 --573061213 --696590771 --94601652 --111941960 --859985418 --872434747 --705235229 --534236749 --597086267 --298923891 --493389461 --907956207 --768661920 --782752502 --428551368 --175593283 --979195850 --902545995 --270728786 --718749922 --950927983 --194600949 --579147420 --171908880 --826025252 --579775600 --680290385 --608498486 --567201339 --8673086 --306721640 --609601033 --855548908 --82026547 --616952740 --868507823 --874159427 --110774478 --566382230 --462617994 --325593268 --919428742 --65659143 --793297377 --454077021 --378887573 --955616154 --181231899 --912737284 --161137910 --33169842 --578053440 --804465832 --383095317 --534359689 --583758558 --104211279 --211803881 --976959953 --627950149 --230495159 --673677810 --271340144 --93872006 --859240669 --94009609 --49874444 --411596816 --532751243 --597401998 --30965260 --269988707 --401632008 --18867301 --100537624 --709468194 --528447164 --395051938 --938928314 --948011744 --740204387 --85349441 --271228947 --546823467 --388725873 --804128323 --23755851 --582796015 --719856241 --480620297 --827404226 --287445403 --791859571 --911996099 --162814105 --279903288 --422802727 --191461604 --361951649 --154204169 --951574882 --925746735 --353069211 --294492040 --367268681 --471023950 --391843149 --872540300 --704548779 --972385296 --70217552 --364550910 --497859842 --784404696 --835856631 --128720355 --379581527 --106618037 --556267059 --266446057 --715441959 --631121357 --888559586 --950657336 --266148733 --429518828 --874090384 --577958845 --43219603 --496522759 --750164748 --409873884 --447979840 --235678863 --874384102 --580427289 --690648377 --198510222 --617476712 --712435537 --78138860 --688839797 --893147805 --605018374 --450326399 --161741345 --280701115 --275303701 --577039643 --187580833 --72537307 --57874828 --500093672 --290836749 --816665302 --327781343 --447368213 --267731854 --159878353 --2611959 --726510708 --590311382 --256708137 --161598821 --140022162 --330431701 --448193927 --760501368 --775018367 --558151223 --41812698 --548556045 --913375014 --18534977 --974803658 --911327477 --405628318 --123139281 --700899570 --747820714 --796893529 --80217988 --498948067 --777664485 --788040887 --438571284 --604547610 --645080644 --351606954 --288591604 --173615870 --342381961 --463540504 --197166823 --259908411 --208973493 --65359416 --366254101 --946669218 --277823110 --482401696 --433358223 --655426293 --312202807 --264159469 --692505962 --318120245 --262603528 --272132829 --536576503 --649596279 --441561274 --553538170 --236311966 --271413825 --108434058 --248601129 --372714239 --111534424 --32812300 --378234855 --80905661 --257091301 --522526640 --777198185 --365683294 --119674397 --791033464 --511420800 --245010469 --285313003 --517483216 --480872342 --19659123 --979199833 --943723975 --771770875 --513006552 --465230351 --775967878 --834017934 --519441445 --84035771 --728247789 --351549877 --315652451 --895316906 --290815389 --238793279 --866744927 --584140528 --861427905 --720267259 --807674541 --340670190 --173670697 --935309006 --190918173 --534747556 --344832929 --560417976 --140445742 --733649130 --707389098 --709442380 --596315510 --31844544 --708089253 --171487751 --825478962 --451222293 --38646296 --505998787 --846950919 --136157761 --842779406 --365803255 --934555862 --109863629 --861360802 --743439869 --943913197 --135890912 --629810499 --790063857 --428867806 --339969322 --210095181 --675994430 --381453828 --134177066 --742128389 --286971269 --233324380 --382441034 --572125800 --632046988 --635561632 --888319540 --252507964 --28401900 --319913008 --748277157 --252451744 --323454241 --595742054 --293306192 --680565674 --969948827 --372806290 --754022337 --893371119 --575437029 --240626469 --181472406 --100294156 --519628310 --857440073 --950493491 --888165042 --839795396 --698237276 --322702043 --167918143 --622380679 --473082862 --775467772 --462361452 --418829146 --180751972 --510046339 --331942846 --618736405 --335123138 --914212393 --924164576 --648002947 --600682913 --32460774 --154881759 --828973765 --175943461 --928737619 --999871269 --343795585 --117907695 --484503528 --427805595 --359342562 --746081878 --748542219 --490498718 --255765697 --788764579 --582871536 --231478911 --460063964 --792314374 --106470348 --156049175 --916254211 --603451196 --120101570 --771724861 --913660752 --971698073 --482524750 --606925307 --298724519 --366676190 --105026119 --242400250 --109261199 --192772231 --33248329 --445246506 --816207456 --711670342 --620119860 --471945001 --132458449 --690200882 --665565688 --369520866 --364745955 --641028370 --825630816 --723768318 --434593515 --846281900 --469473940 --675049299 --324163186 --503597564 --649877976 --383340939 --191722333 --655984750 --963487794 --339639211 --31589651 --628831402 --99445349 --602165901 --705321905 --700035951 --770286380 --191913288 --822649030 --887915332 --600188547 --972988588 --308735580 --79191199 --840129119 --278090535 --941749963 --447780569 --400050860 --32208385 --189840395 --911129351 --755749271 --702580325 --163597468 --94254690 --34998955 --750443732 --206620793 --205786653 --148121907 --579465909 --465943855 --393760804 --597327185 --940735347 --322742371 --291244948 --38523269 --855125631 --422305661 --623906350 --27901745 --850779873 --745212317 --258963906 --503200691 --614223206 --777865029 --514724361 --615822059 --418644594 --113079804 --702728086 --87117502 --263612818 --614980123 --716298662 --740295537 --421810115 --533013836 --466973089 --848039377 --285980032 --998780210 --583495921 --779237669 --421426970 --91982063 --617739965 --335382428 --588755241 --31591024 --545137108 --182062534 --617993866 --740887095 --604044369 --326319182 --831227898 --838839860 --482599174 --128713997 --47335802 --348827903 --235165193 --945633371 --713773295 --715522826 --384869634 --334804356 --352214175 --387824589 --196459703 --106097703 --753434637 --596422239 --699332998 --455752546 --476087934 --809534673 --53777793 --743612990 --615649666 --170429256 --376838245 --150656151 --909823629 --939895315 --915347719 --755086750 --327185618 --362075209 --682536996 --621978268 --391346913 --110719179 --173086503 --93289967 --445712125 --687646423 --148405217 --592135911 --119387599 --267097986 --782046587 --926212540 --119840233 --193610221 --275241658 --126145921 --457646599 --272174279 --846428898 --774003853 --389910956 --497484663 --823369515 --643174032 --709071028 --889595844 --666252624 --663746272 --356451233 --401274170 --126277658 --407898404 --185074627 --798475956 --654886034 --805753932 --706287290 --290080375 --623882324 --653058249 --134079227 --654387033 --452891894 --347359334 --502018144 --337097220 --888556917 --186900680 --995124963 --551193590 --768592502 --553020461 --432966838 --960860240 --465948958 --231206701 --654259231 --311694507 --774855578 --485307241 --204204931 --223018837 --717262100 --611194833 --645520801 --138050584 --748120940 --331012591 --326041715 --988096771 --385725081 --678220996 --110849403 --868793473 --791147543 --734592746 --18742156 --648356182 --677261244 --25755331 --241961296 --94429054 --26038192 --995023886 --260440345 --60747941 --614210886 --884413223 --113832869 --758507467 --734907208 --270310743 --903956798 --974220909 --468952987 --584722108 --452903739 --437148831 --97502384 --13597549 --768563119 --732163259 --171813021 --321817687 --64094509 --240642475 --348504928 --701989302 --272915032 --526525198 --968937904 --685214273 --396680305 --315571023 --105468840 --225352822 --727790643 --167829669 --3066506 --724515223 --359117834 --649841207 --621521157 --129406514 --947212101 --280669230 --711110289 --258862961 --151806860 --848373865 --130823349 --439537677 --304132271 --894806244 --248781984 --599984648 --278273147 --321146901 --608112513 --170462476 --638125580 --781442713 --480681924 --522939101 --171899903 --435149495 --390934318 --516186985 --696049257 --117524304 --500420847 --124927021 --117752628 --649903601 --398287265 --970618438 --671174392 --990929771 --329346717 --835616306 --43654724 --528372044 --930238258 --858181558 --930941966 --92649281 --204114197 --243334229 --459187085 --745127396 --815235370 --906615137 --281298650 --624101199 --844435907 --470969489 --466867649 --836978190 --475539540 --261105219 --408291985 --165974705 --248783748 --700763264 --407808293 --965476719 --210547150 --528547029 --913848727 --187929502 --667215504 --650285440 --915945149 --662222413 --929707090 --996086440 --943226987 --350511758 --993353500 --681425494 --247171623 --724894828 --258197032 --726678582 --615851856 --93092814 --89793290 --962153493 --627990172 --89878368 --515373357 --911055355 --339648414 --250940240 --656340496 --285590394 --274027863 --504412264 --8669529 --392382606 --782994601 --618379162 --355915413 --526876546 --792049745 --386755281 --907889903 --994291309 --40961934 --165654023 --902273116 --8128104 --329384046 --807466955 --779059910 --939824638 --521323697 --484131589 --799219049 --734282630 --548249334 --508769519 --170586540 --561360493 --644648301 --73937399 --34658838 --461038142 --631140785 --190186518 --781293539 --502395961 --408006505 --235180888 --165602884 --397996428 --380779404 --472441504 --372340490 --279669858 --949590806 --625229985 --501601141 --623915982 --9073971 --289549342 --620182251 --638969414 --287550943 --700698486 --870909010 --371173892 --33345591 --368006834 --527363090 --724517010 --447561094 --60668912 --2387314 --286990704 --556989525 --486361893 --838832696 --6396937 --900994357 --218041127 --489193859 --386194301 --862543368 --431297402 --876813313 --565938270 --353524256 --120710247 --335719600 --443347206 --455864057 --952469 --189480211 --537309788 --37012803 --407597614 --442329887 --542877168 --982132400 --828904951 --724339318 --464259173 --712061903 --725172106 --487408679 --10105321 --27071939 --392029775 --51737932 --380830433 --324969720 --828709669 --900781762 --490879760 --180821919 --722833552 --257881248 --592791865 --388254080 --487271271 --88821739 --694147170 --37232834 --220104847 --839026108 --523632796 --710513215 --637165807 --426576200 --583981935 --122347754 --609014516 --980757870 --867488989 --61936520 --106694965 --896741358 --400787490 --740983202 --429645851 --740711320 --874548735 --675508085 --484046000 --247486227 --975068490 --171915791 --212884100 --78616057 --122026979 --722469651 --480079172 --614973617 --286272801 --107822146 --681576150 --631683484 --684617111 --947568401 --181909011 --733533391 --425680165 --496712233 --584089914 --777151297 --781953432 --132795910 --187152765 --767496446 --713427932 --714603082 --568518965 --138008041 --381783526 --89615905 --23880562 --584054492 --562996676 --291991469 --690165689 --338771895 --899496856 --504594235 --523825793 --529060132 --780506145 --589999580 --487219788 --189319301 --128556373 --225795874 --259649513 --40645439 --821676898 --411589295 --61157622 --555077843 --161115422 --460022853 --81818374 --47344916 --612370349 --929103011 --698170275 --504216174 --745506875 --785901774 --798514131 --237155076 --174042919 --702365466 --436632753 --191562061 --19207245 --959981072 --422666324 --940122166 --480738660 --941062616 --77210702 --802963403 --746376582 --559932928 --538614418 --601968519 --953405244 --822147227 --938070959 --879987370 --576477403 --871673897 --308547268 --951246904 --365746106 --507596138 --359036955 --105733153 --259876253 --321032916 --798423516 --722138161 --845142095 --16198158 --676357901 --354310873 --248724029 --816478272 --142828698 --861522401 --940427122 --895757215 --601821261 --164223090 --931039289 --940909657 --91329802 --382243536 --442999901 --202391359 --857590243 --785284966 --576564305 --877322121 --551576526 --597627527 --240450474 --400764436 --337478124 --282151251 --696939252 --814552631 --117047171 --245423163 --500126331 --911577710 --857531106 --626923738 --777008658 --949076794 --75148724 --88333738 --374259920 --443231281 --976866372 --4279137 --898445961 --588421008 --411918365 --762266286 --383505177 --193843540 --255417622 --734335535 --97431196 --527174972 --931315020 --79810785 --8003708 --507283525 --219657721 --639841639 --388367210 --134777384 --989779161 --373712363 --367794868 --339024520 --92143261 --902969444 --841565744 --380913910 --207740224 --289445195 --25665253 --785325554 --336616043 --940512040 --451078074 --991232449 --730195769 --645027894 --513947455 --336047020 --541346294 --300506215 --730260900 --923414855 --785861186 --966299695 --913347795 --164250392 --971357117 --123177466 --595565639 --268194835 --66087744 --628851442 --418498097 --147221760 --960043601 --415720094 --62385070 --300092219 --649191724 --458725281 --631937573 --789878053 --385532860 --992700751 --743643756 --269936631 --797206052 --811797822 --124834645 --967334662 --622761684 --270033852 --537973678 --329942609 --889858565 --920744179 --232238775 --966490200 --587572489 --70815793 --721287185 --222832374 --623700820 --821613645 --140876141 --115719450 --362621246 --575929384 --530578121 --357238309 --733754828 --677246019 --521033926 --122702326 --691541496 --419497546 --499110246 --670718258 --304574248 --562643010 --985513480 --216305390 --188381337 --346432852 --108837494 --675983609 --601045049 --835569762 --665760560 --754729505 --754794634 --921586987 --813268904 --913829517 --772218565 --183839165 --377758324 --446160038 --998237909 --127127670 --855990485 --251216118 --48204482 --234507819 --336894254 --662519544 --814988801 --997317223 --886685091 --456642615 --444295416 --939678960 --681798180 --555830483 --591841845 --625431280 --729351435 --78173109 --907448606 --318657563 --672056546 --773611533 --919558034 --174101307 --789750010 --699062733 --249750109 --65543495 --266026986 --306998139 --41916806 --264761387 --599614241 --916803810 --916309506 --185846521 --366506401 --324725384 --747439132 --721812703 --783602122 --107001097 --588165004 --33143056 --343234534 --215865168 --500576403 --691905950 --665430623 --740380615 --756313246 --502574486 --425040660 --205748404 --812140045 --339388487 --963309691 --9057446 --381861491 --980337757 --561026549 --302275753 --324957510 --537448243 --491305325 --677063489 --361671783 --818952990 --4637976 --218863076 --105928833 --992324580 --723682057 --322863787 --479959849 --67405609 --690753016 --823413836 --283752268 --721200153 --166481297 --36171958 --96771710 --409908502 --425505576 --229532401 --61175664 --959308672 --506556228 --736387733 --372772158 --309991058 --684412566 --855790568 --560727630 --742652134 --187029069 --561473357 --518765592 --444803343 --542967348 --179974486 --83451231 --807166826 --522738557 --386257563 --632171996 --42029081 --141041615 --902582134 --593930823 --228207168 --133506713 --284110255 --823194241 --729760741 --129873703 --320749013 --273217206 --618115708 --414092230 --167608250 --654510463 --714582930 --553819095 --923016675 --832384377 --497192799 --141836652 --28081615 --630225867 --826330117 --243714229 --566951266 --698157404 --364346692 --556116327 --113513494 --145246917 --348235725 --75525587 --46779078 --3660799 --101342025 --436260420 --819089148 --454035212 --745220569 --176677608 --474589782 --569238943 --931178262 --861186057 --332513904 --399816676 --696758711 --694287918 --972865508 --871143142 --521944629 --810831946 --255256956 --781938410 --620774913 --411786016 --583445903 --985973424 --992556881 --782072398 --515758370 --455868169 --656227336 --242301175 --444208204 --522435434 --468227709 --729000049 --101498672 --8863065 --835539680 --191209274 --491172440 --611618486 --647359622 --431731859 --726227755 --145684341 --468179559 --488314449 --244153376 --760204407 --964809050 --837569137 --298657576 --532523716 --89181131 --589389119 --407346509 --71562908 --370458110 --433160306 --390770856 --879504666 --302118875 --389240939 --228802331 --260091727 --511967596 --949018294 --677502734 --168106556 --114893245 --57087259 --290444022 --506564505 --939790015 --982823198 --988542317 --371552303 --212974690 --44008314 --655690283 --146300092 --60940953 --801621085 --502810053 --63252184 --453667048 --778688628 --141688054 --176133945 --24996570 --989881930 --494412507 --152752914 --78066807 --573030960 --974839500 --146595457 --770569719 --936334025 --132031439 --146969634 --925466495 --743731839 --797671889 --140304673 --21800995 --241466320 --393009350 --920231925 --9033107 --45515968 --600039870 --684366668 --723986697 --112107303 --706809942 --965281800 --251856979 --428968572 --379029010 --700872310 --972757503 --819028469 --580848126 --306553195 --629037403 --588713322 --997195993 --560464336 --361907571 --157774961 --13872518 --962467811 --291110742 --679348545 --932949816 --216320936 --605636229 --327052578 --198180581 --292339560 --570358299 --74307471 --889932218 --781796628 --419860793 --846556274 --132607088 --334460796 --988372184 --261273573 --397982419 --794125685 --525951184 --261473076 --394358004 --813360660 --390755960 --88243141 --608763760 --41861411 --849713955 --913679533 --869662509 --167156107 --71655219 --543019558 --295103359 --340867652 --36480276 --696284237 --205395456 --763930022 --402320889 --38151457 --731075014 --45966384 --929305748 --921049530 --103481474 --896117347 --968558621 --440303383 --172100580 --135698265 --474237090 --184874511 --784812251 --705021763 --123882245 --750757260 --512629727 --839465043 --474638012 --503191634 --362305661 --446673538 --724358844 --87457744 --291677741 --830436303 --190489713 --46204781 --262040956 --312248991 --466698605 --732435819 --919307583 --717583714 --635285684 --190023250 --601411090 --257124480 --970320327 --616920853 --431867991 --758871489 --777086846 --174773069 --116366728 --807670492 --41263964 --26905509 --573430075 --315464355 --339654883 --421106696 --62185174 --751794698 --561359106 --784174518 --755770991 --938660051 --710435747 --286606907 --605402610 --646044510 --594871959 --513051907 --959186784 --960010845 --766756167 --357694694 --127329929 --664982304 --584787229 --593426765 --619614978 --439713605 --111938917 --524141900 --457049833 --783258481 --751702134 --382576860 --724152440 --581295697 --655456437 --480432168 --326692552 --84981879 --234759212 --480314889 --502975846 --633416203 --360644149 --889927695 --155407627 --275092260 --124765420 --752806441 --993901946 --479314552 --680617421 --862470087 --218586984 --6117093 --346650289 --814945557 --560298248 --409158669 --946546127 --823236459 --526686229 --614948427 --60713261 --1551415 --803722582 --861609729 --488413322 --788185511 --751086375 --214475345 --460373492 --181902849 --291523968 --323388892 --403254730 --239607721 --182939573 --539617946 --180222041 --191391395 --885298317 --117162121 --468931157 --226150910 --471392435 --730206195 --387789871 --682456947 --252314331 --863599530 --339651753 --675048755 --728822086 --645696568 --900115636 --387524215 --732549284 --418249894 --481353005 --142467349 --925801004 --674117631 --78825076 --589760944 --169742329 --770707812 --320806687 --174071760 --47267617 --416664806 --233531344 --135855304 --769849299 --282659628 --377428927 --544829619 --342637397 --609347255 --952656908 --806713596 --537847063 --721507262 --784517999 --623640471 --595985319 --426325239 --654548891 --662404770 --176873018 --722546544 --458729216 --649014857 --116805354 --51594582 --982021435 --461253986 --521575142 --396918056 --843161125 --137976113 --312905750 --71622244 --355564996 --805652548 --788667382 --957119681 --798909976 --385399353 --230436 --238282408 --526768754 --105983 --479295692 --278129842 --899929595 --496829674 --269059254 --900391674 --408293560 --21995703 --531092701 --496302486 --828509873 --795896315 --8157024 --920051480 --927359365 --620370171 --605544864 --615807299 --170737488 --701858708 --197415004 --989933576 --372332339 --438542024 --952290591 --566628116 --992649240 --568782485 --19980779 --154663077 --776348195 --221774119 --704965851 --893814018 --165625599 --998113467 --881894367 --336473212 --236199810 --217074384 --255367396 --526207101 --400872684 --820092204 --65459307 --115946654 --238788169 --418201795 --443498455 --92323390 --132328014 --823254582 --452553920 --344077931 --149974308 --920496641 --683511472 --387348880 --824408604 --405956465 --237792549 --884057121 --515295833 --123658025 --274363593 --345571985 --339822395 --415084359 --463912606 --208977429 --476421337 --501712501 --111796430 --582460046 --94883519 --858989646 --929536714 --985621720 --946492538 --600494901 --521291520 --806226474 --574422779 --490042424 --946332765 --487786012 --688521350 --933637459 --521482212 --189775507 --858042167 --642029138 --249482536 --560213590 --817383893 --620283282 --161420460 --703806817 --162461497 --655730542 --577665649 --319382330 --427768080 --258254012 --14218567 --966772290 --66292516 --757538303 --549645833 --103520425 --488064621 --9807556 --770246319 --929681966 --945854242 --351886201 --272262527 --896159103 --924876690 --441926912 --655037325 --143026623 --166046919 --537590327 --632499725 --980921810 --169667928 --970473965 --772489260 --49594787 --700492824 --82301023 --617853329 --752932967 --280602925 --695853613 --888940078 --169780026 --975649672 --811539311 --91875227 --823314933 --852871053 --577956828 --520469138 --743509623 --92591136 --230018825 --345446172 --266161301 --46026696 --685143832 --612838056 --343683502 --993491660 --686817219 --144101072 --675540319 --883075859 --717547772 --264536036 --31580298 --194339257 --3016322 --103909081 --101254090 --530157939 --122826078 --544749493 --735952610 --668398792 --454162526 --439310679 --179274011 --971230130 --138327564 --441035531 --277280045 --967046636 --997180089 --749449774 --268449866 --686711833 --642425698 --210794763 --10006369 --277333322 --104023702 --340969333 --557793108 --513031902 --88938884 --733048885 --374762340 --493609675 --238845162 --932911386 --490222339 --450670044 --448179250 --777650199 --395058404 --395494150 --305876949 --134392050 --838999643 --186885918 --47732132 --905502636 --57292166 --928441833 --139529552 --360718985 --237721737 --432461240 --690558300 --523759586 --532202950 --658435299 --537564261 --477860422 --365029508 --49812379 --901090772 --394774417 --759576087 --106723266 --468141548 --149756983 --893777677 --966995740 --13510975 --856096940 --531918545 --209662529 --823770614 --619849081 --202023695 --971107013 --998202557 --901655758 --201657380 --748254639 --519525808 --313166204 --559054609 --75231948 --936134949 --631272813 --548869329 --334008760 --353476596 --325137224 --493646167 --919759982 --263677298 --79882310 --409562217 --685891212 --185523639 --534793934 --362399709 --221946400 --674345238 --274177243 --553455372 --723959794 --387557545 --959752660 --121724769 --194096466 --413509733 --599226360 --630208513 --257270469 --592739289 --816456243 --642520900 --82846596 --829596895 --278860700 --161066769 --101030223 --605952486 --175850722 --798469691 --500695382 --232198780 --333091096 --100981394 --259814162 --286661733 --820288604 --772509248 --501662577 --481642988 --91204779 --58108423 --501938352 --560305004 --74932642 --551999340 --417377759 --959644657 --857929006 --256695489 --739231796 --658972509 --600089508 --856097635 --578084070 --243318741 --517510782 --216887330 --28742086 --604696954 --594995136 --271115965 --994227723 --746892090 --104154111 --918144936 --879130460 --338564771 --496706783 --167743860 --69099112 --801314067 --194577677 --286581129 --301578912 --422167220 --142685740 --465270766 --189501196 --841601181 --587278206 --573355206 --274048708 --63632875 --443645502 --898355339 --256694269 --147785012 --968938720 --993763746 --126570312 --887620818 --772568503 --602906091 --623180271 --710778329 --509949962 --17991405 --709487852 --470720979 --879919668 --111005457 --818542991 --491797681 --793136765 --506057741 --763577738 --215836057 --937328589 --14485607 --125800049 --954425961 --81804548 --948047380 --472973547 --71760227 --73567122 --856531162 --920467822 --857273801 --175726783 --446610383 --222888150 --688454405 --264832326 --248067707 --213143884 --287185527 --815485152 --680287898 --118225649 --812693501 --891746723 --286803017 --448043578 --170709854 --118410818 --21161121 --387003535 --250083358 --663310154 --856354844 --358016243 --179025066 --618758275 --665925158 --589415831 --891261567 --920493114 --816938375 --574533299 --107135197 --615277620 --338188507 --73191554 --725749171 --494631957 --204427734 --402250735 --891389787 --36753367 --845920119 --709181728 --726335672 --247504966 --577862132 --534032908 --165583484 --816562571 --564792790 --985378961 --300220425 --320758115 --657065472 --874302043 --127297530 --747115112 --287499639 --901247843 --537135420 --600640788 --136080066 --863632549 --412026581 --378823845 --96348981 --667886894 --647917652 --199569885 --20042915 --309848767 --473733523 --993299338 --439589078 --485619840 --551289479 --459941438 --750108188 --133104360 --869974453 --464290155 --474342744 --359129071 --56005917 --428174561 --448742736 --765791305 --274016686 --194006883 --753622278 --938860877 --735462462 --715940748 --304119317 --615783025 --207167339 --54743086 --215659041 --120318587 --309164825 --251372664 --475349424 --411449264 --310459637 --315378698 --282029064 --62324734 --96704664 --472016749 --598932002 --763875003 --343088611 --996266003 --536388419 --659514180 --314704731 --745094030 --777917803 --202634820 --526619224 --148870275 --540844475 --336858040 --113551447 --549667595 --528583209 --11533016 --945474277 --386234655 --382292110 --925915126 --99804729 --423473031 --654787787 --562720441 --133732905 --195892397 --188827369 --608689713 --38955971 --813394510 --686368518 --38935855 --887461734 --769313063 --490761477 --851640905 --285983488 --328067703 --663874739 --702838326 --373608816 --893205533 --28637410 --429714698 --676910830 --789491403 --151189264 --515122382 --722305362 --871411451 --250320880 --585258632 --832279357 --815717185 --261532915 --487461564 --74489669 --450719560 --601504428 --117680436 --509010231 --959975906 --382015838 --878784633 --966379338 --511978163 --226578788 --184801880 --790699660 --145864224 --533252806 --296588731 --175983449 --205821452 --853817756 --29822070 --39787774 --759018029 --183854665 --171373146 --798145678 --473480202 --918894795 --372023685 --468246772 --341586642 --262464840 --161466127 --755208280 --542266208 --62053798 --893882131 --959564162 --993180006 --830818354 --493568813 --456440605 --970622269 --222038819 --556622299 --320330412 --270845505 --775125332 --274717566 --843668481 --55125259 --749323975 --352489753 --698224387 --912778661 --253584443 --468866854 --187871519 --251133113 --103373034 --553077307 --59993859 --646161661 --127986605 --558254430 --487318118 --854741632 --346826965 --316386602 --512915412 --368663859 --481965009 --820050680 --647041866 --102662768 --192652260 --599935727 --538531988 --753371056 --511504146 --377377094 --263169126 --724806438 --193755610 --227339804 --883423602 --847571096 --373842498 --738390894 --513946266 --592913130 --976988413 --661748624 --907116786 --281880827 --454055303 --216717615 --783769198 --211847236 --558435207 --373480007 --582434757 --792133935 --116430427 --317955358 --812934261 --949785600 --86842630 --73750575 --77540392 --263614680 --995456722 --121296926 --489637629 --827699733 --359757767 --65389788 --12916121 --306353087 --31505644 --822307812 --308288809 --402253329 --931717130 --83523113 --856862106 --979960155 --989170015 --577282874 --615892028 --720831818 --37460085 --36796834 --18110623 --375244139 --312280051 --419224345 --743451922 --736866772 --796605462 --984406466 --556358894 --618580218 --4212648 --177906224 --59497938 --411691760 --812481217 --696972515 --937849944 --82012169 --592121722 --381912740 --858954645 --655910829 --77188594 --316752374 --52915405 --580323501 --446016367 --163041924 --739380036 --724822098 --136893828 --770658212 --356741971 --984762910 --661061297 --581141934 --487061982 --893696908 --185508689 --45693516 --130111912 --121921732 --70735722 --823552346 --357125253 --517327295 --469721367 --448647441 --674618422 --501756153 --176357937 --289292507 --645009699 --940145156 --448102235 --981763781 --17916780 --85295931 --629001245 --391511405 --455585446 --480258930 --667835682 --663676512 --564007502 --416956865 --638784249 --562878875 --855385016 --150914743 --467172052 --918038036 --520638454 --260455319 --587760060 --637029394 --547297899 --798787059 --957028620 --954784999 --394593665 --811598843 --799742163 --385055573 --266042551 --933919288 --874123029 --154488797 --15041088 --37592181 --257538887 --300313834 --678165281 --519438450 --499691787 --117395250 --492640431 --325508250 --819929235 --221596360 --506334492 --613798233 --245311267 --149320026 --865659354 --822325058 --242617069 --13885129 --228513562 --3232822 --234740203 --591327757 --766418519 --45278539 --421026176 --575437088 --539151217 --513056504 --873316841 --71537050 --36835876 --274892065 --666703233 --38777142 --107769403 --347901749 --71053831 --201115462 --925713061 --431186952 --418495918 --713306768 --893611402 --542484477 --519661528 --962838105 --919496330 --874901633 --984086635 --33996085 --76801391 --825441401 --118425594 --193771979 --880481424 --521190454 --439484935 --668328037 --386082012 --85090872 --311501847 --371399631 --730670278 --690398631 --846220110 --764403615 --162012760 --866273363 --140574112 --954351403 --357051649 --697009892 --479109681 --263405121 --632677479 --551570944 --935885622 --779957595 --250131778 --168856276 --821807397 --762076186 --287759974 --615607144 --752343585 --734062184 --649090938 --388839124 --875786944 --898925147 --487726809 --858847215 --961475252 --900114462 --143186997 --92468905 --631939858 --469734768 --568354469 --773106153 --842682331 --849071759 --606324604 --487008319 --794891824 --917092658 --171773375 --586064844 --53807868 --920639170 --23734916 --86505572 --107894068 --33956683 --433746214 --961255237 --833560437 --14724733 --4369619 --420648601 --56281953 --895229706 --898583418 --799816330 --265710476 --950767814 --428265987 --637841848 --139933694 --286314771 --742422033 --860887805 --231331940 --321035161 --929893075 --548486854 --512922475 --710820075 --933995971 --815427946 --850548431 --949434981 --501521924 --502557161 --225354861 --291027373 --499464406 --968471719 --380070603 --691494329 --90340437 --497199483 --834666489 --59175279 --948909352 --391611117 --785749130 --11041465 --355228923 --528045303 --209319240 --204210302 --369329414 --126024582 --929013465 --424900558 --478182480 --563885233 --408095562 --448700579 --140278888 --258009346 --777163149 --722809553 --853790798 --488920193 --907760765 --376133017 --331589558 --696008499 --512643720 --955082710 --622701011 --353216265 --359516383 --171155960 --766824216 --618070141 --511130612 --886021264 --928066752 --926766387 --182055463 --670717722 --881783521 --246736524 --655542859 --880372104 --248838241 --340912985 --473286595 --258635919 --939041794 --127477425 --671941734 --430346409 --234472230 --917046872 --10295756 --907053608 --815634119 --322382160 --422406349 --679969264 --490326150 --465652448 --948655904 --556731331 --565753657 --867796898 --838477494 --466827553 --625334302 --144133516 --413715395 --290852447 --248616656 --940845663 --355318097 --179483719 --162857555 --996164545 --239606853 --810920083 --495501975 --830101350 --840740932 --594671305 --399162717 --351775059 --36459207 --287835245 --994328505 --302261466 --45075054 --121966881 --574171161 --361096543 --834464917 --544828964 --739176180 --351399738 --663006332 --163653866 --914127945 --581925261 --534577316 --66344522 --791171683 --982106048 --914245958 --568795494 --415616219 --103196431 --239134289 --483166159 --83473258 --247194034 --109412766 --735785092 --158069660 --152337315 --128285385 --312491117 --685183244 --590701742 --84486152 --837584667 --440120813 --2706811 --452131758 --583667510 --399305142 --271493183 --650815334 --245425644 --306997205 --960087116 --914744823 --165155527 --716279630 --903146933 --242046504 --438851611 --376326796 --512688862 --820012049 --678972902 --214773459 --640445530 --421134433 --458419608 --993432991 --894738687 --596868157 --898382073 --367540594 --936262 --617257933 --556211819 --606896463 --451173819 --262866159 --114055113 --818435988 --975577550 --965319759 --400603100 --161257958 --410505708 --378348801 --834307066 --201146298 --969583718 --584049638 --421103676 --528547277 --909965225 --809471992 --98432664 --386271426 --601785802 --652023316 --94631003 --70031760 --840710198 --878669688 --502222464 --728483741 --877239285 --666635492 --447690729 --914901428 --463513930 --721688943 --961134700 --234704600 --944918122 --678380168 --297429988 --638366492 --57144680 --116924713 --746059678 --549110732 --573835880 --406185073 --523437398 --480220147 --505531947 --77305335 --711926078 --900081367 --70946684 --397539088 --678253181 --904029210 --69783242 --687651610 --419155557 --867256260 --141408045 --319809717 --207778253 --776737719 --82774689 --656353191 --532178037 --268823809 --14021708 --810589352 --307822012 --969988319 --31634683 --743884035 --201253111 --365152707 --233036671 --773946991 --525210256 --731086426 --395212354 --281680805 --119059796 --606180687 --565168491 --991648940 --26441869 --673534449 --658944505 --190315183 --853235929 --908292540 --644570078 --364378359 --164563529 --579224156 --557860046 --455263426 --137123817 --87162224 --110853463 --895928799 --236796521 --634712895 --733581788 --970060153 --109082921 --463783529 --832142405 --16490219 --263674732 --827954803 --372956815 --398980865 --186521060 --957676302 --737166339 --764263991 --198560384 --576408275 --779951409 --827046590 --989211270 --663835846 --331168229 --713313528 --605170581 --623679500 --475847410 --303185906 --578054774 --484919881 --84558546 --903476582 --562100702 --807495302 --926775360 --869148136 --107578481 --680718122 --588209061 --503225977 --940828736 --555489881 --217244336 --974963417 --468270591 --310490944 --942855862 --674302700 --554722432 --474736925 --947763927 --214054253 --941770120 --778944392 --783689980 --508443630 --238075241 --504554018 --299912812 --3321074 --477208055 --802966550 --756372920 --74367964 --111941821 --781605545 --150334889 --71293198 --181223532 --515739189 --43546697 --906499454 --407832946 --874010777 --253174361 --720800115 --251434948 --8902177 --456742624 --654658785 --954180400 --739448179 --930161514 --114511917 --824315553 --638955300 --161120864 --130152816 --210846238 --408094754 --796070668 --121436111 --19352082 --505456616 --502106465 --637048617 --174836747 --822834143 --607229671 --81500523 --595403301 --664091756 --325470611 --662254166 --168321115 --887951418 --435126553 --451222731 --123004599 --847483431 --818941821 --305674752 --446346065 --124344002 --549848301 --856355200 --396873641 --724819890 --433734141 --563824626 --881514875 --659339449 --676839600 --274510375 --882132731 --478211230 --136375640 --811767838 --830423700 --239574750 --156556247 --19410495 --625961664 --859753775 --180907910 --370162922 --706503608 --69722790 --927991669 --79980954 --228311366 --383233778 --88229983 --324049403 --956368622 --411911204 --348553649 --379989589 --666979640 --648581379 --947191703 --293155500 --275454887 --821948931 --248480958 --720333546 --277754466 --805807536 --911845322 --459700689 --953220603 --519557320 --645019174 --62238823 --957900729 --339427158 --416618140 --245168998 --187153761 --631583018 --247440954 --40802049 --110636460 --126686799 --515370854 --760414945 --370432064 --373706418 --604529817 --756386545 --695965794 --391615197 --518389444 --634030386 --466849336 --494397182 --288965750 --100959918 --292421097 --91700733 --724790914 --58836449 --609339956 --189325518 --698222737 --483171212 --364391619 --188488725 --728696942 --646627113 --783636345 --464556192 --60944847 --114224114 --868697783 --733503457 --864214248 --917544492 --238606854 --308501590 --968890548 --481569750 --536677017 --636231527 --818399353 --18756311 --499827522 --645789824 --803589787 --70409878 --915247116 --291435743 --153885902 --434149656 --948921776 --434236974 --416855166 --300856409 --691041902 --444685487 --561095442 --993370301 --958260549 --955646683 --517324691 --555985202 --772923825 --803759841 --288097445 --761048952 --619040231 --811227317 --976977366 --510354454 --166698933 --890076286 --857303887 --761432855 --242315651 --164334626 --79783170 --482811448 --994869808 --677799815 --881462161 --266875321 --808015301 --465601773 --198192509 --542549612 --9315279 --445210608 --202186584 --844499576 --535944372 --162698138 --375954678 --747575353 --505852318 --621530839 --302549281 --419995448 --721634834 --361148907 --169365438 --65972636 --620946846 --772249398 --408951565 --760009559 --206073853 --522918601 --17712031 --781952946 --798617560 --802508640 --66289987 --897129007 --76168510 --501388332 --257195545 --671171810 --336633605 --831081077 --431936970 --78281840 --665922934 --309224940 --355164962 --834429942 --231824558 --657637196 --238903464 --321012891 --923709571 --985266256 --688484977 --745177599 --120686017 --252006413 --583845517 --314816664 --238353009 --358148568 --449651674 --954004576 --976899038 --978134515 --781665080 --853730668 --7713022 --278949536 --409218798 --823058948 --130486454 --696812329 --695999901 --815953587 --721345894 --257742931 --709213054 --557957149 --663091620 --939570836 --135916571 --289685199 --958406787 --182041702 --95818386 --179451063 --835676298 --321483210 --14292756 --545492592 --817560244 --885452999 --575048188 --25184449 --914759832 --200886451 --470166703 --579666165 --463555537 --724872146 --87179994 --537557000 --181851917 --655346533 --668976076 --224318419 --792919542 --824441086 --680336737 --718175423 --629708715 --458078882 --691965575 --311347507 --499731887 --11608001 --97513928 --101780355 --860322343 --771642228 --806828520 --198604858 --403409941 --113475443 --311410308 --673550854 --661936370 --453331457 --549062665 --923801867 --591498690 --216579850 --59794706 --654842345 --72865128 --105104743 --301770043 --18214363 --827743108 --255035253 --814870192 --499822147 --446470160 --251708619 --536872154 --296370992 --525432064 --271958536 --237225178 --166765724 --920373258 --673171333 --195622996 --587764882 --8763034 --787105375 --266685706 --680269958 --202321519 --172439441 --815552301 --841154794 --830039905 --887833111 --257539302 --902502253 --222646063 --890619483 --846801889 --865066946 --172979132 --632855132 --933521756 --518648655 --796045557 --11862842 --767051541 --271245602 --397998327 --421291398 --123536770 --898367831 --89105453 --176531993 --803711121 --69348387 --707540525 --199604825 --206861832 --729536539 --814384295 --55064081 --276583897 --105973121 --385210295 --205228841 --410073324 --270513976 --340250146 --582110876 --411199024 --57721553 --483944731 --513990081 --66749725 --876477514 --18857010 --38329305 --165335170 --505157442 --964761928 --209298686 --592575708 --434395539 --74856789 --773821008 --449443916 --621844832 --51374166 --502690010 --39268096 --568676959 --367745136 --808420958 --442029316 --58720171 --983991993 --758198096 --585397315 --438841110 --13914048 --253712401 --874881178 --849735593 --104813747 --40299958 --313155101 --850967997 --198464170 --754952831 --349881774 --53322230 --235863960 --862379272 --978902221 --954692554 --463963953 --456489843 --40018849 --739460696 --401648194 --665912641 --141899678 --921820986 --783330025 --321915648 --540132391 --227659666 --774692539 --450071856 --164058090 --229055032 --290413666 --463931955 --212837137 --842484 --508470399 --275776406 --896548827 --601353828 --23564046 --848211069 --934607616 --436142372 --619769904 --73210852 --967746868 --509720298 --652548291 --158002853 --873505246 --288375109 --206462419 --173155303 --833562545 --840491213 --908987365 --364039659 --526302256 --451174015 --441597227 --599237417 --113948457 --111769864 --834947189 --602158065 --969204789 --596709564 --137049182 --299892619 --937292041 --553283953 --668863226 --94786119 --736437133 --562931020 --424288574 --927176131 --732603138 --756264175 --877745353 --362380150 --653977795 --791061448 --395495453 --25274081 --212169035 --837795236 --340582700 --488516644 --306079673 --601957666 --273617286 --20502436 --842533127 --182481904 --776536115 --842319745 --801888723 --165437208 --712283969 --439468176 --983342853 --79405473 --747956348 --38366770 --220447046 --283030913 --971758970 --705113262 --277413669 --178791532 --56815381 --896452555 --956433645 --452828225 --421034329 --873477490 --192340215 --223557934 --785066164 --637455614 --180730103 --491292676 --544412589 --909198957 --551606947 --31290669 --896537523 --550981149 --786590085 --358593377 --287383718 --718300336 --696061364 --478362289 --345657706 --792918479 --435864845 --524049921 --560325251 --268736907 --209653963 --862868306 --482945649 --789863857 --504119930 --907260278 --88102124 --547676722 --834351589 --169762893 --44717282 --629899527 --892490915 --40363991 --378529776 --17180042 --422309773 --971971555 --827949442 --921388371 --709134610 --283702763 --468257254 --780907911 --618947736 --979770989 --596296595 --192877781 --77806928 --757460089 --210488234 --76132935 --983347301 --704539406 --196547390 --980583735 --487744080 --490188689 --469121238 --537686401 --895987617 --555486178 --749368109 --525983446 --198091956 --561160608 --206733114 --929597041 --502623930 --294723558 --521580105 --945223719 --253181621 --418268130 --171540947 --343034291 --759209363 --413955551 --256719326 --376132438 --163403604 --214927784 --982614175 --116915824 --154283886 --170231113 --119705193 --423357887 --834360479 --958671487 --361852026 --625317452 --775625281 --463273870 --407502193 --979827472 --410927749 --309672294 --540945309 --924623695 --821871726 --539338153 --168467367 --469394954 --128850308 --596399321 --852195502 --607483899 --777279370 --454692002 --797310217 --67000550 --351318449 --781930306 --514247522 --502309762 --740756164 --435479919 --160867517 --214661250 --471260755 --739506780 --241493785 --86278551 --625909317 --439944146 --554151898 --719870394 --551704832 --767313416 --446760147 --179896037 --992067178 --910660400 --289531211 --93532442 --518122994 --604688346 --438541766 --265354206 --281212805 --214780216 --314674009 --627248336 --491361879 --210572732 --651701690 --500649652 --120990769 --789771782 --651065546 --993166120 --301399319 --559638534 --642137117 --897325003 --915030963 --75609362 --944328595 --168356128 --476329696 --240824489 --550665772 --637400541 --625793527 --462497472 --577383549 --708951918 --794479023 --176818182 --281421736 --256731510 --519741996 --822598494 --510206295 --654874701 --482779324 --31240274 --870829686 --203212651 --246556970 --364513013 --561691858 --197308742 --48171407 --379391436 --480772798 --455703775 --441664423 --915970970 --836052945 --306057365 --114118742 --499751403 --721096675 --227441025 --348755321 --59770208 --583061734 --624974886 --542815079 --229318093 --941378522 --28582884 --810027171 --777890524 --997213276 --982879002 --992950856 --646163479 --690240811 --238415494 --735834578 --961475844 --497302471 --754008300 --343284267 --83786013 --243158316 --977529064 --588538228 --345181703 --885770520 --814815783 --735964969 --53802113 --971414466 --878135279 --316620412 --645011147 --970826253 --243242917 --889483100 --916862471 --4815937 --874126302 --757189184 --818160815 --318191113 --397813767 --789583968 --433378941 --891380320 --194060132 --439982115 --133835543 --840767507 --768798934 --774421694 --171240390 --513748484 --483953600 --590001856 --844245052 --697010105 --582709374 --383359897 --789918966 --738082458 --760174197 --157833830 --608188088 --457851783 --374881549 --939703975 --577470264 --130691852 --412009623 --790360796 --411860074 --866814400 --266620861 --519879445 --278215754 --517479966 --532971012 --907566602 --565716983 --601550245 --300918080 --519113909 --88098229 --157873644 --839897819 --23549247 --998434913 --883133875 --408951466 --225914813 --397059969 --296683386 --227391600 --28873667 --928556307 --774435672 --169469019 --140130826 --723776362 --32909767 --1037516 --250976263 --804277432 --803586227 --496194525 --40019691 --209356683 --129926932 --583864444 --761316415 --692046685 --153696777 --521681009 --52998787 --536720004 --32363723 --724581832 --58633751 --782923462 --93864902 --846707348 --949654512 --213695860 --625280112 --490442880 --289394902 --176698988 --976787743 --887999706 --805747051 --532791690 --113287616 --781700580 --150536261 --672152631 --883072056 --992129896 --121170027 --454851750 --259589538 --105550197 --24054951 --830120419 --30458214 --60146112 --101714269 --420097027 --893950614 --129502135 --749731655 --969442976 --259970105 --831385276 --420868054 --93488743 --650469750 --269809527 --272879195 --142810681 --809306581 --835160181 --889436496 --511230497 --750754668 --826768850 --276660186 --743641260 --125867484 --552265458 --947075592 --661161103 --627563510 --214474819 --871251080 --865000327 --586304746 --330267482 --339493717 --711868627 --349003057 --142354512 --651387016 --130867486 --470013277 --706574811 --774678167 --84246618 --621323809 --552534581 --319228603 --87665414 --823082164 --997053540 --566316881 --664502821 --208511711 --538146889 --814238146 --633832264 --260934326 --752622468 --383941847 --335160442 --924309170 --508397180 --564576629 --75439042 --950564334 --295935387 --448655513 --844257707 --451400476 --409381555 --389913254 --234787503 --178101370 --27071216 --434144193 --273588860 --822079254 --407173026 --3750137 --622682664 --122801917 --991781359 --808374369 --483779865 --205012751 --715959418 --895663799 --204126661 --967222648 --170858431 --906899619 --628831821 --714790964 --348728653 --727024574 --656703346 --230297099 --428850226 --535452768 --723051487 --928254239 --493217689 --786367260 --437928684 --695727034 --580828066 --130952033 --67994735 --515491453 --289457153 --595278704 --103414972 --761084661 --901076502 --736519501 --11844290 --999070494 --822817103 --746946567 --807643526 --904308295 --771946688 --15968508 --835895233 --326481319 --638553102 --352592695 --531879373 --452990486 --221284593 --831843499 --60138798 --469485713 --717930571 --99265560 --383668091 --239552392 --636845203 --798642366 --490956959 --66240910 --215803373 --172938875 --273836128 --410373061 --425722144 \ No newline at end of file diff --git a/list/sort/benchmark.html b/list/sort/benchmark.html deleted file mode 100644 index bdbab2c730d..00000000000 --- a/list/sort/benchmark.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - -

        can.List.sort Benchmarks

        - -

        - -
        -

        -
          -
          - - - - \ No newline at end of file diff --git a/list/sort/simple_sort.html b/list/sort/simple_sort.html deleted file mode 100644 index 9e5f5801b20..00000000000 --- a/list/sort/simple_sort.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - diff --git a/list/sort/sort.html b/list/sort/sort.html deleted file mode 100644 index dc8a34c60c5..00000000000 --- a/list/sort/sort.html +++ /dev/null @@ -1,106 +0,0 @@ -
          - - diff --git a/list/sort/sort.js b/list/sort/sort.js deleted file mode 100644 index 6e8a16b6458..00000000000 --- a/list/sort/sort.js +++ /dev/null @@ -1,435 +0,0 @@ -var can = require('can/util/util'); -require('can/list/list'); - -// BUBBLE RULE -// 1. list.bind("change") -> bubbling -// list.unbind("change") -> no bubbling - -// 2. list.attr("comparator","id") -> nothing -// list.bind("length") -> bubbling -// list.removeAttr("comparator") -> nothing - -// 3. list.bind("change") -> bubbling -// list.attr("comparator","id") -> bubbling -// list.unbind("change") -> no bubbling - -// 4. list.bind("length") -> nothing -// list.attr("comparator","id") -> bubbling -// list.removeAttr("comparator") -> nothing - -// 5. list.bind("length") -> nothing -// list.attr("comparator","id") -> bubbling -// list.unbind("length") -> nothing - -// Change bubble rule to bubble on change if there is a comparator. -var oldBubbleRule = can.List._bubbleRule; -can.List._bubbleRule = function(eventName, list) { - var oldBubble = oldBubbleRule.apply(this, arguments); - - if (list.comparator && can.inArray('change', oldBubble) === -1) { - oldBubble.push('change'); - } - - return oldBubble; -}; - -var proto = can.List.prototype, - _changes = proto._changes || function(){}, - setup = proto.setup, - unbind = proto.unbind; - -can.extend(proto, { - setup: function (instances, options) { - setup.apply(this, arguments); - this.bind('change', can.proxy(this._changes, this)); - this._comparatorBound = false; - - this.bind('comparator', can.proxy(this._comparatorUpdated, this)); - delete this._init; - - if (this.comparator) { - this.sort(); - } - }, - _comparatorUpdated: function(ev, newValue){ - if( newValue || newValue === 0 ) { - this.sort(); - - if(this._bindings > 0 && ! this._comparatorBound) { - this.bind("change", this._comparatorBound = function(){}); - } - } else if(this._comparatorBound){ - unbind.call(this, "change", this._comparatorBound); - this._comparatorBound = false; - - } - }, - unbind: function(ev, handler){ - var res = unbind.apply(this, arguments); - - if(this._comparatorBound && this._bindings === 1) { - unbind.call(this,"change", this._comparatorBound); - this._comparatorBound = false; - } - - return res; - }, - _comparator: function (a, b) { - - var comparator = this.comparator; - - // If the user has defined a comparator, use it - if (comparator && typeof comparator === 'function') { - return comparator(a, b); - } - - // Compare strings correctly in all languages - if (typeof a === 'string' && typeof b === 'string' && - ''.localeCompare) { - return a.localeCompare(b); - } - - return (a === b) ? 0 : (a < b) ? -1 : 1; - }, - _changes: function (ev, attr, how, newVal, oldVal) { - var dotIndex = ("" + attr).indexOf('.'); - - // If a comparator is defined and the change was to a - // list item, consider moving the item. - if (this.comparator && dotIndex !== -1) { - if (ev.batchNum) { - if (ev.batchNum === this._lastProcessedBatchNum) { - return; - } else { - this.sort(); - this._lastProcessedBatchNum = ev.batchNum; - return; - } - } - - var currentIndex = +attr.substr(0, dotIndex); - var item = this[currentIndex]; - var changedAttr = attr.substr(dotIndex + 1); - - // Don't waste time evaluating items in ways that aren't - // relevant or have changed in ways that aren't relevant. - if (typeof item !== 'undefined' && - (typeof this.comparator !== 'string' || - this.comparator.indexOf(changedAttr) === 0)) { - - // Determine where this item should reside as a result - // of the change - var newIndex = - this._getRelativeInsertIndex(item, currentIndex); - - if (newIndex !== currentIndex) { - this._swapItems(currentIndex, newIndex); - - // Trigger length change so that {{#block}} helper - // can re-render - can.batch.trigger(this, 'length', [ - this.length - ]); - } - - } - } - _changes.apply(this, arguments); - }, - - _getInsertIndex: function (item, lowerBound, upperBound) { - - var insertIndex = 0; - var a = this._getComparatorValue(item); - var b, dir, comparedItem, testIndex; - - lowerBound = (typeof lowerBound === 'number' ? - lowerBound : 0); - upperBound = (typeof upperBound === 'number' ? - upperBound : this.length - 1); - - while (lowerBound <= upperBound) { - testIndex = (lowerBound + upperBound) / 2 | 0; - comparedItem = this[testIndex]; - b = this._getComparatorValue(comparedItem); - dir = this._comparator(a, b); // -1 === a < b; 1 === a > b - - - if (dir < 0) { // Compared item > our item - upperBound = testIndex - 1; - } else if (dir >= 0) { // Compared item <= our item - lowerBound = testIndex + 1; - insertIndex = lowerBound; - } - } - - return insertIndex; - }, - - _getRelativeInsertIndex: function (item, currentIndex) { - var naiveInsertIndex = this._getInsertIndex(item); - var nextItemIndex = currentIndex + 1; - var a = this._getComparatorValue(item); - var b; - - // Don't count the item being moved itself - which would - // cause something like this: - // [1(a, b), 2, 3] // i = 0; a === b; Don't swap; - // [1(a), 2(b), 3] // i = 1; a < b; Do swap (a) from 0 to 1; - // .splice(0, 1) // [2, 3] - // .splice(1, 0, a) // [2, 1, 3] - if (naiveInsertIndex >= currentIndex) { - naiveInsertIndex -= 1; - } - - // If a forward swap is suggested by _getInsertIndex, inspect - // the next item for the same value. Otherwise, we may be - // needlessly leapfroging over same value items to be naively - // positioned before an item with a greater value. Otherwise, - // the naiveInsertIndex is totally valid. - if (currentIndex < naiveInsertIndex && nextItemIndex < this.length) { - b = this._getComparatorValue(this[nextItemIndex]); - - if (this._comparator(a, b) === 0) { - return currentIndex; - } - } - - return naiveInsertIndex; - }, - - /** - * @returns {number} The value that should be passed to the comparator - **/ - _getComparatorValue: function (item) { - - // Get the user supplied comparator - var comparator = this.comparator; - - // If the comparator is a string, use it to get the value of that - // property on the item. Example: - // list.comparator = 'prop'; // -> item.attr('prop'); - // list.comparator = 'method'; // -> item['method'](); - // If the comparator is a method, don't do anything. - if (item && comparator && typeof comparator === 'string') { - item = typeof item[comparator] === 'function' ? - item[comparator]() : - item.attr(comparator); - } - - return item; - }, - - _getComparatorValues: function () { - var self = this; - var a = []; - this.each(function (item, index) { - a.push(self._getComparatorValue(item)); - }); - return a; - }, - - sort: function (comparator) { - - if (arguments.length) { - - // Set the comparator; Sort if the value changed - this.attr('comparator', comparator); - } else { - - // Sort without a comparator - this._sort(); - } - - return this; - }, - - _sort: function () { - var a, b, c, isSorted; - - for (var i, iMin, j = 0, n = this.length; j < n-1; j++) { - iMin = j; - - isSorted = true; - c = undefined; - - for (i = j+1; i < n; i++) { - - a = this._getComparatorValue(this.attr(i)); - b = this._getComparatorValue(this.attr(iMin)); - - // [1, 2, 3, 4(b), 9, 6, 3(a)] - if (this._comparator.call(this, a, b) < 0) { - isSorted = false; - iMin = i; - } - - // [1, 2, 3, 4, 8(b), 12, 49, 9(c), 6(a), 3] - // While iterating over the unprocessed items in search - // of a "min", attempt to find two neighboring values - // that are improperly sorted. - // Note: This is not part of the original selection - // sort agortithm - if (c && this._comparator.call(this, a, c) < 0) { - isSorted = false; - } - - c = a; - } - - if (isSorted) { - break; - } - - if (iMin !== j) { - this._swapItems(iMin, j); - } - } - - - // Trigger length change so that {{#block}} helper can re-render - can.batch.trigger(this, 'length', [this.length]); - - return this; - }, - - _swapItems: function (oldIndex, newIndex) { - - var temporaryItemReference = this[oldIndex]; - - // Remove the item from the list - [].splice.call(this, oldIndex, 1); - - // Place the item at the correct index - [].splice.call(this, newIndex, 0, temporaryItemReference); - - // Update the DOM via can.view.live.list - can.batch.trigger(this, 'move', [ - temporaryItemReference, - newIndex, - oldIndex - ]); - } - -}); - -can.each({ - /** - * @function push - * Add items to the end of the list. - * - * var l = new can.List([]); - * - * l.bind('change', function( - * ev, // the change event - * attr, // the attr that was changed, for multiple items, "*" is used - * how, // "add" - * newVals, // an array of new values pushed - * oldVals, // undefined - * where // the location where these items where added - * ) { - * - * }) - * - * l.push('0','1','2'); - * - * @param {...*} [...items] items to add to the end of the list. - * @return {Number} the number of items in the array - */ - push: "length", - /** - * @function unshift - * Add items to the start of the list. This is very similar to - * [can.List::push]. Example: - * - * var l = new can.List(["a","b"]); - * l.unshift(1,2,3) //-> 5 - * l.attr() //-> [1,2,3,"a","b"] - * - * @param {...*} [...items] items to add to the start of the list. - * @return {Number} the length of the array. - */ - unshift: 0 - }, - // adds a method where - // @param where items in the array should be added - // @param name method name - function (where, name) { - var proto = can.List.prototype, - old = proto[name]; - proto[name] = function () { - - if (this.comparator && arguments.length) { - // Get the items being added - var args = can.makeArray(arguments); - var length = args.length; - var i = 0; - var newIndex, val; - - // Increment, don't decrement in order to minimize the - // number of items after each subsequent .splice(); - while (i < length) { - - // Convert anything to a `map` that needs to be converted. - val = can.bubble.set(this, i, this.__type(args[i], i) ); - - // Get the sorted index - newIndex = this._getInsertIndex(val); - - // Insert this item at the correct index - // NOTE: On ultra-big lists, this will be the slowest - // part of an "add" because `.splice()` is O(n) - Array.prototype.splice.apply(this, [newIndex, 0, val]); - - // Render, etc - this._triggerChange('' + newIndex, 'add', [val], undefined); - - // Next - i++; - } - - // Render, etc - can.batch.trigger(this, 'reset', [args]); - - return this; - } else { - // Call the original method - return old.apply(this, arguments); - } - }; - }); - -// Overwrite .splice so that items added to the list (no matter what the -// defined index) are inserted at the correct index, while preserving the -// ability to remove items from a list. -(function () { - var proto = can.List.prototype; - var oldSplice = proto.splice; - - proto.splice = function (index, howMany) { - - var args = can.makeArray(arguments); - - // Don't use this "sort" oriented splice unless this list has a - // comparator - if (! this.comparator) { - return oldSplice.apply(this, args); - } - - // Remove items using the original splice method - oldSplice.call(this, index, howMany); - - // Remove the 1st and 2nd args so that the newly added - // items can be processed directly, rather than `.slice()` - // which creates a copy, or `for (...) { added.push(args[i]); }` - // which iterates needlessly - args.splice(0, 2); - - // Add items by way of push so that they're sorted into - // the correct position - proto.push.apply(this, args); - }; -})(); - - -module.exports = exports = can.Map; diff --git a/list/sort/sort.md b/list/sort/sort.md deleted file mode 100644 index 650fe76be22..00000000000 --- a/list/sort/sort.md +++ /dev/null @@ -1,125 +0,0 @@ -@page can.List.plugins.sort sort -@parent can.List.plugins -@plugin can/list/sort -@test can/list/sort/test.html - -Sort a [can.List] and keep it that way. - -@body - -## Use - -The [can.List.plugins.sort] plugin makes it easy to define -and maintain how items are arranged in a [can.List]. To use it, -set a `comparator` [can.Map::attr attr] on a [can.List]. It can be a -`String` or a `Function`. - -``` -var cart = new can.List([ - { title: 'Bread', price: 4.00 }, - { title: 'Butter', price: 3.50 }, - { title: 'Juice', price: 3.05 } -]); -cart.attr("comparator", 'price'); -cart; // -> [Juice, Butter, Bread] -``` - -Setting a comparator will sort the list immediately. But what's really nice -is that if your list is being listened to, it will automatically sort when -any of its items are changed: - -``` -var cart = new can.List([ - { title: 'Juice', price: 3.05 } - { title: 'Butter', price: 3.50 }, - { title: 'Bread', price: 4.00 } -]); -cart.attr("comparator", 'price'); -cart.bind("length", function(){}); -cart.attr("0.price", 5); -cart; // -> [Butter, Bread, Juice] -``` - -And it will keep sort order when items are pushed, unshifted, or spliced into the can.List: - -``` -var cart = new can.List([ - { title: 'Juice', price: 3.05 } - { title: 'Butter', price: 3.50 }, - { title: 'Bread', price: 4.00 } -]); -cart.attr('comparator', 'price'); -cart.bind("length", function(){}); -cart.push({ title: 'Apple', price: 3.25 }); -cart; // -> [Juice, Apple, Butter, Bread] -``` - -If you are using a `can.List` in a template, it will be bound to -automatically. Check out this demo that lets you change the sort order -and a person's name: - -@demo can/list/sort/simple_sort.html - -## Function Comparators - -When a `String` is defined the default comparator function -arranges the items in ascending order. To customize the sort behavior, -define your own comparator function. - -``` -var stockPrices = new can.List([ - 0.01, 0.98, 0.75, 0.12, 0.05, 0.16 -]); -stockPrices.attr("comparator", function (a, b) { - return a === b ? 0 : a < b ? 1 : -1; // Descending -}) -stockPrices // -> [0.98, 0.75, 0.16, 0.12, 0.05, 0.01]; -``` - -## String Comparators - -String comparators will be passed to [can.List.attr] to -retrieve the values being compared. - -``` -var table = new can.List([ - [6, 3, 4], - [1, 8, 2], - [7, 9, 5] -]); -table.attr('comparator', '2') // Translates to: row.attr('2') -table // -> [1, 8, 2], - [6, 3, 4], - [7, 9, 5] -``` - -The default sort order for string comparators is ascending. To sort -items in descending order use a function comparator. - -## Move Events - -Whenever there are changes to items in the [can.List], the -[can.List.plugins.sort] plugin moves the item to the correct -index and fires a "move" event. - -``` -var cart = new can.List([ - { title: 'Bread', price: 3.00 }, - { title: 'Butter', price: 3.50 }, - { title: 'Juice', price: 3.25 } -]); -cart.attr('comparator', 'price'); - -cart.bind('move', function (ev, item, newIndex, oldIndex) { - console.log('Moved:', item.title + ', from:', oldIndex + ', to:', newIndex); -}) - -cart.attr('0.price', 4.00); // Moved: Bread, from: 0, to: 3 - // -> [Juice, Butter, Bread] -``` - -## Changing Comparators - -A comparator can be set explicitly with [can.Map::attr attr], or implicitly -with the more familiar `list.sort(newComparator)`. Both have the same -effect. \ No newline at end of file diff --git a/list/sort/sort_benchmark.js b/list/sort/sort_benchmark.js deleted file mode 100644 index a785970d625..00000000000 --- a/list/sort/sort_benchmark.js +++ /dev/null @@ -1,96 +0,0 @@ -var can = require('can'); -var benchmarks = require('can/test/benchmarks'); -require('can/list/list'); -require('can/list/sort/sort'); - -can.ajax({ - url: '20k.txt' -}).then(function (file) { - - window.numbers = file.split('\n')/*.slice(0, 100000)*/; - window.setup = function () { - // Create a list - window.list = new can.List(); - - // Activate the sort plugin - window.list.attr('comparator', can.List.prototype._comparator); - - // Start the clock - window.s = +new Date(); - - // Reset - window.probe1 = 0; - window.probe2 = 0; - }; - - window.cleanup = function () { - // Stop the clock - // var time = +new Date() - window.s; - - // Compare against control - // var passed = _.isEqual(window.sortedNumbers, window.list.attr ? - // window.list.attr() : - // window.list); - - // Clear up memory - window.list.splice(0, window.list.length); - - // Print results - // console.log(time, window.probe1, window.probe2, passed); - }; - - // Make comparisons faster by using numbers, not strings - can.each(window.numbers, function (num, i) { - window.numbers[i] = parseInt(num, 10); - }); - - // Control - window.sortedNumbers = window.numbers.slice(0).sort(can.List.prototype._comparator); - - benchmarks.add('Sorting items via native .sort([comparator])', function () { - - // Display an intermittent timer - window.s = +new Date(); - - // Create a list - var list = window.list = window.numbers.slice(0); - list.sort(can.List.prototype._comparator); - - window.cleanup(); - }); - - benchmarks.add('Adding items via .push([list])', function () { - - window.setup(); - - // Add the items to the sorted list - window.list.push.apply(window.list, window.numbers); - - window.cleanup(); - }); - - benchmarks.add('Adding items via .splice(0, 0, [list])', function () { - - window.setup(); - - // Add the items to the sorted list - window.list.splice.apply(window.list, [0, 0].concat(window.numbers)); - - window.cleanup(); - }); - - benchmarks.add('Adding items via .each([list]) + .push([item])', function () { - - window.setup(); - - // Add the items to the sorted list - can.each(window.numbers, function (num) { - window.list.push(num); - }); - - window.cleanup(); - }); - - - benchmarks.run(); -}); diff --git a/list/sort/sort_test.js b/list/sort/sort_test.js deleted file mode 100644 index b707028806f..00000000000 --- a/list/sort/sort_test.js +++ /dev/null @@ -1,719 +0,0 @@ -require('can/list/sort/sort'); -require('can/view/stache/stache'); -require('can/model/model'); -require('steal-qunit'); - -QUnit.module('can/list/sort'); - -test('List events', (4*3), function () { - var list = new can.List([{ - name: 'Justin' - }, { - name: 'Brian' - }, { - name: 'Austin' - }, { - name: 'Mihael' - }]); - list.attr('comparator','name'); - // events on a list - // - move - item from one position to another - // due to changes in elements that change the sort order - // - add (items added to a list) - // - remove (items removed from a list) - // - reset (all items removed from the list) - // - change something happened - // a move directly on this list - list.bind('move', function (ev, item, newPos, oldPos) { - ok(ev, '"move" event passed `ev`'); - equal(item.name, 'Zed', '"move" event passed correct `item`'); - equal(newPos, 3, '"move" event passed correct `newPos`'); - equal(oldPos, 0, '"move" event passed correct `oldPos`'); - }); - - // a remove directly on this list - list.bind('remove', function (ev, items, oldPos) { - ok(ev, '"remove" event passed ev'); - equal(items.length, 1, '"remove" event passed correct # of `item`\'s'); - equal(items[0].name, 'Alexis', '"remove" event passed correct `item`'); - equal(oldPos, 0, '"remove" event passed correct `oldPos`'); - }); - - list.bind('add', function (ev, items, index) { - ok(ev, '"add" event passed ev'); - equal(items.length, 1, '"add" event passed correct # of items'); - equal(items[0].name, 'Alexis', '"add" event passed correct `item`'); - equal(index, 0, '"add" event passed correct `index`'); - }); - - // Push: Should result in a "add" event - list.push({ - name: 'Alexis' - }); - - // Splice: Should result in a "remove" event - list.splice(0, 1); - - // Update: Should result in a "move" event - list[0].attr('name', 'Zed'); -}); - -test('Passing a comparator function to sort()', 1, function () { - var list = new can.List([{ - priority: 4, - name: 'low' - }, { - priority: 1, - name: 'high' - }, { - priority: 2, - name: 'middle' - }, { - priority: 3, - name: 'mid' - }]); - list.sort(function (a, b) { - // Sort functions always need to return the -1/0/1 integers - if (a.priority < b.priority) { - return -1; - } - return a.priority > b.priority ? 1 : 0; - }); - equal(list[0].name, 'high'); -}); - -test('Passing a comparator string to sort()', 1, function () { - var list = new can.List([{ - priority: 4, - name: 'low' - }, { - priority: 1, - name: 'high' - }, { - priority: 2, - name: 'middle' - }, { - priority: 3, - name: 'mid' - }]); - list.sort('priority'); - equal(list[0].name, 'high'); -}); - -test('Defining a comparator property', 1, function () { - var list = new can.List([{ - priority: 4, - name: 'low' - }, { - priority: 1, - name: 'high' - }, { - priority: 2, - name: 'middle' - }, { - priority: 3, - name: 'mid' - }]); - list.attr('comparator','priority'); - equal(list[0].name, 'high'); -}); - -test('Defining a comparator property that is a function of a can.Map', 4, function () { - var list = new can.Map.List([ - new can.Map({ - text: 'Bbb', - func: can.compute(function () { - return 'bbb'; - }) - }), - new can.Map({ - text: 'abb', - func: can.compute(function () { - return 'abb'; - }) - }), - new can.Map({ - text: 'Aaa', - func: can.compute(function () { - return 'aaa'; - }) - }), - new can.Map({ - text: 'baa', - func: can.compute(function () { - return 'baa'; - }) - }) - ]); - list.attr('comparator','func'); - - equal(list.attr()[0].text, 'Aaa'); - equal(list.attr()[1].text, 'abb'); - equal(list.attr()[2].text, 'baa'); - equal(list.attr()[3].text, 'Bbb'); -}); - -test('Sorts primitive items', function () { - var list = new can.List(['z', 'y', 'x']); - list.sort(); - - equal(list[0], 'x', 'Moved string to correct index'); -}); - - - -function renderedTests (templateEngine, helperType, renderer) { - test('Insert pushed item at correct index with ' + templateEngine + ' using ' + helperType +' helper', function () { - var el = document.createElement('div'); - - var items = new can.List([{ - id: 'b' - }]); - items.attr('comparator', 'id'); - - // Render the template and place inside the
          - el.appendChild(renderer({ - items: items - })); - - var firstElText = el.querySelector('li').firstChild.data; - - /// Check that the template rendered an item - equal(firstElText, 'b', - 'First LI is a "b"'); - - // Add another item - items.push({ - id: 'a' - }); - - // Get the text of the first
        1. in the
          - firstElText = el.querySelector('li').firstChild.data; - - // Check that the template rendered that item at the correct index - equal(firstElText, 'a', - 'An item pushed into the list is rendered at the correct position'); - - }); - - // TODO: Test that push and sort have the result in the same output - - test('Insert unshifted item at correct index with ' + templateEngine + ' using ' + helperType +' helper', function () { - var el = document.createElement('div'); - - var items = new can.List([ - { id: 'a' }, - { id: 'c' } - ]); - items.attr('comparator', 'id'); - - // Render the template and place inside the
          - el.appendChild(renderer({ - items: items - })); - - var firstElText = el.querySelector('li').firstChild.data; - - /// Check that the template rendered an item - equal(firstElText, 'a', 'First LI is a "a"'); - - // Attempt to add an item to the beginning of the list - items.unshift({ - id: 'b' - }); - - firstElText = el.querySelectorAll('li')[1].firstChild.data; - - // Check that the template rendered that item at the correct index - equal(firstElText, 'b', - 'An item unshifted into the list is rendered at the correct position'); - - }); - - test('Insert spliced item at correct index with ' + templateEngine + ' using ' + helperType +' helper', function () { - var el = document.createElement('div'); - - var items = new can.List([ - { id: 'b' }, - { id: 'c' } - ]); - items.attr('comparator','id'); - - // Render the template and place inside the
          - el.appendChild(renderer({ - items: items - })); - - var firstElText = el.querySelector('li').firstChild.data; - - // Check that the "b" is at the beginning of the list - equal(firstElText, 'b', - 'First LI is a b'); - - // Add a "1" to the middle of the list - items.splice(1, 0, { - id: 'a' - }); - - // Get the text of the first
        2. in the
          - firstElText = el.querySelector('li').firstChild.data; - - // Check that the "a" was added to the beginning of the list despite - // the splice - equal(firstElText, 'a', - 'An item spliced into the list at the wrong position is rendered ' + - 'at the correct position'); - - }); - - // TODO: Test adding and removing items at the same time with .splice() - - test('Moves rendered item to correct index after "set" using ' + helperType +' helper', function () { - var el = document.createElement('div'); - - var items = new can.List([ - { id: 'x' }, - { id: 'y' }, - { id: 'z' } - ]); - items.attr('comparator', 'id'); - - // Render the template and place inside the
          - el.appendChild(renderer({ - items: items - })); - - var firstElText = el.querySelector('li').firstChild.data; - - // Check that the "x" is at the beginning of the list - equal(firstElText, 'x', 'First LI is a "x"'); - - // Change the ID of the last item so that it's sorted above the first item - items.attr('2').attr('id', 'a'); - - // Get the text of the first
        3. in the
          - firstElText = el.querySelector('li').firstChild.data; - - // Check that the "a" was added to the beginning of the list despite - // the splice - equal(firstElText, 'a', 'The last item was moved to the first position ' + - 'after it\'s value was changed'); - - }); - - test('Move DOM items when list is sorted with ' + templateEngine + ' using the ' + helperType +' helper', function () { - var el = document.createElement('div'); - - var items = new can.List([ - { id: 4 }, - { id: 1 }, - { id: 6 }, - { id: 3 }, - { id: 2 }, - { id: 8 }, - { id: 0 }, - { id: 5 }, - { id: 6 }, - { id: 9 }, - ]); - - // Render the template and place inside the
          - el.appendChild(renderer({ - items: items - })); - - var firstElText = el.querySelector('li').firstChild.data; - - // Check that the "4" is at the beginning of the list - equal(firstElText, 4, 'First LI is a "4"'); - - // Sort the list in-place - items.attr('comparator' , 'id'); - - firstElText = el.querySelector('li').firstChild.data; - - equal(firstElText, 0, 'The `0` was moved to beginning of the list' + - 'once sorted.'); - - }); - - test('Push multiple items with ' + templateEngine + ' using the ' + helperType +' helper (#1509)', function () { - var el = document.createElement('div'); - - var items = new can.List(); - items.attr('comparator' , 'id'); - - // Render the template and place inside the
          - el.appendChild(renderer({ - items: items - })); - - items.bind('add', function (ev, items) { - equal(items.length, 1, 'One single item was added'); - }); - - items.push.apply(items, [ - { id: 4 }, - { id: 1 }, - { id: 6 } - ]); - - var liLength = el.getElementsByTagName('li').length; - - equal(liLength, 3, 'The correct number of items have been rendered'); - - }); - -} - -var blockHelperTemplate = '
            {{#items}}
          • {{id}}
          • {{/items}}'; -var eachHelperTemplate = '
              {{#each items}}
            • {{id}}
            • {{/each}}'; - -renderedTests('Stache', '{{#block}}', can.stache(blockHelperTemplate)); -renderedTests('Stache', '{{#each}}', can.stache(eachHelperTemplate)); - - -test('Sort primitive values without a comparator defined', function () { - var list = new can.List([8,5,2,1,5,9,3,5]); - list.sort(); - equal(list[0], 1, 'Sorted the list in ascending order'); -}); - -test('Sort primitive values with a comparator function defined', function () { - var list = new can.List([8,5,2,1,5,9,3,5]); - list.attr('comparator' , function (a, b) { - return a === b ? 0 : a < b ? 1 : -1; - }); - equal(list[0], 9, 'Sorted the list in descending order'); -}); - -test('The "destroyed" event bubbles on a sorted list', 2, function () { - - var list = new can.Model.List([ - new can.Model({ name: 'Joe' }), - new can.Model({ name: 'Max' }), - new can.Model({ name: 'Pim' }) - ]); - - list.attr('comparator' , 'name'); - - list.bind('destroyed', function (ev) { - ok(true, '"destroyed" event triggered'); - }); - - list.attr(0).destroy(); - - equal(list.attr('length'), 2, 'item removed'); -}); - -test("sorting works with #each (#1566)", function(){ - - var heroes = new can.List([ { id: 1, name: 'Superman'}, { id: 2, name: 'Batman'} ]); - - heroes.attr('comparator', 'name'); - - var template = can.stache("
                \n{{#each heroes}}\n
              • {{id}}-{{name}}
              • \n{{/each}}
              "); - - var frag = template({ - heroes: heroes - }); - - var lis = frag.childNodes[0].getElementsByTagName("li"); - - equal(lis[0].innerHTML, "2-Batman"); - equal(lis[1].innerHTML, "1-Superman"); - - heroes.attr('comparator', 'id'); - - equal(lis[0].innerHTML, "1-Superman"); - equal(lis[1].innerHTML, "2-Batman"); -}); - -test("sorting works with comparator added after a binding", function(){ - var heroes = new can.List([ { id: 1, name: 'Superman'}, { id: 2, name: 'Batman'} ]); - - var template = can.stache("
                \n{{#each heroes}}\n
              • {{id}}-{{name}}
              • \n{{/each}}
              "); - - var frag = template({ - heroes: heroes - }); - - heroes.attr('comparator', 'id'); - - heroes.attr("0.id",3); - - var lis = frag.childNodes[0].getElementsByTagName("li"); - - equal(lis[0].innerHTML, "2-Batman"); - equal(lis[1].innerHTML, "3-Superman"); - -}); - -test("removing comparator tears down bubbling", function(){ - - var heroes = new can.List([ { id: 1, name: 'Superman'}, { id: 2, name: 'Batman'} ]); - var lengthHandler = function(){}; - - heroes.bind("length",lengthHandler); - - ok(!heroes[0]._bindings, "item has no bindings"); - - heroes.attr('comparator', 'id'); - - heroes.attr("0.id",3); - - ok(heroes._bindings, "list has bindings"); - ok(heroes[0]._bindings, "item has bindings"); - - heroes.removeAttr('comparator'); - - ok(!heroes[0]._bindings, "has bindings"); - ok(heroes._bindings, "list has bindings"); - - heroes.unbind("length",lengthHandler); - ok(!heroes._bindings, "list has no bindings"); -}); - -test('sorting works when returning any negative value (#1601)', function() { - var list = new can.List([1, 4, 2]); - - list.attr('comparator', function(a, b) { - return a - b; - }); - - list.sort(); - deepEqual(list.attr(), [1, 2, 4]); -}); - -test('Batched events originating from sort plugin lack batchNum (#1707)', function () { - var list = new can.List(); - list.attr('comparator', 'id'); - - list.bind('length', function (ev) { - ok(ev.batchNum, 'Has batchNum'); - }); - - can.batch.start(); - list.push({ id: 'a' }); - list.push({ id: 'a' }); - list.push({ id: 'a' }); - can.batch.stop(); -}); - -test('The sort plugin\'s _change handler ignores batched _changes (#1706)', function () { - var list = new can.List(); - var _getRelativeInsertIndex = list._getRelativeInsertIndex; - var sort = list.sort; - list.attr('comparator', 'id'); - - list.bind('move', function (ev) { - ok(false, 'No "move" events should be fired'); - }); - list._getRelativeInsertIndex = function () { - ok(false, 'No items should be evaluated independently'); - return _getRelativeInsertIndex.apply(this, arguments); - }; - list.sort = function () { - ok(true, 'Batching caused sort() to be called'); - return sort.apply(this, arguments); - }; - - can.batch.start(); - list.push({ id: 'c', index: 1 }); - list.push({ id: 'a', index: 2 }); - list.push({ id: 'a', index: 3 }); - can.batch.stop(); - - equal(list.attr('2.id'), 'c', 'List was sorted'); -}); - -test('Items aren\'t unecessarily swapped to the end of a list of equal items (#1705)', function () { - var list = new can.List([ - { id: 'a', index: 1 }, - { id: 'b', index: 2 }, - { id: 'c', index: 3 } - ]); - list.attr('comparator', 'id'); - list.bind('move', function () { - ok(false, 'No "move" events should be fired'); - }); - - list.attr('0.id', 'b'); - equal(list.attr('0.index'), 1, 'Item hasn\'t moved'); - - ok(true, '_getRelativeInsertIndex prevented an unecessary \'move\' event'); -}); - -test('Items aren\'t unecessarily swapped to the beginning of a list of equal items (#1705)', function () { - var list = new can.List([ - { id: 'a', index: 1 }, - { id: 'b', index: 2 }, - { id: 'c', index: 3 } - ]); - list.attr('comparator', 'id'); - list.bind('move', function () { - ok(false, 'No "move" events should be fired'); - }); - - list.attr('2.id', 'b'); - equal(list.attr('2.index'), 3, 'Item hasn\'t moved'); - - ok(true, '_getRelativeInsertIndex prevented an unecessary \'move\' event'); -}); - -test('Insert index is not evaluted for irrelevant changes', function () { - var list = new can.List([ - { - id: 'a', - index: 1 - }, - { - id: 'b', - index: 2, - child: { - grandchild: { - id: 'c', - index: 3 - } - } - } - ]); - - // Setup - var _getRelativeInsertIndex = list._getRelativeInsertIndex; - - list.bind('move', function (ev) { - ok(false, 'A "move" events should be fired'); - }); - list._getRelativeInsertIndex = function () { - ok(false, 'This item should not be evaluated independently'); - return _getRelativeInsertIndex.apply(this, arguments); - }; - list.attr('comparator', 'id'); - - // Start test - list.attr('0.index', 4); - list.attr('comparator', 'child.grandchild.id'); - list.attr('1.child.grandchild.index', 4); - - list._getRelativeInsertIndex = function () { - ok(true, 'This item should be evaluated independently'); - return _getRelativeInsertIndex.apply(this, arguments); - }; - - list.attr('1.child', { - grandchild: { - id: 'c', - index: 4 - } - }); - - equal(list.attr('0.id'), 'a', 'Item not moved'); -}); - -test('_getInsertIndex positions items correctly', function () { - var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - var alphabet = letters.split(''); - var expected = alphabet.slice(0); - var sorted = new can.List(alphabet); - - // Enable the sort plugin - sorted.attr('comparator', can.List.prototype._comparator); - - // There are some gotcha's that we can't compare to native sort: - // http://blog.rodneyrehm.de/archives/14-Sorting-Were-Doing-It-Wrong.html - var samples = ['0A','ZZ','**','LM','LL','Josh','James','Juan','Julia', - '!!HOORAY!!']; - - can.each(samples, function (value) { - expected.push(value); - expected.sort(can.List.prototype._comparator); - sorted.push(value); - - can.each(expected, function (value, index) { - equal(value, sorted.attr(index), - 'Sort plugin output matches native output'); - }); - }); -}); - -test('set comparator on init', function() { - var Item = can.Map.extend(); - Item.List = Item.List.extend({ - init: function() { - this.attr('comparator', 'isPrimary'); - } - }); - - var items = [ - { isPrimary: false }, - { isPrimary: true }, - { isPrimary: false } - ]; - - deepEqual(new Item.List(items).serialize(), [ - { isPrimary: false }, - { isPrimary: false }, - { isPrimary: true } - ]); -}); - -test('{{%index}} is updated for "move" events (#1962)', function () { - var list = new can.List([100, 200, 300]); - list.attr('comparator', function (a, b) { return a < b ? -1 : 1; }); - - var template = can.stache('
                {{#each list}}
              • ' + - '{{%index}} - ' + - '{{.}}' + - '
              • {{/each}}
              '); - - var frag = template({ list: list }); - var expected; - - var evaluate = function () { - var liEls = frag.querySelectorAll('li'); - - for (var i = 0; i < expected.length; i++) { - var li = liEls[i]; - var index = li.querySelectorAll('.index')[0].innerHTML; - var value = li.querySelectorAll('.value')[0].innerHTML; - - equal(index, ''+i, '{{%index}} rendered correct value'); - equal(value, ''+expected[i], '{{.}} rendered correct value'); - } - }; - - expected = [100, 200, 300]; - evaluate(); - - list.attr('comparator', function (a, b) { return a < b ? 1 : -1; }); - - expected = [300, 200, 100]; - evaluate(); -}); - -test('Setting comparator with .sort() (#2159)', function () { - var list = new can.List([ - { letter: 'x', number: 3 }, - { letter: 'y', number: 2 }, - { letter: 'z', number: 1 }, - ]); - - list.attr('comparator', 'number'); - - equal(list.attr('0.number'), 1, 'First value is correct'); - equal(list.attr('1.number'), 2, 'Second value is correct'); - equal(list.attr('2.number'), 3, 'Third value is correct'); - - list.sort('letter'); - - equal(list.attr('0.letter'), 'x', 'First value is correct after comparator set'); - equal(list.attr('1.letter'), 'y', 'Second value is correct after comparator set'); - equal(list.attr('2.letter'), 'z', 'Third value is correct after comparator set'); - - list.push({ letter: 'w', number: 4 }); - - equal(list.attr('0.letter'), 'w', 'First value is correct after insert'); - equal(list.attr('0.number'), 4, 'First value is correct after insert'); - -}); diff --git a/list/sort/test.html b/list/sort/test.html deleted file mode 100644 index bc7a60ff5d3..00000000000 --- a/list/sort/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/list/sort - -
              diff --git a/list/test.html b/list/test.html deleted file mode 100644 index ee5ab61b2a0..00000000000 --- a/list/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/list - -
              diff --git a/map/app/app.js b/map/app/app.js deleted file mode 100644 index 0a2f7890541..00000000000 --- a/map/app/app.js +++ /dev/null @@ -1,70 +0,0 @@ -var can = require('can/util/util'); -require('can/map/map'); -require('can/compute/compute'); - -function sortedSetJson(set){ - if(set == null) { - return set; - } else { - var sorted = {}; - - var keys = []; - for(var k in set){ - keys.push(k); - } - keys.sort(); - can.each(keys, function(prop){ - sorted[prop] = set[prop]; - }); - return JSON.stringify(sorted); - } -} - -can.AppMap = can.Map.extend({ - setup: function(){ - can.Map.prototype.setup.apply(this, arguments); - this.__readyPromises = []; - this.__pageData = {}; - - if(typeof System !== "undefined" && System.has("asset-register")) { - var register = System.get("asset-register")["default"]; - var self = this; - register("inline-cache", function(){ - var script = document.createElement("script"); - var text = document.createTextNode("\nINLINE_CACHE = " + JSON.stringify(self.__pageData) + ";\n"); - script.appendChild(text); - return script; - }); - } - }, - waitFor: function(promise){ - this.__readyPromises.push(promise); - return promise; - }, - pageData: can.__notObserve(function(key, set, inst){ - var appState = this; - - function store(data){ - var keyData = appState.__pageData[key]; - if(!keyData) { - keyData = appState.__pageData[key] = {}; - } - - keyData[sortedSetJson(set)] = typeof data.serialize === "function" ? - data.serialize() : data; - } - - if(can.isDeferred(inst)){ - this.waitFor(inst); - inst.then(function(data){ - store(data); - }); - } else { - store(inst); - } - - return inst; - }) -}); - -module.exports = exports = can.AppMap; diff --git a/map/app/app_test.js b/map/app/app_test.js deleted file mode 100644 index 1180e01efc5..00000000000 --- a/map/app/app_test.js +++ /dev/null @@ -1,21 +0,0 @@ -var AppMap = require("can/map/app/"); -var QUnit = require("steal-qunit"); - -QUnit.module("can/map/app"); - -QUnit.module("pageData"); - -var keys = Object.keys || function(obj){ - var result = []; - for (var prop in obj) { - result.push(prop); - } -}; - -test("sorts correctly", function(){ - var map = new AppMap(); - map.pageData("foo", { "one": 1, "two": 2 }, {}); - map.pageData("foo", { "two": 2, "one": 1 }, {}); - - equal(keys(map.__pageData).length, 1, "There is one key"); -}); diff --git a/map/app/test.html b/map/app/test.html deleted file mode 100644 index 07843b6e39a..00000000000 --- a/map/app/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/map/app - -
              diff --git a/map/benchmark.html b/map/benchmark.html deleted file mode 100644 index 555e07951b7..00000000000 --- a/map/benchmark.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - -

              can.Map Test Suite

              - -

              - -
              -

              -
                -
                - - - - \ No newline at end of file diff --git a/map/bubble.js b/map/bubble.js deleted file mode 100644 index 3e504201d2f..00000000000 --- a/map/bubble.js +++ /dev/null @@ -1,218 +0,0 @@ -// # can/map/map_helpers -// Helpers that enable bubbling of an event on a child object to a -// parent event on a parent object. Bubbling works by listening on the child object -// and forwarding events to the parent object. -// -// Bubbling is complicated because bubbling setup can happen before or after -// items are added to the parent object. -// -// This means that: -// - When bubbling is first initialied, by binding to an event that bubbles, -// all child objects need to be setup to bubble. This is managed by [bubble.bind](#bubble-bind). -// - When bubbling is stopped, by removing all listeners to events that bubble, -// all child objects need to have bubbling torn down. This is managed by [bubble.unbind](#bubble-unbind). -// - While bubbling is running, as child items are added, -// the child elements need to be setup to bubble. This is managed by [bubble.add](#bubble-add) and [bubble.addMany](#bubble-addmany). -// - While bubbling is running, as child items are removed, -// the child elements need to stop bubbling. This is managed by -// [bubble.remove](#bubble-remove) and [bubble.removeMany](#bubble-removeMany). -// - While bubbling is running, as child item replaces another child, the old child needs bubbling removed -// and the new child needs bubbling setup. This is managed by [bubble.set](bubble-set). -// -// [bubble.events](bubble-events) controls which events setup bubbling. -var can = require('can/util/util'); - -var bubble = can.bubble = { - // ## bubble.bind - // Called when an event is bound to an object. This - // should setup bubbling if this is the first time - // an event that bubbles is bound. - bind: function(parent, eventName) { - if (!parent.__inSetup ) { - - var bubbleEvents = bubble.events(parent, eventName), - len = bubbleEvents.length, - bubbleEvent; - - if(!parent._bubbleBindings) { - parent._bubbleBindings = {}; - } - - for (var i = 0; i < len; i++) { - bubbleEvent = bubbleEvents[i]; - - // If there isn't a bubbling setup for this binding, - // bubble all the children; otherwise, increment the - // number of bubble bindings. - if (!parent._bubbleBindings[bubbleEvent]) { - parent._bubbleBindings[bubbleEvent] = 1; - bubble.childrenOf(parent, bubbleEvent); - } else { - parent._bubbleBindings[bubbleEvent]++; - } - } - } - }, - - // ## bubble.unbind - // Called when an event is unbound from an object. This should - // teardown bubbling if there are no more bubbling event handlers. - unbind: function(parent, eventName) { - var bubbleEvents = bubble.events(parent, eventName), - len = bubbleEvents.length, - bubbleEvent; - - for (var i = 0; i < len; i++) { - bubbleEvent = bubbleEvents[i]; - - if (parent._bubbleBindings ) { - parent._bubbleBindings[bubbleEvent]--; - } - - if (parent._bubbleBindings && !parent._bubbleBindings[bubbleEvent] ) { - delete parent._bubbleBindings[bubbleEvent]; - bubble.teardownChildrenFrom(parent, bubbleEvent); - if(can.isEmptyObject(parent._bubbleBindings)) { - delete parent._bubbleBindings; - } - } - } - }, - - // ## bubble.add - // Called when a new `child` value has been added to `parent`. - // If the `parent` is bubbling and the child is observable, - // setup bubbling on the child to the parent. This calls - // `teardownFromParent` to ensure we aren't bubbling the same - // child more than once. - add: function(parent, child, prop){ - if(child instanceof can.Map && parent._bubbleBindings) { - for(var eventName in parent._bubbleBindings) { - if( parent._bubbleBindings[eventName] ) { - bubble.teardownFromParent(parent, child, eventName); - bubble.toParent(child, parent, prop, eventName); - } - } - } - }, - // ## bubble.addMany - // Called when many `children` are added to `parent`. - addMany: function(parent, children){ - for (var i = 0, len = children.length; i < len; i++) { - bubble.add(parent, children[i], i); - } - }, - // ## bubble.remove - // Called when a `child` has been removed from `parent`. - // Removes all bubbling events from `child` to `parent`. - remove: function(parent, child){ - if(child instanceof can.Map && parent._bubbleBindings) { - for(var eventName in parent._bubbleBindings) { - if( parent._bubbleBindings[eventName] ) { - bubble.teardownFromParent(parent, child, eventName); - } - } - } - }, - // ## bubble.removeMany - // Called when many `children` are removed from `parent`. - removeMany: function(parent, children){ - for(var i = 0, len = children.length; i < len; i++) { - bubble.remove(parent, children[i]); - } - }, - // ## bubble.set - // Called when a new child `value` replaces `current` value. - set: function(parent, prop, value, current){ - - if( can.isMapLike(value) ) { - bubble.add(parent, value, prop); - } - // bubble.add will remove, so only remove if we are replacing another object - if( can.isMapLike(current) ) { - bubble.remove(parent, current); - } - return value; - }, - - // ## bubble.events - // For an event binding on an object, returns the events that should be bubbled. - // For example, `"change" -> ["change"]`. - events: function(map, boundEventName) { - return map.constructor._bubbleRule(boundEventName, map); - }, - - - // ## bubble.toParent - // Forwards an event on `child` to `parent`. `child` is - // the `prop` property of `parent`. - toParent: function(child, parent, prop, eventName) { - can.listenTo.call(parent, child, eventName, function ( /* ev, attr */ ) { - - var args = can.makeArray(arguments), - ev = args.shift(); - - // Updates the nested property name that will be dispatched. - // If the parent is a list, the index of the child needs to - // be calculated every time. - args[0] = - (can.List && parent instanceof can.List ? - parent.indexOf(child) : - prop ) + (args[0] ? "."+args[0] : ""); - - // Track all objects that we have bubbled this event to. - // If we have already bubbled to this object, do not dispatch another - // event on it. This prevents cycles. - ev.triggeredNS = ev.triggeredNS || {}; - if (ev.triggeredNS[parent._cid]) { - return; - } - ev.triggeredNS[parent._cid] = true; - - // Send bubbled event to parent. - can.trigger(parent, ev, args); - - // Trigger named event. - if(eventName === "change") { - can.trigger(parent, args[0], [args[2], args[3]]); - } - - }); - }, - - // ## bubble.childrenOf - // Bubbles all the children of `parent`. - childrenOf: function (parent, eventName) { - - parent._each(function (child, prop) { - if (child && child.bind) { - bubble.toParent(child, parent, prop, eventName); - } - }); - }, - - // ## bubble.teardownFromParent - // Undo the bubbling from `child` to `parent`. - teardownFromParent: function (parent, child, eventName ) { - if(child && child.unbind ) { - can.stopListening.call(parent, child, eventName); - } - }, - - // ## bubble.teardownChildrenFrom - // Undo the bubbling of every child of `parent` - teardownChildrenFrom: function(parent, eventName){ - parent._each(function (child) { - - bubble.teardownFromParent(parent, child, eventName); - }); - }, - - // ## bubble.isBubbling - // Returns true or false if `parent` is bubbling `eventName`. - isBubbling: function(parent, eventName){ - return parent._bubbleBindings && parent._bubbleBindings[eventName]; - } - }; - -module.exports = exports = bubble; diff --git a/map/define/define.js b/map/define/define.js deleted file mode 100644 index a740c2d9288..00000000000 --- a/map/define/define.js +++ /dev/null @@ -1,364 +0,0 @@ -var can = require('can/util/util'); -var mapHelpers = require('can/map/map_helpers'); -require('can/map/map'); -require('can/compute/compute'); - -if (!can.define) { - - var define = can.define = {}; // jshint ignore:line - - var getPropDefineBehavior = function(behavior, attr, define) { - var prop, defaultProp; - - if (define) { - prop = define[attr]; - defaultProp = define['*']; - - if (prop && prop[behavior] !== undefined) { - return prop[behavior]; - } else if (defaultProp && defaultProp[behavior] !== undefined) { - return defaultProp[behavior]; - } - } - }; - - // This is called when the Map is defined - mapHelpers.define = function(Map) { - var definitions = Map.prototype.define; - //!steal-remove-start - if (Map.define) { - can.dev.warn("The define property should be on the map's prototype properties, " + - "not the static properties."); - } - //!steal-remove-end - Map.defaultGenerators = {}; - for (var prop in definitions) { - var type = definitions[prop].type; - if (typeof type === "string") { - if (typeof define.types[type] === "object") { - delete definitions[prop].type; - can.extend(definitions[prop], define.types[type]); - } - } - if ("value" in definitions[prop]) { - if (typeof definitions[prop].value === "function") { - Map.defaultGenerators[prop] = definitions[prop].value; - } else { - Map.defaults[prop] = definitions[prop].value; - } - } - if (typeof definitions[prop].Value === "function") { - (function(Constructor) { - Map.defaultGenerators[prop] = function() { - return new Constructor(); - }; - })(definitions[prop].Value); - } - } - }; - - - var oldSetupDefaults = can.Map.prototype._setupDefaults; - can.Map.prototype._setupDefaults = function(obj) { - var defaults = oldSetupDefaults.call(this), - propsCommittedToAttr = {}, - Map = this.constructor, - originalGet = this._get; - - // Overwrite this._get with a version that commits defaults to - // this.attr() as needed. Because calling this.attr() for each - // individual default would be expensive. - this._get = function(originalProp) { - // If a this.attr() was called using dot syntax (e.g number.0), - // disregard everything after the "." until we call the - // original this._get(). - var prop = (originalProp.indexOf('.') !== -1 ? - originalProp.substr(0, originalProp.indexOf('.')) : - originalProp); - - // If this property has a default and we haven't yet committed it to - // this.attr() - if ((prop in defaults) && !(prop in propsCommittedToAttr)) { - - // Commit the property's default so that it can be read in - // other defaultGenerators. - this.attr(prop, defaults[prop]); - - // Make not so that we don't commit this property again. - propsCommittedToAttr[prop] = true; - } - - return originalGet.apply(this, arguments); - }; - - for (var prop in Map.defaultGenerators) { - // Only call the prop's value method if the property wasn't provided - // during instantiation. - if (!obj || !(prop in obj)) { - defaults[prop] = Map.defaultGenerators[prop].call(this); - } - } - - // delete this._get which will default to the one on the prototype. - delete this._get; - - return defaults; - }; - - - var proto = can.Map.prototype, - oldSet = proto.__set; - proto.__set = function(prop, value, current, success, error) { - //!steal-remove-start - var asyncTimer; - //!steal-remove-end - - // check if there's a setter - var errorCallback = function(errors) { - //!steal-remove-start - clearTimeout(asyncTimer); - //!steal-remove-end - - var stub = error && error.call(self, errors); - // if 'validations' is on the page it will trigger - // the error itself and we dont want to trigger - // the event twice. :) - if (stub !== false) { - can.trigger(self, 'error', [ - prop, - errors - ], true); - } - return false; - }, - self = this, - setter = getPropDefineBehavior("set", prop, this.define), - getter = getPropDefineBehavior("get", prop, this.define); - - - // if we have a setter - if (setter) { - // call the setter, if returned value is undefined, - // this means the setter is async so we - // do not call update property and return right away - can.batch.start(); - var setterCalled = false, - - setValue = setter.call(this, value, function(value) { - if (getter) { - self[prop](value); - } else { - oldSet.call(self, prop, value, current, success, errorCallback); - } - - setterCalled = true; - //!steal-remove-start - clearTimeout(asyncTimer); - //!steal-remove-end - }, errorCallback, getter ? this._computedAttrs[prop].compute.computeInstance.lastSetValue.get() : current); - if (getter) { - // if there's a getter we don't call old set - // instead we call the getter's compute with the new value - if (setValue !== undefined && !setterCalled && setter.length >= 1) { - this._computedAttrs[prop].compute(setValue); - } - - can.batch.stop(); - return; - } - // if it took a setter and returned nothing, don't set the value - else if (setValue === undefined && !setterCalled && setter.length >= 1) { - //!steal-remove-start - asyncTimer = setTimeout(function() { - can.dev.warn('can/map/define: Setter "' + prop + '" did not return a value or call the setter callback.'); - }, can.dev.warnTimeout); - //!steal-remove-end - can.batch.stop(); - return; - } else { - if (!setterCalled) { - oldSet.call(self, prop, - // if no arguments, we are side-effects only - setter.length === 0 && setValue === undefined ? value : setValue, - current, - success, - errorCallback); - } - can.batch.stop(); - return this; - } - - } else { - oldSet.call(self, prop, value, current, success, errorCallback); - } - - return this; - }; - - define.types = { - 'date': function(str) { - var type = typeof str; - if (type === 'string') { - str = Date.parse(str); - return isNaN(str) ? null : new Date(str); - } else if (type === 'number') { - return new Date(str); - } else { - return str; - } - }, - 'number': function(val) { - if (val == null) { - return val; - } - return +(val); - }, - 'boolean': function(val) { - if (val === 'false' || val === '0' || !val) { - return false; - } - return true; - }, - /** - * Implements HTML-style boolean logic for attribute strings, where - * any string, including "", is truthy. - */ - 'htmlbool': function(val) { - return typeof val === "string" || !!val; - }, - '*': function(val) { - return val; - }, - 'string': function(val) { - if (val == null) { - return val; - } - return '' + val; - }, - 'compute': { - set: function(newValue, setVal, setErr, oldValue) { - if (newValue.isComputed) { - return newValue; - } - if (oldValue && oldValue.isComputed) { - oldValue(newValue); - return oldValue; - } - return newValue; - }, - get: function(value) { - return value && value.isComputed ? value() : value; - } - } - }; - - // the old type sets up bubbling - var oldType = proto.__type; - proto.__type = function(value, prop) { - var type = getPropDefineBehavior("type", prop, this.define), - Type = getPropDefineBehavior("Type", prop, this.define), - newValue = value; - - if (typeof type === "string") { - type = define.types[type]; - } - - if (type || Type) { - // If there's a type, convert it. - if (type) { - newValue = type.call(this, newValue, prop); - } - // If there's a Type create a new instance of it - if (Type && !(newValue instanceof Type)) { - newValue = new Type(newValue); - } - // If the newValue is a Map, we need to hook it up - return newValue; - - } - // If we pass in a object with define - else if (can.isPlainObject(newValue) && newValue.define) { - newValue = can.Map.extend(newValue); - newValue = new newValue(); - } - return oldType.call(this, newValue, prop); - }; - - var oldRemove = proto.__remove; - proto.__remove = function(prop, current) { - var remove = getPropDefineBehavior("remove", prop, this.define), - res; - if (remove) { - can.batch.start(); - res = remove.call(this, current); - - if (res === false) { - can.batch.stop(); - return; - } else { - - res = oldRemove.call(this, prop, current); - can.batch.stop(); - return res; - } - } - return oldRemove.call(this, prop, current); - }; - - var oldSetupComputes = proto._setupComputedProperties; - proto._setupComputedProperties = function() { - oldSetupComputes.apply(this, arguments); - for (var attr in this.define) { - var def = this.define[attr], - get = def.get; - if (get) { - mapHelpers.addComputedAttr(this, attr, can.compute.async(undefined, get, this)); - } - } - }; - // Overwrite the invidual property serializer b/c we will overwrite it. - var oldSingleSerialize = proto.___serialize; - proto.___serialize = function(name, val) { - return serializeProp(this, name, val); - }; - // If the map has a define serializer for the given attr, run it. - var serializeProp = function(map, attr, val) { - var serializer = attr === "*" ? false : getPropDefineBehavior("serialize", attr, map.define); - if (serializer === undefined) { - return oldSingleSerialize.call(map, attr, val); - } else if (serializer !== false) { - return typeof serializer === "function" ? serializer.call(map, val, attr) : oldSingleSerialize.call(map, attr, val); - } - }; - - // Overwrite serialize to add in any missing define serialized properties. - var oldSerialize = proto.serialize; - proto.serialize = function(property) { - var serialized = oldSerialize.apply(this, arguments); - if (property) { - return serialized; - } - // add in properties not already serialized - - var serializer, - val; - // Go through each property. - for (var attr in this.define) { - // if it's not already defined - if (!(attr in serialized)) { - // check there is a serializer so we aren't doing extra work on serializer:false - serializer = this.define && this.define[attr] && this.define[attr].serialize; - if (serializer) { - val = serializeProp(this, attr, this.attr(attr)); - if (val !== undefined) { - serialized[attr] = val; - } - } - } - } - return serialized; - }; - - module.exports = exports = can.define; - -} diff --git a/map/define/define_test.js b/map/define/define_test.js deleted file mode 100644 index 39943294615..00000000000 --- a/map/define/define_test.js +++ /dev/null @@ -1,1230 +0,0 @@ -/* jshint asi: false */ -require('can/map/define/define'); -require('can/route/route'); -require('can/test/test'); -require('steal-qunit'); - -QUnit.module('can/map/define'); - -// remove, type, default -test('basics set', function() { - var Defined = can.Map.extend({ - define: { - prop: { - set: function(newVal) { - return "foo" + newVal; - } - } - } - }); - - var def = new Defined(); - def.attr("prop", "bar"); - - equal(def.attr("prop"), "foobar", "setter works"); - - Defined = can.Map.extend({ - define: { - prop: { - set: function(newVal, setter) { - setter("foo" + newVal); - } - } - } - }); - - def = new Defined(); - def.attr("prop", "bar"); - - equal(def.attr("prop"), "foobar", "setter callback works"); - -}); - -test("basics remove", function() { - var ViewModel = can.Map.extend({ - define: { - makeId: { - remove: function() { - this.removeAttr("models"); - } - }, - models: { - remove: function() { - this.removeAttr("modelId"); - } - }, - modelId: { - remove: function() { - this.removeAttr("years"); - } - }, - years: { - remove: function() { - this.removeAttr("year"); - } - } - } - }); - - var mmy = new ViewModel({ - makes: [ - { - id: 1 - } - ], - makeId: 1, - models: [ - { - id: 2 - } - ], - modelId: 2, - years: [2010], - year: 2010 - }); - - var events = ["year", "years", "modelId", "models", "makeId"], - eventCount = 0, - batchNum; - - mmy.bind("change", function(ev, attr) { - if (batchNum === undefined) { - batchNum = ev.batchNum; - } - equal(attr, events[eventCount++], "got correct attribute"); - ok(ev.batchNum && ev.batchNum === batchNum, "batched"); - }); - - mmy.removeAttr("makeId"); - -}); - -test("basics get", function() { - - var Person = can.Map.extend({ - define: { - fullName: { - get: function() { - return this.attr("first") + " " + this.attr("last"); - } - } - } - }); - - var p = new Person({ - first: "Justin", - last: "Meyer" - }); - - equal(p.attr("fullName"), "Justin Meyer", "sync getter works"); - - var Adder = can.Map.extend({ - define: { - more: { - get: function(curVal, setVal) { - var num = this.attr("num"); - setTimeout(function() { - setVal(num + 1); - }, 10); - } - } - } - }); - - var a = new Adder({ - num: 1 - }), - callbackVals = [ - [2, undefined, function() { - a.attr("num", 2); - }], - [3, 2, function() { - start(); - }] - ], - callbackCount = 0; - - a.bind("more", function(ev, newVal, oldVal) { - var vals = callbackVals[callbackCount++]; - equal(newVal, vals[0], "newVal is correct"); - equal(a.attr("more"), vals[0], "attr value is correct"); - - equal(oldVal, vals[1], "oldVal is correct"); - - setTimeout(vals[2], 10); - }); - - stop(); -}); - -test("basic type", function() { - - expect(6); - - var Typer = can.Map.extend({ - define: { - arrayWithAddedItem: { - type: function(value) { - if (value && value.push) { - value.push("item"); - } - return value; - } - }, - listWithAddedItem: { - type: function(value) { - if (value && value.push) { - value.push("item"); - } - return value; - }, - Type: can.List - } - } - }); - - - var t = new Typer(); - deepEqual(can.Map.keys(t), [], "no keys"); - - var array = []; - t.attr("arrayWithAddedItem", array); - - deepEqual(array, ["item"], "updated array"); - equal(t.attr("arrayWithAddedItem"), array, "leave value as array"); - - t.attr("listWithAddedItem", []); - - ok(t.attr("listWithAddedItem") instanceof can.List, "convert to can.List"); - equal(t.attr("listWithAddedItem").attr(0), "item", "has item in it"); - - t.bind("change", function(ev, attr) { - equal(attr, "listWithAddedItem.1", "got a bubbling event"); - }); - - t.attr("listWithAddedItem").push("another item"); - -}); - -test("basic Type", function() { - var Foo = function(name) { - this.name = name; - }; - Foo.prototype.getName = function() { - return this.name; - }; - - var Typer = can.Map.extend({ - define: { - foo: { - Type: Foo - } - } - }); - - var t = new Typer({ - foo: "Justin" - }); - equal(t.attr("foo").getName(), "Justin", "correctly created an instance"); - - var brian = new Foo("brian"); - - t.attr("foo", brian); - - equal(t.attr("foo"), brian, "same instances"); - -}); - -test("type converters", function() { - - var Typer = can.Map.extend({ - define: { - date: { - type: 'date' - }, - string: { - type: 'string' - }, - number: { - type: 'number' - }, - 'boolean': { - type: 'boolean' - }, - htmlbool: { - type: 'htmlbool' - }, - leaveAlone: { - type: '*' - } - } - }); - var obj = {}; - - var t = new Typer({ - date: 1395896701516, - string: 5, - number: '5', - 'boolean': 'false', - htmlbool: "", - leaveAlone: obj - }); - - ok(t.attr("date") instanceof Date, "converted to date"); - - equal(t.attr("string"), '5', "converted to string"); - - equal(t.attr("number"), 5, "converted to number"); - - equal(t.attr("boolean"), false, "converted to boolean"); - - equal(t.attr("htmlbool"), true, "converted to htmlbool"); - - equal(t.attr("leaveAlone"), obj, "left as object"); - t.attr({ - 'number': '15' - }); - ok(t.attr("number") === 15, "converted to number"); - -}); - - -test("basics value", function() { - var Typer = can.Map.extend({ - define: { - prop: { - value: 'foo' - } - } - }); - - equal(new Typer().attr('prop'), "foo", "value is used as default value"); - - - var Typer2 = can.Map.extend({ - define: { - prop: { - value: function() { - return []; - }, - type: "*" - } - } - }); - - var t1 = new Typer2(), - t2 = new Typer2(); - ok(t1.attr("prop") !== t2.attr("prop"), "different array instances"); - ok(can.isArray(t1.attr("prop")), "its an array"); - - -}); - -test("basics Value", function() { - - var Typer = can.Map.extend({ - define: { - prop: { - Value: Array, - type: "*" - } - } - }); - - var t1 = new Typer(), - t2 = new Typer(); - ok(t1.attr("prop") !== t2.attr("prop"), "different array instances"); - ok(can.isArray(t1.attr("prop")), "its an array"); - - -}); - - -test("setter with no arguments and returns undefined does the default behavior, the setter is for side effects only", function() { - - var Typer = can.Map.extend({ - define: { - prop: { - set: function() { - this.attr("foo", "bar"); - } - } - } - }); - - var t = new Typer(); - - t.attr("prop", false); - - deepEqual(t.attr(), { - foo: "bar", - prop: false - }); - - -}); - -test("type happens before the set", function() { - var MyMap = can.Map.extend({ - define: { - prop: { - type: "number", - set: function(newValue) { - equal(typeof newValue, "number", "got a number"); - return newValue + 1; - } - } - } - }); - - var map = new MyMap(); - map.attr("prop", "5"); - - equal(map.attr("prop"), 6, "number"); -}); - -test("getter and setter work", function() { - expect(5); - var Paginate = can.Map.extend({ - define: { - page: { - set: function(newVal) { - this.attr('offset', (parseInt(newVal) - 1) * this.attr('limit')); - }, - get: function() { - return Math.floor(this.attr('offset') / this.attr('limit')) + 1; - } - } - } - }); - - var p = new Paginate({ - limit: 10, - offset: 20 - }); - - equal(p.attr("page"), 3, "page get right"); - - p.bind("page", function(ev, newValue, oldValue) { - equal(newValue, 2, "got new value event"); - equal(oldValue, 3, "got old value event"); - }); - - - p.attr("page", 2); - - equal(p.attr("page"), 2, "page set right"); - - equal(p.attr("offset"), 10, "page offset set"); - - -}); - -test("getter with initial value", function() { - - var compute = can.compute(1); - - var Grabber = can.Map.extend({ - define: { - vals: { - type: "*", - Value: Array, - get: function(current, setVal) { - if (setVal) { - current.push(compute()); - } - return current; - } - } - } - }); - - var g = new Grabber(); - // This assertion doesn't mean much. It's mostly testing - // that there were no errors. - equal(g.attr("vals").length, 0, "zero items in array"); - -}); - - -test("serialize basics", function() { - var MyMap = can.Map.extend({ - define: { - name: { - serialize: function() { - return; - } - }, - locations: { - serialize: false - }, - locationIds: { - get: function() { - var ids = []; - this.attr('locations').each(function(location) { - ids.push(location.id); - }); - return ids; - }, - serialize: function(locationIds) { - return locationIds.join(','); - } - }, - bared: { - get: function() { - return this.attr("name") + "+bar"; - }, - serialize: true - }, - ignored: { - get: function() { - return this.attr("name") + "+ignored"; - } - } - } - }); - - var map = new MyMap({ - name: "foo" - }); - map.attr("locations", [{ - id: 1, - name: "Chicago" - }, { - id: 2, - name: "LA" - }]); - equal(map.attr("locationIds").length, 2, "get locationIds"); - equal(map.attr("locationIds")[0], 1, "get locationIds index 0"); - equal(map.attr("locations")[0].id, 1, "get locations index 0"); - - var serialized = map.serialize(); - equal(serialized.locations, undefined, "locations doesn't serialize"); - equal(serialized.locationIds, "1,2", "locationIds serializes"); - equal(serialized.name, undefined, "name doesn't serialize"); - - equal(serialized.bared, "foo+bar", "true adds computed props"); - equal(serialized.ignored, undefined, "computed props are not serialized by default"); - -}); - -test("serialize context", function() { - var context, serializeContext; - var MyMap = can.Map.extend({ - define: { - name: { - serialize: function(obj) { - context = this; - return obj; - } - } - }, - serialize: function() { - serializeContext = this; - can.Map.prototype.serialize.apply(this, arguments); - - } - }); - - var map = new MyMap(); - map.serialize(); - equal(context, map); - equal(serializeContext, map); -}); - -test("methods contexts", function() { - var contexts = {}; - var MyMap = can.Map.extend({ - define: { - name: { - value: 'John Galt', - - get: function(obj) { - contexts.get = this; - return obj; - }, - - remove: function(obj) { - contexts.remove = this; - return obj; - }, - - set: function(obj) { - contexts.set = this; - return obj; - }, - - serialize: function(obj) { - contexts.serialize = this; - return obj; - }, - - type: function(val) { - contexts.type = this; - return val; - } - } - - } - }); - - var map = new MyMap(); - map.serialize(); - map.removeAttr('name'); - - equal(contexts.get, map); - equal(contexts.remove, map); - equal(contexts.set, map); - equal(contexts.serialize, map); - equal(contexts.type, map); -}); - -test("value generator is not called if default passed", function() { - var TestMap = can.Map.extend({ - define: { - foo: { - value: function() { - throw '"foo"\'s value method should not be called.'; - } - } - } - }); - - var tm = new TestMap({ - foo: 'baz' - }); - - equal(tm.attr('foo'), 'baz'); -}); - -test("Value generator can read other properties", function() { - var Map = can.Map.extend({ - letters: 'ABC', - numbers: [1, 2, 3], - define: { - definedLetters: { - value: 'DEF' - }, - definedNumbers: { - value: [4, 5, 6] - }, - generatedLetters: { - value: function() { - return 'GHI'; - } - }, - generatedNumbers: { - value: function() { - return new can.List([7, 8, 9]); - } - }, - - // Get prototype defaults - firstLetter: { - value: function() { - return this.attr('letters').substr(0, 1); - } - }, - firstNumber: { - value: function() { - return this.attr('numbers.0'); - } - }, - - // Get defined simple `value` defaults - middleLetter: { - value: function() { - return this.attr('definedLetters').substr(1, 1); - } - }, - middleNumber: { - value: function() { - return this.attr('definedNumbers.1'); - } - }, - - // Get defined `value` function defaults - lastLetter: { - value: function() { - return this.attr('generatedLetters').substr(2, 1); - } - }, - lastNumber: { - value: function() { - return this.attr('generatedNumbers.2'); - } - } - } - }); - - var map = new Map(); - var prefix = 'Was able to read dependent value from '; - - equal(map.attr('firstLetter'), 'A', - prefix + 'traditional can.Map style property definition'); - equal(map.attr('firstNumber'), 1, - prefix + 'traditional can.Map style property definition'); - - equal(map.attr('middleLetter'), 'E', - prefix + 'define plugin style default property definition'); - equal(map.attr('middleNumber'), 5, - prefix + 'define plugin style default property definition'); - - equal(map.attr('lastLetter'), 'I', - prefix + 'define plugin style generated default property definition'); - equal(map.attr('lastNumber'), 9, - prefix + 'define plugin style generated default property definition'); -}); - -test('default behaviors with "*" work for attributes', function() { - expect(9); - var DefaultMap = can.Map.extend({ - define: { - someNumber: { - value: '5' - }, - '*': { - type: 'number', - serialize: function(value) { - return '' + value; - }, - set: function(newVal) { - ok(true, 'set called'); - return newVal; - }, - remove: function(currentVal) { - ok(true, 'remove called'); - return false; - } - } - } - }); - - var map = new DefaultMap(), - serializedMap; - - equal(map.attr('someNumber'), 5, 'value of someNumber should be converted to a number'); - map.attr('number', '10'); // Custom set should be called - equal(map.attr('number'), 10, 'value of number should be converted to a number'); - map.removeAttr('number'); // Custom removed should be called - equal(map.attr('number'), 10, 'number should not be removed'); - - serializedMap = map.serialize(); - - equal(serializedMap.number, '10', 'number serialized as string'); - equal(serializedMap.someNumber, '5', 'someNumber serialized as string'); - equal(serializedMap['*'], undefined, '"*" is not a value in serialized object'); -}); - -test('models properly serialize with default behaviors', function() { - var DefaultMap = can.Map.extend({ - define: { - name: { - value: 'Alex' - }, - shirt: { - value: 'blue', - serialize: true - }, - '*': { - serialize: false - } - } - }); - var map = new DefaultMap({ - age: 10, - name: 'John' - }), - serializedMap = map.serialize(); - - equal(serializedMap.age, undefined, 'age doesn\'t exist'); - equal(serializedMap.name, undefined, 'name doesn\'t exist'); - equal(serializedMap.shirt, 'blue', 'shirt exists'); -}); - -test("nested define", function() { - var nailedIt = 'Nailed it'; - var Example = can.Map.extend({}, { - define: { - name: { - value: nailedIt - } - } - }); - - var NestedMap = can.Map.extend({}, { - define: { - isEnabled: { - value: true - }, - test: { - Value: Example - }, - examples: { - value: { - define: { - one: { - Value: Example - }, - two: { - value: { - define: { - deep: { - Value: Example - } - } - } - } - } - } - } - } - }); - - var nested = new NestedMap(); - - // values are correct - equal(nested.attr('test.name'), nailedIt); - equal(nested.attr('examples.one.name'), nailedIt); - equal(nested.attr('examples.two.deep.name'), nailedIt); - - // objects are correctly instanced - ok(nested.attr('test') instanceof Example); - ok(nested.attr('examples.one') instanceof Example); - ok(nested.attr('examples.two.deep') instanceof Example); -}); - -test('Can make an attr alias a compute (#1470)', 9, function() { - var computeValue = can.compute(1); - var GetMap = can.Map.extend({ - define: { - value: { - set: function(newValue, setVal, setErr, oldValue) { - if (newValue.isComputed) { - return newValue; - } - if (oldValue && oldValue.isComputed) { - oldValue(newValue); - return oldValue; - } - return newValue; - }, - get: function(value) { - return value && value.isComputed ? value() : value; - } - } - } - }); - - var getMap = new GetMap(); - - getMap.attr("value", computeValue); - - equal(getMap.attr("value"), 1); - - var bindCallbacks = 0; - - getMap.bind("value", function(ev, newVal, oldVal) { - - switch (bindCallbacks) { - case 0: - equal(newVal, 2, "0 - bind called with new val"); - equal(oldVal, 1, "0 - bind called with old val"); - break; - case 1: - equal(newVal, 3, "1 - bind called with new val"); - equal(oldVal, 2, "1 - bind called with old val"); - break; - case 2: - equal(newVal, 4, "2 - bind called with new val"); - equal(oldVal, 3, "2 - bind called with old val"); - break; - } - - - bindCallbacks++; - }); - - // Try updating the compute's value - computeValue(2); - - // Try setting the value of the property - getMap.attr("value", 3); - - equal(getMap.attr("value"), 3, "read value is 3"); - equal(computeValue(), 3, "the compute value is 3"); - - // Try setting to a new comptue - var newComputeValue = can.compute(4); - - getMap.attr("value", newComputeValue); - -}); - -test('setting a value of a property with type "compute" triggers change events', function() { - - var handler; - var message = 'The change event passed the correct {prop} when set with {method}'; - - var createChangeHandler = function(expectedOldVal, expectedNewVal, method) { - return function(ev, newVal, oldVal) { - var subs = { - prop: 'newVal', - method: method - }; - equal(newVal, expectedNewVal, can.sub(message, subs)); - subs.prop = 'oldVal'; - equal(oldVal, expectedOldVal, can.sub(message, subs)); - }; - }; - - var ComputableMap = can.Map.extend({ - define: { - computed: { - type: 'compute', - } - } - }); - - var computed = can.compute(0); - - var m1 = new ComputableMap({ - computed: computed - }); - - equal(m1.attr('computed'), 0, 'm1 is 1'); - - handler = createChangeHandler(0, 1, ".attr('computed', newVal)"); - - - handler = createChangeHandler(0, 1, ".attr('computed', newVal)"); - m1.bind('computed', handler); - m1.attr('computed', 1); - m1.unbind('computed', handler); - - handler = createChangeHandler(1, 2, "computed()"); - m1.bind('computed', handler); - computed(2); - m1.unbind('computed', handler); -}); - -test('replacing the compute on a property with type "compute"', function() { - var compute1 = can.compute(0); - var compute2 = can.compute(1); - - var ComputableMap = can.Map.extend({ - define: { - computable: { - type: 'compute' - } - } - }); - - var m = new ComputableMap(); - - m.attr('computable', compute1); - - - equal(m.attr('computable'), 0, 'compute1 readable via .attr()'); - - m.attr('computable', compute2); - - - equal(m.attr('computable'), 1, 'compute2 readable via .attr()'); -}); - -// The old attributes plugin interferes severly with this test. -// TODO remove this condition when taking the plugins out of the main repository -test('value and get (#1521)', function() { - var MyMap = can.Map.extend({ - define: { - data: { - value: function() { - return new can.List(['test']); - } - }, - size: { - value: 1, - get: function(val) { - var list = this.attr('data'); - var length = list.attr('length'); - return val + length; - } - } - } - }); - - var map = new MyMap({}); - equal(map.attr('size'), 2); -}); - - -test("One event on getters (#1585)", function() { - - var AppState = can.Map.extend({ - define: { - person: { - get: function(lastSetValue, setAttrValue) { - if (lastSetValue) { - return lastSetValue; - } else if (this.attr("personId")) { - setAttrValue(new can.Map({ - name: "Jose", - id: 5 - })); - } else { - return null; - } - } - } - } - }); - - var appState = new AppState(); - var personEvents = 0; - appState.bind("person", function(ev, person) { - personEvents++; - }); - - appState.attr("personId", 5); - - - appState.attr("person", new can.Map({ - name: "Julia" - })); - - - equal(personEvents, 2); -}); - -test('Can read a defined property with a set/get method (#1648)', function() { - // Problem: "get" is not passed the correct "lastSetVal" - // Problem: Cannot read the value of "foo" - - var Map = can.Map.extend({ - define: { - foo: { - value: '', - set: function(setVal) { - return setVal; - }, - get: function(lastSetVal) { - return lastSetVal; - } - } - } - }); - - var map = new Map(); - - equal(map.attr('foo'), '', 'Calling .attr(\'foo\') returned the correct value'); - - map.attr('foo', 'baz'); - - equal(map.attr('foo'), 'baz', 'Calling .attr(\'foo\') returned the correct value'); -}); - -test('Can bind to a defined property with a set/get method (#1648)', 3, function() { - // Problem: "get" is not called before and after the "set" - // Problem: Function bound to "foo" is not called - // Problem: Cannot read the value of "foo" - - var Map = can.Map.extend({ - define: { - foo: { - value: '', - set: function(setVal) { - return setVal; - }, - get: function(lastSetVal) { - return lastSetVal; - } - } - } - }); - - var map = new Map(); - - map.bind('foo', function() { - ok(true, 'Bound function is called'); - }); - - equal(map.attr('foo'), '', 'Calling .attr(\'foo\') returned the correct value'); - - map.attr('foo', 'baz'); - - equal(map.attr('foo'), 'baz', 'Calling .attr(\'foo\') returned the correct value'); -}); - - -test("type converters handle null and undefined in expected ways (1693)", function() { - - var Typer = can.Map.extend({ - define: { - date: { - type: 'date' - }, - string: { - type: 'string' - }, - number: { - type: 'number' - }, - 'boolean': { - type: 'boolean' - }, - htmlbool: { - type: 'htmlbool' - }, - leaveAlone: { - type: '*' - } - } - }); - - var t = new Typer().attr({ - date: undefined, - string: undefined, - number: undefined, - 'boolean': undefined, - htmlbool: undefined, - leaveAlone: undefined - }); - - equal(t.attr("date"), undefined, "converted to date"); - - equal(t.attr("string"), undefined, "converted to string"); - - equal(t.attr("number"), undefined, "converted to number"); - - equal(t.attr("boolean"), false, "converted to boolean"); - - equal(t.attr("htmlbool"), false, "converted to htmlbool"); - - equal(t.attr("leaveAlone"), undefined, "left as object"); - - t = new Typer().attr({ - date: null, - string: null, - number: null, - 'boolean': null, - htmlbool: null, - leaveAlone: null - }); - - equal(t.attr("date"), null, "converted to date"); - - equal(t.attr("string"), null, "converted to string"); - - equal(t.attr("number"), null, "converted to number"); - - equal(t.attr("boolean"), false, "converted to boolean"); - - equal(t.attr("htmlbool"), false, "converted to htmlbool"); - - equal(t.attr("leaveAlone"), null, "left as object"); - -}); - -test('Initial value does not call getter', function() { - expect(0); - - var Map = can.Map.extend({ - define: { - count: { - get: function(lastVal) { - ok(false, 'Should not be called'); - return lastVal; - } - } - } - }); - - new Map({ - count: 100 - }); -}); - -test("getters produce change events", function() { - var Map = can.Map.extend({ - define: { - count: { - get: function(lastVal) { - return lastVal; - } - } - } - }); - - var map = new Map(); - - map.bind("change", function() { - ok(true, "change called"); - }); - - map.attr("count", 22); -}); - -test("Asynchronous virtual properties cause extra recomputes (#1915)", function() { - - stop(); - - var ran = false; - var VM = can.Map.extend({ - define: { - foo: { - get: function(lastVal, setVal) { - setTimeout(function() { - if (setVal) { - setVal(5); - } - }, 10); - } - }, - bar: { - get: function() { - var foo = this.attr('foo'); - if (foo) { - if (ran) { - ok(false, 'Getter ran twice'); - } - ran = true; - return foo * 2; - } - } - } - } - }); - - var vm = new VM(); - vm.bind('bar', function() {}); - - setTimeout(function() { - equal(vm.attr('bar'), 10); - start(); - }, 200); - -}); - - -test("double get in a compute (#2230)", function() { - var VM = can.Map.extend({ - define: { - names: { - get: function(val, setVal) { - ok(setVal, "setVal passed"); - return 'Hi!'; - } - } - } - }); - - var vm = new VM(); - - var c = can.compute(function() { - return vm.attr("names"); - }); - - c.bind("change", function() {}); - -}); diff --git a/map/define/doc/TypeConstructor.md b/map/define/doc/TypeConstructor.md deleted file mode 100644 index 2cf3b950ed4..00000000000 --- a/map/define/doc/TypeConstructor.md +++ /dev/null @@ -1,23 +0,0 @@ -@function can.Map.prototype.define.TypeConstructor Type -@parent can.Map.prototype.define - -Provides a constructor function to be used to convert any value passed into [can.Map::attr attr] into an appropriate value - - -@signature `constructorFunc` - -A constructor function can be provided that is called to convert incoming values set on this property, like: - - define: { - prop: { - Type: Person - } - } - -@body - -Similar to [can.Map.prototype.define.type type], this uppercase version provides a mechanism for converting incoming values to another format or type. - -Specifically, this constructor will be invoked any time this property is set, and any data passed into the setter will be passed as arguments for the constructor. - -If the call to attr passes an object that is already an instance of the constructor specified with `Type`, no conversion is done. diff --git a/map/define/doc/ValueConstructor.md b/map/define/doc/ValueConstructor.md deleted file mode 100644 index 9c8b8878def..00000000000 --- a/map/define/doc/ValueConstructor.md +++ /dev/null @@ -1,21 +0,0 @@ -@function can.Map.prototype.define.ValueConstructor Value -@parent can.Map.prototype.define - -Provides a constructor function to be used to provide a default value for a certain property of a can.Map. This constructor will be invoked with `new` each time a new instance of the map is created. - -@signature `constructorFunc` - -A constructor function can be provided that is called to create a default value used for this property, like: - - define: { - prop: { - Value: Array - }, - person: { - Value: Person - } - } - -@body - -Similar to [can.Map.prototype.define.value value], this uppercase version provides a mechanism for providing a default value. If the default value is an object, providing a constructor is a good way to ensure a copy is made for each instance. \ No newline at end of file diff --git a/map/define/doc/attrDefinition.md b/map/define/doc/attrDefinition.md deleted file mode 100644 index a405f4b892e..00000000000 --- a/map/define/doc/attrDefinition.md +++ /dev/null @@ -1,176 +0,0 @@ -@typedef {{}} can.Map.prototype.define.attrDefinition attrDefinition -@parent can.Map.prototype.define - -Defines the type, initial value, and get, set, and remove behavior for an attribute of a [can.Map]. - -@option {can.Map.prototype.define.value|*} value Specifies the initial value of the attribute or -a function that returns the initial value. For example, a default value of `0` can be -specified like: - - define: { - prop: { - value: 0 - } - } - -`Object` types should not be specified directly on `value` because that same object will -be shared on every instance of the Map. Instead, a [can.Map::define.value value function] that -returns a fresh copy can be provided: - - define: { - prop: { - value: function(){ - return {foo: "bar"} - } - } - } - -@option {function} Value Specifies a function that will be called with `new` whose result is -set as the initial value of the attribute. For example, if the default value should be a can.List: - - define: { - prop: { - Value: can.List - } - } - -@option {can.Map.prototype.define.type|String} type Specifies the type of the -attribute. The type can be specified as either a [can.Map.prototype.define.type type function] -that returns the type coerced value or one of the following strings: - - - `"string"` - Converts the value to a string. - - `"date"` - Converts the value to a date or `null if the date can not be converted. - - `"number"` - Passes the value through `parseFloat`. - - `"boolean"` - Converts falsey, `"false"` or `"0"` to `false` and everything else to true. - - `"*"` - Prevents the default type coersion of converting Objects to [can.Map]s and Arrays to [can.List]s. - -The following example converts the `count` property to a number and the `items` property to an array: - - define: { - count: {type: "number"}, - items: { - type: function(newValue){ - if(typeof newValue === "string") { - return newValue.split(",") - } else if( can.isArray(newValue) ) { - return newValue; - } - } - } - } - -@option {function} Type A constructor function that takes -the value passed to [can.Map::attr attr] as the first argument and called with -new. For example, if you want whatever -gets passed to go through `new Array(newValue)` you can do that like: - - define: { - items: { - Type: Array - } - } - -If the value passed to [can.Map::attr attr] is already an Array, it will be left as is. - -@option {can.Map.prototype.define.set} set A set function that specifies what should happen when an attribute -is set on a [can.Map]. `set` is called with the result of `type` or `Type`. The following -defines a `page` setter that updates the map's offset: - - define: { - page: { - set: function(newVal){ - this.attr('offset', (parseInt(newVal) - 1) * - this.attr('limit')); - } - } - } - -@option {can.Map.prototype.define.get} get A function that specifies how the value is retrieved. The get function is -converted to an [can.compute.async async compute]. It should derive its value from other values -on the map. The following -defines a `page` getter that reads from a map's offset and limit: - - define: { - page: { - get: function (newVal) { - return Math.floor(this.attr('offset') / - this.attr('limit')) + 1; - } - } - } - -A `get` definition makes the property __computed__ which means it will not be serialized by default. - -@option {can.Map.prototype.define.remove} remove A function that specifies what should happen when an attribute is removed -with [can.Map::removeAttr removeAttr]. The following removes a `modelId` when `makeId` is removed: - - define: { - makeId: { - remove: function(){ - this.removeAttr("modelId"); - } - } - } - -@option {can.Map.prototype.define.serialize|Boolean} serialize Specifies the behavior of the -property when [can.Map::serialize serialize] is called. - -By default, serialize does not include computed values. Properties with a `get` definition -are computed and therefore are not added to the result. Non-computed properties values are -serialized if possible and added to the result. - - Paginate = can.Map.extend({ - define: { - pageNum: { - get: function(){ return this.offset() / 20 } - } - } - }); - - p = new Paginate({offset: 40}); - p.serialize() //-> {offset: 40} - -If `true` is specified, computed properties will be serialized and added to the result. - - Paginate = can.Map.extend({ - define: { - pageNum: { - get: function(){ return this.offset() / 20 }, - serialize: true - } - } - }); - - p = new Paginate({offset: 40}); - p.serialize() //-> {offset: 40, pageNum: 2} - - -If `false` is specified, non-computed properties will not be added to the result. - - Paginate = can.Map.extend({ - define: { - offset: { - serialize: false - } - } - }); - - p = new Paginate({offset: 40}); - p.serialize() //-> {} - -If a [can.Map.prototype.define.serialize serialize function] is specified, the result -of the function is added to the result. - - Paginate = can.Map.extend({ - define: { - offset: { - serialize: function(offset){ - return (offset / 20)+1 - } - } - } - }); - - p = new Paginate({offset: 40}); - p.serialize() //-> {offset: 3} - diff --git a/map/define/doc/define.md b/map/define/doc/define.md deleted file mode 100644 index 9690922dd85..00000000000 --- a/map/define/doc/define.md +++ /dev/null @@ -1,121 +0,0 @@ -@property {Object} can.Map.prototype.define define -@parent can.Map.plugins - -Defines the -type, initial value, get, set, remove, and serialize behavior for attributes -of a [can.Map]. - -@option {Object} A map of -attribute names to [can.Map::define.attrDefinition attribute definition] -objects. - -@body - -## Use - -The [can.Map::define can.Map.define] plugin allows you to completely control the behavior -of attributes on a [can.Map]. To use it, you specify -an define object that is a mapping of properties -to [can.Map::define.attrDefinition attribute definitions]. The following example -specifies a Paginate Map: - - var Paginate = can.Map.extend({ - define: { - count: { - type: "number", - value: Infinity, - // Keeps count above 0. - set: function(newCount){ - return newCount < 0 ? 0 : newCount; - } - }, - offset: { - type: "number", - value: 0, - // Keeps offset between 0 and count - set: function(newOffset){ - var count = this.attr("count"); - return newOffset < 0 ? - 0 : - Math.min(newOffset, !isNaN( count - 1) ? - count - 1 : - Infinity); - } - }, - limit: { - type: "number", - value: 5 - }, - page: { - // Setting page changes the offset - set: function(newVal){ - this.attr('offset', (parseInt(newVal) - 1) * - this.attr('limit')); - }, - // The page value is derived from offset and limit. - get: function (newVal) { - return Math.floor(this.attr('offset') / - this.attr('limit')) + 1; - } - } - } - }); - -## Default behaviors - -The [can.Map::define can.Map.define] plugin not only allows you to define -individual attribute behaviors on a [can.Map], but you can also define default -behaviors that would apply to any unspecified attribute. This is particularly -helpful for when you need a particular behavior to apply to every attribute on -a [can.Map] but won't be certain of what every attribute will be. - -The following example is a [can.Map] that is tied to [can.route] where only -specified attributes that are serialized will be updated in the location hash: - - var State = can.Map.extend({ - define: { - foo: { - serialize: true - }, - '*': { - serialize: false - } - } - }); - - var state = new State(); - - // tie State map to the route - can.route.map(state); - can.route.ready(); - - state.attr('foo', 'bar'); - state.attr('bar', 'baz'); - - window.location.hash; // -> #!foo=bar - - -## Overview - -This plugin is a replacement for the now deprecated [can.Map.attributes attributes] and [can.Map.setter setter] plugins. It intends to provide a single place to define the behavior of all the properties of a can.Map. - -Here is the cliffnotes version of this plugin. To define... - -* The default value for a property - use [can.Map.prototype.define.value value] -* That default value as a constructor function - use [can.Map.prototype.define.ValueConstructor Value] -* What value is returned when a property is read - use [can.Map.prototype.define.get get] -* Behavior when a property is set - use [can.Map.prototype.define.set set] -* How a property is serialized when [can.Map::serialize serialize] is called on it - use [can.Map.prototype.define.serialize serialize] -* Behavior when a property is removed - use [can.Map.prototype.define.remove remove] -* A custom converter method or a pre-defined standard converter called whenever a property is set - use [can.Map.prototype.define.type type] -* That custom converter method as a constructor function - use [can.Map.prototype.define.TypeConstructor Type] - -## Demo - -The following shows picking cars by make / model / year: - -@demo can/map/define/doc/examples/make-model-year.html - - - - diff --git a/map/define/doc/examples/make-model-year.html b/map/define/doc/examples/make-model-year.html deleted file mode 100644 index 944c9cf1529..00000000000 --- a/map/define/doc/examples/make-model-year.html +++ /dev/null @@ -1,228 +0,0 @@ -
                -
                - -
                - - - diff --git a/map/define/doc/get.md b/map/define/doc/get.md deleted file mode 100644 index 93a7edec969..00000000000 --- a/map/define/doc/get.md +++ /dev/null @@ -1,194 +0,0 @@ -@function can.Map.prototype.define.get get -@parent can.Map.prototype.define - -Specify what happens when a certain property is read on a map. `get` functions -work like a [can.compute] and automatically update themselves when a dependent -observable value is changed. - - -@signature `get( [lastSetValue] )` - - Defines the behavior when a value is read on a [can.Map]. Used to provide properties that derive their value from - other properties of the map, or __update__ their value from - the changes in the value that was set. - - @param {*} [lastSetValue] The value last set by `.attr(property, value)`. Typically, _lastSetValue_ - should be an observable value, like a [can.compute] or promise. If it's not, it's likely - that a [can.Map.prototype.define.set define.set] should be used instead. - - @return {*} The value of the property. - -@signature `get( lastSetValue, setAttrValue(value) )` - - Asynchronously defines the behavior when a value is read on a [can.Map]. Used to provide property values that - are available asynchronously. - - @param {*} lastSetValue The value last set by `.attr(property, value)`. - - @param {function(*)} setAttrValue(value) Updates the value of the property. This can be called - multiple times if needed. - -@body - -## Use - -Getter methods are useful for: - - - Defining virtual properties on a map. - - Defining property values that change with their _internal_ set value. - -## Virtual properties - - -Virtual properties are properties that don't actually store any value, but derive their value -from some other properties on the map. - -Whenever a getter is provided, it is wrapped in a [can.compute], which ensures -that whenever its dependent properties change, a change event will fire for this property also. - -``` -var Person = can.Model.extend({ - define: { - fullName: { - get: function () { - return this.attr("first") + " " + this.attr("last"); - } - } - } -}); - -var p = new Person({first: "Justin", last: "Meyer"}); - -p.attr("fullName"); // "Justin Meyer" - -p.bind("fullName", function(ev, newVal){ - newVal //-> "Lincoln Meyer"; -}); - -p.attr("first","Lincoln"); -``` - -## Asyncronous virtual properties - -Often, a virtual property's value only becomes available after some period of time. For example, -given a `personId`, one might want to retrieve a related person: - -``` -var AppState = can.Map.extend({ - define: { - person: { - get: function(lastSetValue, setAttrValue){ - Person.findOne({id: this.attr("personId")}) - .then(function(person){ - setAttrValue(person); - }); - } - } - } -}); -``` - -Asyncronous properties should be bound to before reading their value. If -they are not bound to, the `get` function will be called each time. - -The following example will make multiple `Person.findOne` requests: - -``` -var state = new AppState({personId: 5}); -state.attr("person") //-> undefined - -// called sometime later ... -state.attr("person") //-> undefined -``` - -However, by binding, the compute only reruns the `get` function once `personId` changes: - -``` -var state = new AppState({personId: 5}); - -state.bind("person", function(){}) - -state.attr("person") //-> undefined - -// called sometime later -state.attr("person") //-> Person<{id: 5}> -``` - -A template like [can.stache] will automatically bind for you, so you can pass -`state` to the template like the following without binding: - -``` -var template = can.stache("{{person.fullName}}"); -var state = new AppState({}); -var frag = template(state); - -state.attr("personId",5); -frag.childNodes[0].innerHTML //=> "" - -// sometime later -frag.childNodes[0].innerHTML //=> "Lincoln Meyer" - -``` - -The magic tags are updated as `personId`, `person`, and `fullName` change. - - -## Properties values that change with their _internal_ set value - -A getter can be used to derive a value from a set value. A getter's -`lastSetValue` argument is the last value set by [can.Map::attr]. - -For example, a property might be set to a compute, but when read, provides the value -of the compute. - -``` -var MyMap = can.Map.extend({ - define: { - value: { - get: function( lastSetValue ){ - return lastSetValue(); - } - } - } -}); - -var map = new MyMap(); -var compute = can.compute(1); -map.attr("value", compute); - -map.attr("value") //-> 1 -compute(2); -map.attr("value") //-> 2 -``` - -This technique should only be used when the `lastSetValue` is some form of -observable, that when it changes, can update the `getter` value. - -For simple conversions, [can.Map.prototype.define.set] or [can.Map.prototype.define.type] should be used. - -## Updating the virtual property value - -It's very common (and better performing) to update the virtual property value -instead of replacing it. - -The following example creates an empty `locationIds` [can.List] when a new -instance of `Store` is created. However, as `locations` change, -the [can.List] will be updated with the `id`s of the `locations`. - - -``` -var Store = can.Map.extend({ - define: { - locationIds: { - Value: can.List, - get: function(initialValue){ - var ids = []; - this.attr('locations').each(function(location){ - ids.push(location.attr("id")); - }); - return initialValue.replace(ids); - } - } - } -}); -``` diff --git a/map/define/doc/remove.md b/map/define/doc/remove.md deleted file mode 100644 index bfdd5d479a3..00000000000 --- a/map/define/doc/remove.md +++ /dev/null @@ -1,24 +0,0 @@ -@function can.Map.prototype.define.remove remove -@parent can.Map.prototype.define - -Called when an attribute is removed. - -@signature `remove( currentValue )` - -@return {*|false} If `false` is returned, the value is not removed. - -@body - -## Use - -The following prevents removing the _prop_ attribute if someone tries to remove the value 0: - - - define: { - prop: { - remove: function( currentVal ){ - return currentVal !== 0; - } - } - } - diff --git a/map/define/doc/serialize.md b/map/define/doc/serialize.md deleted file mode 100644 index 90c677d4f2d..00000000000 --- a/map/define/doc/serialize.md +++ /dev/null @@ -1,59 +0,0 @@ -@function can.Map.prototype.define.serialize serialize -@parent can.Map.prototype.define - -Called when an attribute is removed. - -@signature `serialize( currentValue )` - -@param {*} value The current value of the attribute. - -@param {String} attr The name of the attribute being serialized. - -@return {*|undefined} If `undefined` is returned, the value is not serialized. - -@this {can.Map} The map instance being serialized. - -@body - -## Use - -[can.Map::serialize serialize] is useful for serializing a can.Map instance into -a more JSON-friendly form. This can be used for many reasons, including saving a -[can.Model] instance on the server or serializing [can.route.map]'s internal -can.Map for display in the hash or pushstate URL. - -The serialize property allows an opportunity to define how -each attribute will behave when the map is serialized. This can be useful for: - -- serializing complex types like dates, arrays, or objects into string formats -- causing certain properties to be ignored when serialize is called - -The following causes a locationIds property to be serialized into -the comma separated ID values of the location property on this map: - - define: { - locationIds: { - serialize: function(){ - var ids = []; - this.attr('locations').each(function(location){ - ids.push(location.id); - }); - return ids.join(','); - } - } - } - -Returning `undefined` for any property means this property will not be part of the serialized -object. For example, if the property numPages is not greater than zero, the following example -won't include it in the serialized object. - - define: { - prop: { - numPages: function( num ){ - if(num <= 0) { - return undefined; - } - return num; - } - } - } diff --git a/map/define/doc/set.md b/map/define/doc/set.md deleted file mode 100644 index ffe7143789d..00000000000 --- a/map/define/doc/set.md +++ /dev/null @@ -1,227 +0,0 @@ -@function can.Map.prototype.define.set set -@parent can.Map.prototype.define - -Specify what happens when a value is set on a map attribute. - -@signature `set( [newVal,] [setValue] )` - -A set function defines the behavior of what happens when a value is set on a -[can.Map]. It is typically used to: - - - Add or remove other attributes as side effects - - Coerce the set value into an appropriate action - -The behavior of the setter depends on the number of arguments specified. This means that a -setter like: - - define: { - prop: { - set: function(){} - } - } - -behaves differently than: - - define: { - prop: { - set: function(newVal){} - } - } - -@param {*} [newVal] The [can.Map::define.type type function] coerced value the user intends to set on the -can.Map. - -@param {function(*)} [setValue(newValue)] A callback that can set the value of the property -asyncronously. - -@return {*|undefined} If a non-undefined value is returned, that value is set as -the attribute value. - - -If an `undefined` value is returned, the behavior depends on the number of -arguments the setter declares: - - - If the setter _does not_ specify the `newValue` argument, the attribute value is set - to whatever was passed to [can.Map::attr attr]. - - If the setter specifies the `newValue` argument only, the attribute value will be removed. - - If the setter specifies both `newValue` and `setValue`, the value of the property will not be - updated until `setValue` is called. - - -@body - -## Use - -An attribute's `set` function can be used to customize the behavior of when an attribute value is set -via [can.Map::attr]. Lets see some common cases: - -#### Side effects - -The following makes setting a `page` property update the `offset`: - - define: { - page: { - set: function(newVal){ - this.attr('offset', (parseInt(newVal) - 1) * - this.attr('limit')); - } - } - } - -The following makes changing `makeId` remove the `modelId` property: - - define: { - makeId: { - set: function(newValue){ - // Check if we are changing. - if(newValue !== this.attr("makeId")) { - this.removeAttr("modelId"); - } - // Must return value to set as we have a `newValue` argument. - return newValue; - } - } - } - -#### Asynchronous Setter - -The following shows an async setter: - - define: { - prop: { - set: function( newVal, setVal){ - $.get("/something", {}, setVal ); - } - } - } - - -## Behavior depends on the number of arguments. - -When a setter returns `undefined`, its behavior changes depending on the number of arguments. - -With 0 arguments, the original set value is set on the attribute. - - MyMap = can.Map.extend({ - define: { - prop: {set: function(){}} - } - }) - - var map = new MyMap({prop : "foo"}); - - map.attr("prop") //-> "foo" - -With 1 argument, `undefined` will remove the property. - - - MyMap = can.Map.extend({ - define: { - prop: {set: function(newVal){}} - } - }) - - var map = new MyMap({prop : "foo"}); - - can.Map.keys(map) //-> [] - -With 2 arguments, `undefined` leaves the property in place. It is expected -that `setValue` will be called: - - MyMap = can.Map.extend({ - define: { - prop: {set: function(newVal, setValue){}} - } - }) - - var map = new MyMap({prop : "foo"}); - - map.attr("prop") //-> "foo" - -## Side effects - -A set function provides a useful hook for performing side effect logic as a certain property is being changed. - -For example, in the example below, Paginator can.Map includes a `page` property, which derives its value entirely from other properties (limit and offset). If something tries to set the `page` directly, the set method will set the value of `offset`: - - - var Paginate = can.Map.extend({ - define: { - page: { - set: function (newVal) { - this.attr('offset', (parseInt(newVal) - 1) * this.attr('limit')); - }, - get: function () { - return Math.floor(this.attr('offset') / this.attr('limit')) + 1; - } - } - } - }); - - var p = new Paginate({limit: 10, offset: 20}); - -## Merging - -By default, if a value returned from a setter is an object, array, can.Map, or can.List, the effect will be to replace the property with the new object completely. - - Contact = can.Map.extend({ - define: { - info: { - set: function(newVal){ - return newVal; - } - } - } - }) - - var alice = new Contact({ - info: {name: 'Alice Liddell', email: 'alice@liddell.com'} - }); - alice.attr(); // {name: 'Alice Liddell', 'email': 'alice@liddell.com'} - alice.info._cid; // '.map1' - - alice.attr('info', {name: 'Allison Wonderland', phone: '888-888-8888'}); - alice.attr(); // {name: 'Allison Wonderland', 'phone': '888-888-8888'} - alice.info._cid; // '.map2' - -By contrast, if you access a property of a Map using `.attr`, then change it by calling `.attr` on it directly, the new properties will be merged with the existing nested Map, not replaced. - - var contact = new can.Map({ - 'info' : {'breath' : 'smells like roses'} - }); - var newInfo = {'teeth' : 'shiny and clean'}; - contact.attr('info').attr(newInfo); // info is now a merged object - -If you would rather have the new Map or List merged into the current value, not replaced, call -`attr` inside the setter: - - - Contact = can.Map.extend({ - define: { - info: { - set: function(newVal){ - this.info.attr(newVal); - return this.info; - } - } - } - }) - - var alice = new Contact({ - info: {name: 'Alice Liddell', email: 'alice@liddell.com'} - }); - alice.attr(); // {name: 'Alice Liddell', 'email': 'alice@liddell.com'} - alice.info._cid; // '.map1' - - alice.attr('info', {name: 'Allison Wonderland', phone: '888-888-8888'}); - alice.attr(); - //{ - // name: 'Allison Wonderland', - // email: 'alice@liddell.com', - // phone: '888-888-8888' - //} - alice.info._cid; // '.map1' - -## Batched Changes - -By default, calls to set methods are wrapped in a call to [can.batch.start] and [can.batch.stop], so if a set method has side effects that set more than one property, all these sets are wrapped in a single batch for better performance. diff --git a/map/define/doc/type.md b/map/define/doc/type.md deleted file mode 100644 index 9a0f4782cc5..00000000000 --- a/map/define/doc/type.md +++ /dev/null @@ -1,90 +0,0 @@ -@function can.Map.prototype.define.type type -@parent can.Map.prototype.define - -Converts a value passed to [can.Map::attr attr] into an appropriate value. - -@param {*} newValue The value passed to `attr`. -@param {String} attrName The attribute name being set. -@this {can.Map} the instance of the can.Map. -@return {*} The value that should be passed to `set` or (if there is no `set` property) the value to set on the map instance. - -@body - -## Use - -The `type` property specifies the type of the attribute. The type can be specified -as either a type function that returns the type coerced value or one of the following strings: - - - `"string"` - Converts the value to a string except `null` or `undefined`. - - `"date"` - Converts the value to a date or `null` if the date can not be converted. - - `"number"` - Passes the value through `parseFloat` except for `null` or `undefined`. - - `"boolean"` - Converts falsey, `"false"` or `"0"` to `false` and everything else to true. - - `"htmlbool"` - Like `boolean`, but also converts empty strings to - `true`. Used, for example, when input is from component attributes like - `` - - `"compute"` - If the value set is a compute, will allow the returning of the computed value. - - `"*"` - Prevents the default type coersion of converting Objects to [can.Map]s and Arrays to [can.List]s. - -### Basic Example - -The following example converts the `count` property to a number and the `items` property to an array: - - define: { - count: {type: "number"}, - items: { - type: function(newValue){ - if(typeof newValue === "string") { - return newValue.split(",") - } else if( can.isArray(newValue) ) { - return newValue; - } - } - } - } - -When a user tries to set those properties like: - - map.attr({count: "4", items: "1,2,3"}); - -The number converter will be used to turn count into 4, and the items type converter function will be used to turn items into [1,2,3]. - -### Preventing Arrays and Objects from Automatic Conversion - -When an array is passed into a can.Map setter, it is automatically converted into a can.List. Likewise, objects are converted into can.Map instances. This behavior can be prevented like the following: - - define: { - locations: {type: "*"} - } - -When a user tries to set this property, the resulting value will remain an array. - - map.attr('locations', [1, 2, 3]); // locations is an array, not a can.List - -### Working with the 'compute' type - -Setting type as `compute` allows for resolving a computed property with the .attr() -method. - -``` -MyMap = can.Map.extend({ - define: { - value: { - type: "compute" - } - } -}); - -var myMap = new MyMap(); -var c = can.compute(5); - -myMap.attr("value",c); -myMap.attr("value"); //-> 5 - -c(6); -myMap.attr("value"); //-> 6 - -//Be sure if setting to pass the new compute -var c2 = can.compute("a"); -myMap.attr("value",c2); -myMap.attr("value"); //-> "a" -``` diff --git a/map/define/doc/value.md b/map/define/doc/value.md deleted file mode 100644 index 100ce31ead4..00000000000 --- a/map/define/doc/value.md +++ /dev/null @@ -1,36 +0,0 @@ -@function can.Map.prototype.define.value value -@parent can.Map.prototype.define - -Returns the default value for instances of this can.Map. This is called before `init`. - -@signature `defaulter()` - -A function can be provided that returns the default value used for this property, like: - - define: { - prop: { - value: function(){ return []; } - } - } - -If the default value should be an object of some type, it should be specified as the return value of a function (the above call signature) so that all instances of this map don't point to the same object. For example, if the property `value` above had not returned an empty array but instead just specified an array using the next call signature below, all instances of that map would point to the same array (because JavaScript passes objects by reference). - -@this {can.Map} the instance of the can.Map. - -@return {*} The default value. This will be passed through setter and type. - -@signature `defaulVal` - -Any value can be provided as the default value used for this property, like: - - define: { - prop: { - value: 'foo' - } - } - -@param {*} defaultVal The default value, which will be passed through setter and type. - -@body - -There is a third way to provide a default value, which is explained in the [can.Map.prototype.define.ValueConstructor Value] docs page. `value` lowercased is for providing default values for a property type, while `Value` uppercased is for providing a constructor function, which will be invoked with `new` to create a default value for each instance of this map. \ No newline at end of file diff --git a/map/define/test.html b/map/define/test.html deleted file mode 100644 index d6fa91894e9..00000000000 --- a/map/define/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/map/define - -
                diff --git a/map/doc/map.md b/map/doc/map.md deleted file mode 100644 index f7bbd52b7e1..00000000000 --- a/map/doc/map.md +++ /dev/null @@ -1,140 +0,0 @@ -@constructor can.Map -@inherits can.Construct -@parent canjs -@group can.Map.prototype 0 Prototype -@group can.Map.static 1 Static -@group can.Map.plugins 2 Plugins -@test can/map/test.html -@plugin can/map -@release 2.0 -@link ../docco/map/map.html docco - -@description Create observable objects. - - -@signature `new can.Map([props])` - -Creates a new instance of can.Map. - -@param {Object} [props] Properties and values to seed the Observe with. -@return {can.Map} An instance of `can.Map` with the properties from _props_. - -@signature `can.Map.extend([name,] [staticProperties,] instanceProperties)` - -Creates a new extended constructor function. - - -@body - -## Use - -Watch this video to see an example of creating an ATM machine using can.Map: - - - - -`can.Map` provides a way for you to listen for and keep track of changes -to objects. When you use the getters and setters provided by `can.Map`, -events are fired that you can react to. `can.Map` also has support for -working with deep properties. Observable arrays are also available with -`[can.List]`, which is based on `can.Map`. - -## Working with Observes - -To create an Observe, use `new can.Map([props])`. This will return a -copy of `props` that emits events when its properties are changed with -`[can.Map.prototype.attr attr]`. - -You can read the values of properties on Observes directly, but you should -never set them directly. You can also read property values using `attr`. - - - var aName = {a: 'Alexis'}, - map = new can.Map(aName); - - // Observes are copies of data: - aName === map; // false - - // reading from an Observe: - map.attr(); // {a: 'Alexis'} - map.a; // 'Alexis' - map.attr('a'); // 'Alexis' - - // setting an Observe's property: - map.attr('a', 'Alice'); - map.a; // Alice - - // removing an Observe's property; - map.removeAttr('a'); - map.attr(); // {} - - // Don't do this! - map.a = 'Adam'; // wrong! - - -Find out more about manipulating properties of a map under -[can.Map.prototype.attr attr] and [can.Map.prototype.removeAttr removeAttr]. - -## Listening to changes - -The real power of maps comes from being able to react to -properties being added, set, and removed. Maps emit events when -properties are changed that you can bind to. - -`can.Map` has two types of events that fire due to changes on a map: -- the _change_ event fires on every change to a map. -- an event named after the property name fires on every change to that property. - - - var o = new can.Map({}); - o.bind('change', function(ev, attr, how, newVal, oldVal) { - console.log('Something on o changed.'); - }); - o.bind('a', function(ev, newVal, oldVal) { - console.log('a was changed.'); - }); - - o.attr('a', 'Alexis'); // 'Something on o changed.' - // 'a was changed.' - o.attr({ - 'a': 'Alice', // 'Something on o changed.' (for a's change) - 'b': 'Bob' // 'Something on o changed.' (for b's change) - }); // 'a was changed.' - - o.removeAttr('a'); // 'Something on o changed.' - // 'a was changed.' - - -For more detail on how to use these events, see [can.Map.prototype.bind bind] and -[can.Map.prototype.unbind unbind]. There is also a plugin called [can.Map.delegate] -that makes binding to specific types of events easier: - - - var o = new can.Map({}); - o.delegate('a', 'add', function(ev, newVal, oldVal) { - console.log('a was added.'); - }); - o.delegate('a', 'set', function(ev, newVal, oldVal) { - console.log('a was set.'); - }); - o.delegate('a', 'remove', function(ev, newVal, oldVal) { - console.log('a was removed.'); - }); - o.delegate('a', 'change', function(ev, newVal, oldVal) { - console.log('a was changed.'); - }); - - o.attr('a', 'Alexis'); // 'a was added.' - // 'a was changed.' - - o.attr('a', 'Alice'); // 'a was set.' - // 'a was changed.' - - o.removeAttr('a'); // 'a was removed.' - // 'a was changed.' - -## Object.prototype.watch - -Due to a method available on the base Object prototype called "watch", refrain from -using properties with the same name on Gecko based browsers as there will be a -collision. [Source](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/watch) diff --git a/map/doc/prototype.attr.md b/map/doc/prototype.attr.md deleted file mode 100644 index fd36c7974df..00000000000 --- a/map/doc/prototype.attr.md +++ /dev/null @@ -1,159 +0,0 @@ -@function can.Map.prototype.attr attr -@parent can.Map.prototype 2 - -@description Get or set properties on a Map. - -@signature `map.attr()` - -Gets a collection of all the properties in this `can.Map`. - -@return {Object} an object with all the properties in this `can.Map`. - -@signature `map.attr(key)` - -Reads a property from this `can.Map`. - -@param {String} key the property to read -@return {*} the value assigned to _key_. - -@signature `map.attr(key, value)` - -Assigns _value_ to a property on this `can.Map` called _key_. - -@param {String} key the property to set -@param {*} the value to assign to _key_. -@return {can.Map} this Map, for chaining - -@signature `map.attr(obj[, removeOthers])` - -Assigns each value in _obj_ to a property on this `can.Map` named after the -corresponding key in _obj_, effectively merging _obj_ into the Map. - -@param {Object} obj a collection of key-value pairs to set. -If any properties already exist on the `can.Map`, they will be overwritten. - -@param {bool} [removeOthers=false] whether to remove keys not present in _obj_. -To remove keys without setting other keys, use `[can.Map::removeAttr removeAttr]`. - -@return {can.Map} this Map, for chaining - -@body -`attr` gets or sets properties on the `can.Map` it's called on. Here's a tour through -how all of its forms work: - - - var people = new can.Map({}); - - // set a property: - people.attr('a', 'Alex'); - - // get a property: - people.attr('a'); // 'Alex' - - // set and merge multiple properties: - people.attr({ - a: 'Alice', - b: 'Bob' - }); - - // get all properties: - people.attr(); // {a: 'Alice', b: 'Bob'} - - // set properties while removing others: - people.attr({ - b: 'Bill', - e: 'Eve' - }, true); - - people.attr(); // {b: 'Bill', e: 'Eve'} - - -## Deep properties - -`attr` can also set and read deep properties. All you have to do is specify -the property name as you normally would if you weren't using `attr`. - - - var people = new can.Map({names: {}}); - - // set a property: - people.attr('names.a', 'Alice'); - - // get a property: - people.attr('names.a'); // 'Alice' - people.names.attr('a'); // 'Alice' - - // get all properties: - people.attr(); // {names: {a: 'Alice'}} - - -Objects that are added to Observes become Observes themselves behind the scenes, -so changes to deep properties fire events at each level, and you can bind at any -level. As this example shows, all the same events are fired no matter what level -you call `attr` at: - - - var people = new can.Map({names: {}}); - - people.bind('change', function(ev, attr, how, newVal, oldVal) { - console.log('people change: ' + attr + ', ' + how + ', ' + newVal + ', ' + oldVal); - }); - - people.names.bind('change', function(ev, attr, how, newVal, oldVal) { - console.log('people.names change' + attr + ', ' + how + ', ' + newVal + ', ' + oldVal); - }); - - people.bind('names', function(ev, newVal, oldVal) { - console.log('people names: ' + newVal + ', ' + oldVal); - }); - - people.names.bind('a', function(ev, newVal, oldVal) { - console.log('people.names a: ' + newVal + ', ' + oldVal); - }); - - people.bind('names.a', function(ev, newVal, oldVal) { - console.log('people names.a: ' + newVal + ', ' + oldVal); - }); - - people.attr('names.a', 'Alice'); // people change: names.a, add, Alice, undefined - // people.names change: a, add, Alice, undefined - // people.names a: Alice, undefined - // people names.a: Alice, undefined - - people.names.attr('b', 'Bob'); // people change: names.b, add, Bob, undefined - // people.names change: b, add, Bob, undefined - // people.names b: Bob, undefined - // people names.b: Bob, undefined - - -## Properties with dots in their name - -As shown above, `attr` enables reading and setting deep properties so special care must be taken when property names include dots '`.`'. To read a property containing dots, escape each one using '`\`'. This prevents `attr` from performing a deep lookup and throwing an error when the deep property is not found. - -``` -var person = new can.Map({ - 'first.name': 'Alice' -}); - -person.attr('first.name'); // throws Error -person.attr('first\.name'); // 'Alice' - -``` - -When setting a property containing dots, pass an object to `attr` containing the property name and new value. Setting a property by passing a string to `attr` will attempt to set a deep property and will throw an error. - -``` -var person = new can.Map({ - 'first.name': 'Alice' -}); - -person.attr('first.name', 'Bob'); // throws Error -person.attr('first\.name', 'Bob'); // throws Error -person.attr({'first.name': 'Bob'}); // Works - -``` - -## See also - -For information on the events that are fired on property changes and how -to listen for those events, see [can.Map.prototype.bind bind]. diff --git a/map/doc/prototype.bind.md b/map/doc/prototype.bind.md deleted file mode 100644 index c14607907f2..00000000000 --- a/map/doc/prototype.bind.md +++ /dev/null @@ -1,99 +0,0 @@ -@function can.Map.prototype.bind bind -@parent can.Map.prototype 3 - -@description Bind event handlers to a Map. - -@signature `map.bind(eventType, handler)` - -@param {String} eventType the type of event to bind this handler to -@param {Function} handler the handler to be called when this type of event fires -The signature of the handler depends on the type of event being bound. See below -for details. -@return {can.Map} this Map, for chaining - -@body -`bind` binds event handlers to property changes on `can.Map`s. When you change -a property using `attr`, two events are fired on the Map, allowing other parts -of your application to map the changes to the object. - -## The _change_ event - -The first event that is fired is the _change_ event. The _change_ event is useful -if you want to react to all changes on a Map. - - - var o = new can.Map({}); - o.bind('change', function(ev, attr, how, newVal, oldVal) { - console.log('Something changed.'); - }); - - -The parameters of the event handler for the _change_ event are: - -- _ev_ The event object. -- _attr_ Which property changed. -- _how_ Whether the property was added, removed, or set. Possible values are `'add'`, `'remove'`, or `'set'`. -- _newVal_ The value of the property after the change. `newVal` will be `undefined` if the property was removed. -- _oldVal_ This is the value of the property before the change. `oldVal` will be `undefined` if the property was added. - -Here is a concrete tour through the _change_ event handler's arguments: - - - var o = new can.Map({}); - o.bind('change', function(ev, attr, how, newVal, oldVal) { - console.log(ev + ', ' + attr + ', ' + how + ', ' + newVal + ', ' + oldVal); - }); - - o.attr('a', 'Alexis'); // [object Object], a, add, Alexis, undefined - o.attr('a', 'Adam'); // [object Object], a, set, Adam, Alexis - o.attr({ - 'a': 'Alice', // [object Object], a, set, Alice, Adam - 'b': 'Bob' // [object Object], b, add, Bob, undefined - }); - o.removeAttr('a'); // [object Object], a, remove, undefined, Alice - - -(See also `[can.Map::removeAttr removeAttr]`, which removes properties). - -## The _property name_ event - -The second event that is fired is an event whose type is the same as the changed -property's name. This event is useful for noticing changes to a specific property. - - - var o = new can.Map({}); - o.bind('a', function(ev, newVal, oldVal) { - console.log('The value of a changed.'); - }); - - -The parameters of the event handler for the _property name_ event are: - -- _ev_ The event object. -- _newVal_ The value of the property after the change. `newVal` will be `undefined` if the property was removed. -- _oldVal_ The value of the property before the change. `oldVal` will be `undefined` if the property was added. - -Here is a concrete tour through the _property name_ event handler's arguments: - - - var o = new can.Map({}); - o.bind('a', function(ev, newVal, oldVal) { - console.log(ev + ', ' + newVal + ', ' + oldVal); - }); - - o.attr('a', 'Alexis'); // [object Object], Alexis, undefined - o.attr('a', 'Adam'); // [object Object], Adam, Alexis - o.attr({ - 'a': 'Alice', // [object Object], Alice, Adam - 'b': 'Bob' - }); - o.removeAttr('a'); // [object Object], undefined, Alice - - -## See also - -More information about changing properties on Observes can be found under -[can.Map.prototype.attr attr]. - -For a more specific way to changes on Observes, see the [can.Map.delegate] plugin. -*/ \ No newline at end of file diff --git a/map/doc/prototype.compute-attr.md b/map/doc/prototype.compute-attr.md deleted file mode 100644 index a1540d2f989..00000000000 --- a/map/doc/prototype.compute-attr.md +++ /dev/null @@ -1,68 +0,0 @@ -@property {can.compute} can.Map.prototype.COMPUTE-ATTR -@parent can.Map.prototype 0 - -@deprecated {2.3} Using attributes as a compute directly have been deprecated in favor of the [can.Map.prototype.define define] plugin, which provides the same -functionality. It will still be maintained up to 3.0 and potentially after. -Projects using computes as a direct attribute should consider switching the -[can.Map.prototype.define define] plugin. - -@description Specify an attribute that is computed from other attributes. - -@option {can.compute} A compute that reads values on instances of the -map and returns a derived value. The compute may also be a getter-setter -compute and able to be passed a value. - -@body - -## Use - -When extending [can.Map], if a prototype property is a [can.compute] -it will setup that compute to behave like a normal attribute. This means -that it can be read and written to with [can.Map::attr attr] and bound to -with [can.Map::bind bind]. - -The following example makes a `fullName` attribute on `Person` maps: - - var Person = can.Map.extend({ - fullName: can.compute(function(){ - return this.attr("first") + " " + this.attr("last") - }) - }) - - var me = new Person({first: "Justin", last: "Meyer"}) - - me.attr("fullName") //-> "Justin Meyer" - - me.bind("fullName", function(ev, newValue, oldValue){ - newValue //-> Brian Moschel - oldValue //-> Justin Meyer - }) - - me.attr({first: "Brian", last: "Moschel"}) - -## Getter / Setter computes - -A compute's setter will be called if [can.Map::attr attr] is -used to set the compute-property's value. - -The following makes `fullName` able to set `first` and `last`: - - var Person = can.Map.extend({ - fullName: can.compute(function(newValue){ - if( arguments.length ) { - var parts = newValue.split(" "); - this.attr({ - first: parts[0], - last: parts[1] - }); - } else { - return this.attr("first") + " " + this.attr("last"); - } - }) - }) - - var me = new Person({first: "Justin", last: "Meyer"}) - - me.attr("fullName", "Brian Moschel") - me.attr("first") //-> "Brian" - me.attr("last") //-> "Moschel" diff --git a/map/doc/prototype.compute.md b/map/doc/prototype.compute.md deleted file mode 100644 index bcc57b28fa7..00000000000 --- a/map/doc/prototype.compute.md +++ /dev/null @@ -1,28 +0,0 @@ -@function can.Map.prototype.compute compute -@parent can.Map.prototype 4 - -@description Make a can.compute from an observable property. - -@signature `map.compute(attrName)` -@param {String} attrName the property to bind to -@return {can.compute} a [can.compute] bound to _attrName_ - -@body - -`compute` is a convenience method for making computes from properties -of Observes. More information about computes can be found under [can.compute]. - - - var map = new can.Map({a: 'Alexis'}); - var name = map.compute('a'); - name.bind('change', function(ev, nevVal, oldVal) { - console.log('a changed from ' + oldVal + 'to' + newName + '.'); - }); - - name(); // 'Alexis' - - map.attr('a', 'Adam'); // 'a changed from Alexis to Adam.' - name(); // 'Adam' - - name('Alice'); // 'a changed from Adam to Alice.' - name(); // 'Alice' diff --git a/map/doc/prototype.default-attr.md b/map/doc/prototype.default-attr.md deleted file mode 100644 index a8c27a2bf40..00000000000 --- a/map/doc/prototype.default-attr.md +++ /dev/null @@ -1,33 +0,0 @@ -@property {*} can.Map.prototype.DEFAULT-ATTR -@parent can.Map.prototype 1 - -@description Specify a default property and value. - -@option {*} A value of any type other than a function that will -be set as the `DEFAULT-ATTR` attribute's value. - -@body - -## Use - -When extending [can.Map], if a prototype property is not a function, -it is used as a default value on instances of the extended Map. For example: - -``` -var Paginate = can.Map.extend({ - limit: 20, - offset: 0, - next: function(){ - this.attr("offset", this.attr("offset")+this.attr("limit")) - } -}); - -var paginate = new Paginate({limit: 30}); - -paginate.attr("offset") //-> 0 -paginate.attr("limit") //-> 30 - -paginate.next(); - -paginate.attr("offset") //-> 30 -``` diff --git a/map/doc/prototype.each.md b/map/doc/prototype.each.md deleted file mode 100644 index 55178d61e21..00000000000 --- a/map/doc/prototype.each.md +++ /dev/null @@ -1,36 +0,0 @@ -@function can.Map.prototype.each each -@parent can.Map.prototype 5 - -@description Call a function on each property of a Map. - -@signature `map.each( callback(item, propName ) )` - -`each` iterates through the Map, calling a function -for each property value and key. - -@param {function(*,String)} callback(item,propName) the function to call for each property -The value and key of each property will be passed as the first and second -arguments, respectively, to the callback. If the callback returns false, -the loop will stop. - -@return {can.Map} this Map, for chaining - -@body - - var names = []; - new can.Map({a: 'Alice', b: 'Bob', e: 'Eve'}).each(function(value, key) { - names.push(value); - }); - - names; // ['Alice', 'Bob', 'Eve'] - - names = []; - new can.Map({a: 'Alice', b: 'Bob', e: 'Eve'}).each(function(value, key) { - names.push(value); - if(key === 'b') { - return false; - } - }); - - names; // ['Alice', 'Bob'] - \ No newline at end of file diff --git a/map/doc/prototype.removeAttr.md b/map/doc/prototype.removeAttr.md deleted file mode 100644 index 6a9f0509b79..00000000000 --- a/map/doc/prototype.removeAttr.md +++ /dev/null @@ -1,24 +0,0 @@ -@function can.Map.prototype.removeAttr removeAttr -@parent can.Map.prototype 6 - -@description Remove a property from a Map. - -@signature `map.removeAttr(attrName)` -@param {String} attrName the name of the property to remove -@return {*} the value of the property that was removed - -@body -`removeAttr` removes a property by name from a Map. - - - var people = new can.Map({a: 'Alice', b: 'Bob', e: 'Eve'}); - - people.removeAttr('b'); // 'Bob' - people.attr(); // {a: 'Alice', e: 'Eve'} - - -Removing an attribute will cause a _change_ event to fire with `'remove'` -passed as the _how_ parameter and `undefined` passed as the _newVal_ to -handlers. It will also cause a _property name_ event to fire with `undefined` -passed as _newVal_. An in-depth description at these events can be found -under `[can.Map.prototype.attr attr]`. \ No newline at end of file diff --git a/map/doc/prototype.serialize.md b/map/doc/prototype.serialize.md deleted file mode 100644 index a20470de490..00000000000 --- a/map/doc/prototype.serialize.md +++ /dev/null @@ -1,26 +0,0 @@ -@function can.Map.prototype.serialize serialize -@parent can.Map.prototype 7 - -@description Serialize this object to something that can be passed to `JSON.stringify`. - -@signature `map.serialize()` - -Get the serialized Object form of the map. Serialized -data is typically used to send back to a server. - - - o.serialize() //-> { name: 'Justin' } - - -Serialize currently returns the same data -as [can.Map.prototype.attrs]. However, in future -versions, serialize will be able to return serialized -data similar to [can.Model]. The following will work: - - - new Map({time: new Date()}) - .serialize() //-> { time: 1319666613663 } - - -@return {Object} a JavaScript Object that can be -serialized with `JSON.stringify` or other methods. \ No newline at end of file diff --git a/map/doc/prototype.unbind.md b/map/doc/prototype.unbind.md deleted file mode 100644 index 6d211f9955e..00000000000 --- a/map/doc/prototype.unbind.md +++ /dev/null @@ -1,31 +0,0 @@ -@function can.Map.prototype.unbind unbind -@parent can.Map.prototype 8 - -@description Unbind event handlers from a Map. - -@signature `map.unbind(eventType[, handler])` -@param {String} eventType the type of event to unbind, exactly as passed to `bind` -@param {Function} [handler] the handler to unbind - -@body -`unbind` unbinds event handlers previously bound with [can.Map.prototype.bind bind]. -If no _handler_ is passed, all handlers for the given event type will be unbound. - - - var i = 0, - increaseBy2 = function() { i += 2; }, - increaseBy3 = function() { i += 3; }, - o = new can.Map(); - - o.bind('change', increaseBy2); - o.bind('change', increaseBy3); - o.attr('a', 'Alice'); - i; // 5 - - o.unbind('change', increaseBy2); - o.attr('b', 'Bob'); - i; // 8 - - o.unbind('change'); - o.attr('e', 'Eve'); - i; // 8 diff --git a/map/doc/static.keys.md b/map/doc/static.keys.md deleted file mode 100644 index 90e75cda44e..00000000000 --- a/map/doc/static.keys.md +++ /dev/null @@ -1,20 +0,0 @@ -@function can.Map.keys keys -@parent can.Map.static 0 - -@description Returns an array of the map's keys. - -@signature `can.Map.keys(map)` -@param {can.Map} map the `can.Map` to get the keys from -@return {Array} array An array containing the keys from _map_. - -@body -`keys` iterates over a map to get an array of its keys. - - - var people = new can.Map({ - a: 'Alice', - b: 'Bob', - e: 'Eve' - }); - - can.Map.keys(people); // ['a', 'b', 'e'] \ No newline at end of file diff --git a/map/map.html b/map/map.html deleted file mode 100644 index b2365e0c92b..00000000000 --- a/map/map.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - Memory Leak Test - - - - -

                -
                - - - - - - - diff --git a/map/map.js b/map/map.js index 428d3dda475..28f5497f2e5 100644 --- a/map/map.js +++ b/map/map.js @@ -1,658 +1,3 @@ -/* jshint -W079 */ -// # can/map/map.js (aka can.Map) -// `can.Map` provides the observable pattern for JavaScript objects. It -// provides an `attr` and `removeAttr` method that can be used to get/set and -// remove properties and nested properties by calling a "pipeline" of protected -// methods: -// -// - `_get`, `_set`, `_remove` - handle nested properties. -// - `__get`, `__set`, `__remove` - handle triggering events. -// - `___get`, `___set`, `___remove` - read / write / remove raw values. -// -// When `attr` gets or sets multiple properties it calls `_getAttrs` or `_setAttrs`. -// -// [bubble.js](bubble.html) - Handles bubbling of child events to parent events. -// [map_helpers.js](map_helpers.html) - Assorted helpers for handling cycles during serialization or -// instantition of objects. -var can = require('can/util/util'); -var bubble = require('./bubble'); -var mapHelpers = require('./map_helpers'); -require('can/util/bind/bind'); -require('can/construct/construct'); -require('can/util/batch/batch'); -require('can/compute/get_value_and_bind'); +var can = require('../util/can'); -// properties that can't be observed on ... no matter what -var unobservable = { - "constructor": true -}; - -// Extend [can.Construct](../construct/construct.html) to make inheriting a `can.Map` easier. -var Map = can.Map = can.Construct.extend( - /** - * @static - */ - // ## Static Properties and Methods - { - // ### setup - // Called when a Map constructor is defined/extended to - // perform any initialization behavior for the new constructor - // function. - setup: function () { - - can.Construct.setup.apply(this, arguments); - - // A cached list of computed properties on the prototype. - this._computedPropertyNames = []; - - // Do not run if we are defining can.Map. - if (can.Map) { - - // Provide warnings if can.Map is used incorrectly. - //!steal-remove-start - if(this.prototype.define && !mapHelpers.define) { - can.dev.warn("can/map/define is not included, yet there is a define property "+ - "used. You may want to add this plugin."); - } - if(this.define && !mapHelpers.define) { - can.dev.warn("The define property should be on the map's prototype properties, "+ - "not the static properties. Also, can/map/define is not included."); - } - //!steal-remove-end - - // Create a placeholder for default values. - if (!this.defaults) { - this.defaults = {}; - } - - - // Go through everything on the prototype. If it's a primitive, - // treat it as a default value. If it's a compute, identify it so - // it can be setup as a computed property. - for (var prop in this.prototype) { - if ( - prop !== "define" && - prop !== "constructor" && - ( - typeof this.prototype[prop] !== "function" || - this.prototype[prop].prototype instanceof can.Construct - ) - ) { - this.defaults[prop] = this.prototype[prop]; - } else if (this.prototype[prop].isComputed) { - this._computedPropertyNames.push(prop); - } - } - - // If define is a function, call it with this can.Map - if(mapHelpers.define) { - mapHelpers.define(this); - } - } - - // If we inherit from can.Map, but not can.List, create a can.List that - // creates instances of this Map type. - if (can.List && !(this.prototype instanceof can.List)) { - this.List = Map.List.extend({ - Map: this - }, {}); - } - - }, - // ### shortName - // Tells `can.Construct` to show instance as `Map` in the debugger. - shortName: "Map", - - // ### _bubbleRule - // Returns which events to setup bubbling on for a given bound event. - // By default, only bubbles "change" events if someone listens to a - // "change" event or a nested event like "foo.bar". - _bubbleRule: function(eventName) { - return (eventName === "change" || eventName.indexOf(".") >= 0 ) ? - ["change"] : - []; - }, - - // ### bind, unbind - // Listen to events on the Map constructor. These - // are here mostly for can.Model. - bind: can.bindAndSetup, - unbind: can.unbindAndTeardown, - - // ### id - // Name of the id field. Used in can.Model. - id: "id", - - // ### keys - // An observable way to get the keys from a map. - keys: function (map) { - var keys = []; - can.__observe(map, '__keys'); - for (var keyName in map._data) { - keys.push(keyName); - } - return keys; - } - }, - /** - * @prototype - */ - // ## Prototype Properties and Methods - { - // ### setup - // Initializes the map instance's behavior. - setup: function (obj) { - - if(obj instanceof can.Map){ - obj = obj.serialize(); - } - - // Where we keep the values of the compute. - this._data = {}; - - // The namespace this `object` uses to listen to events. - can.cid(this, ".map"); - - this._setupComputedProperties(); - - var teardownMapping = obj && mapHelpers.addToMap(obj, this); - - var defaultValues = this._setupDefaults(obj); - var data = can.extend(can.extend(true, {}, defaultValues), obj); - - this.attr(data); - - if (teardownMapping) { - teardownMapping(); - } - }, - - // ### _setupComputes - // Sets up computed properties on a Map. - // Stores information for each computed property on - // `this._computedAttrs` that looks like: - // - // ``` - // { - // // the number of bindings on this property - // count: 1, - // // a handler that forwards events on the compute - // // to the map instance - // handler: handler, - // compute: compute // the compute - // } - // ``` - _setupComputedProperties: function () { - this._computedAttrs = {}; - - var computes = this.constructor._computedPropertyNames; - - for (var i = 0, len = computes.length; i < len; i++) { - var attrName = computes[i]; - mapHelpers.addComputedAttr(this, attrName, this[attrName].clone(this)); - } - }, - - // ### _setupDefaults - // Returns the default values for the instance. - _setupDefaults: function(){ - return this.constructor.defaults || {}; - }, - - // ### attr - // The primary get/set interface for can.Map. - // Calls `_get`, `_set` or `_attrs` depending on - // how it is called. - attr: function (attr, val) { - var type = typeof attr; - if(attr === undefined) { - return this._getAttrs(); - } else if (type !== "string" && type !== "number") { - // Get or set multiple attributes. - return this._setAttrs(attr, val); - } - else if (arguments.length === 1) { - // Get a single attribute. - return this._get(attr+""); - } else { - // Set an attribute. - this._set(attr+"", val); - return this; - } - }, - - // ### _get - // Handles reading nested properties like "foo.bar" by - // getting the value of "foo" and recursively - // calling `_get` for the value of "bar". - // To read the actual values, `_get` calls - // `___get`. - _get: function (attr) { - var dotIndex = attr.indexOf('.'); - - if( dotIndex >= 0 ) { - - // Attempt to get the value anyway in case - // somone wrote `new can.Map({"foo.bar": 1})`. - var value = this.___get(attr); - if (value !== undefined) { - can.__observe(this, attr); - return value; - } - - var first = attr.substr(0, dotIndex), - second = attr.substr(dotIndex+1); - - var current = this.__get( first ); - - return current && current._get ? current._get(second) : undefined; - } else { - return this.__get( attr ); - } - }, - - // ### __get - // Signals `can.compute` that an observable - // property is being read. - __get: function(attr){ - if(!unobservable[attr] && !this._computedAttrs[attr]) { - can.__observe(this, attr); - } - return this.___get( attr ); - }, - - // ### ___get - // When called with an argument, returns the value of this property. If that - // property is represented by a computed attribute, return the value of that compute. - // If no argument is provided, return the raw data. - ___get: function (attr) { - if (attr !== undefined) { - var computedAttr = this._computedAttrs[attr]; - if (computedAttr && computedAttr.compute) { - // return computedAttr.compute(); - return computedAttr.compute(); - } else { - return this._data.hasOwnProperty(attr) ? this._data[attr] : undefined; - } - } else { - return this._data; - } - }, - - // ### _set - // Handles setting nested properties by finding the - // nested observable and recursively calling `_set` on it. Eventually, - // it calls `__set` with the `__type` converted value to set - // and the current value. The current value is passed for two reasons: - // - so `__set` can trigger an event if the value has changed. - // - for advanced setting behavior that define.set can do. - // - // If the map is initializing, the current value does not need to be - // read because no change events are dispatched anyway. - _set: function (attr, value, keepKey) { - - var dotIndex = attr.indexOf('.'), - current; - - if(dotIndex >= 0 && !keepKey){ - var first = attr.substr(0, dotIndex), - second = attr.substr(dotIndex+1); - - current = this.__inSetup ? undefined : this.___get( first ); - - if( can.isMapLike(current) ) { - current._set(second, value); - } else { - throw new Error("can.Map: Object does not exist"); - } - - } else { - current = this.__inSetup ? undefined : this.___get( attr ); - - // //Convert if there is a converter. Remove in 3.0. - if (this.__convert) { - value = this.__convert(attr, value); - } - - this.__set(attr, this.__type(value, attr), current); - } - }, - - // ## __type - // Converts set values to another type. By default, - // this converts Objects to can.Maps and Arrays to - // can.Lists. - // This also makes it so if a plain JavaScript object - // has already been converted to a list or map, that same - // list or map instance is used. - __type: function(value, prop){ - - if (typeof value === "object" && !( value instanceof can.Map) && mapHelpers.canMakeObserve(value) ) { - - var cached = mapHelpers.getMapFromObject(value); - if(cached) { - return cached; - } - if( can.isArray(value) ) { - var List = can.List; - return new List(value); - } else { - var Map = this.constructor.Map || can.Map; - return new Map(value); - } - } - return value; - }, - - // ## __set - // Handles firing events if the value has changed and - // works with the `bubble` helpers to setup bubbling. - // Calls `___set` to do the actual setting. - __set: function (prop, value, current) { - - if (value !== current) { - var computedAttr = this._computedAttrs[prop]; - - // Dispatch an "add" event if adding a new property. - var changeType = computedAttr || current !== undefined || this.___get() - .hasOwnProperty(prop) ? "set" : "add"; - - // Set the value on `_data` and set up bubbling. - this.___set(prop, typeof value === "object" ? bubble.set(this, prop, value, current) : value ); - - // Computed properties change events are already forwarded except if - // no one is listening to them. - if(!computedAttr || !computedAttr.count) { - this._triggerChange(prop, changeType, value, current); - } - - - // Stop bubbling old nested maps. - if (typeof current === "object") { - bubble.teardownFromParent(this, current); - } - } - }, - - // ### ___set - // Directly saves the set value as a property on `_data` - // or sets the computed attribute. - ___set: function (prop, val) { - var computedAttr = this._computedAttrs[prop]; - if ( computedAttr ) { - computedAttr.compute(val); - } else { - this._data[prop] = val; - } - - // Adds the property directly to the map instance. But first, - // checks that it's not overwriting a method. This should be removed - // in 3.0. - if ( typeof this.constructor.prototype[prop] !== 'function' && !computedAttr ) { - this[prop] = val; - } - }, - - removeAttr: function (attr) { - return this._remove(attr); - }, - - // ### _remove - // Handles removing nested observes. - _remove: function(attr){ - // If this is List. - var parts = mapHelpers.attrParts(attr), - // The actual property to remove. - prop = parts.shift(), - // The current value. - current = this.___get(prop); - - // If we have more parts, call `removeAttr` on that part. - if (parts.length && current) { - return current.removeAttr(parts); - } else { - - // If attr does not have a `.` - if (typeof attr === 'string' && !!~attr.indexOf('.')) { - prop = attr; - } - - this.__remove(prop, current); - return current; - } - }, - - // ### __remove - // Handles triggering an event if a property could be removed. - __remove: function(prop, current){ - if (prop in this._data) { - this.___remove(prop); - // Let others now this property has been removed. - this._triggerChange(prop, "remove", undefined, current); - } - }, - - // ### ___remove - // Deletes a property from `_data` and the map instance. - ___remove: function(prop){ - delete this._data[prop]; - if (!(prop in this.constructor.prototype)) { - delete this[prop]; - } - }, - - // ### ___serialize - // Serializes a property. Uses map helpers to - // recursively serialize nested observables. - ___serialize: function(name, val){ - return mapHelpers.getValue(this, name, val, "serialize"); - }, - - // ### _getAttrs - // Returns the values of all attributes as a plain JavaScript object. - _getAttrs: function(){ - return mapHelpers.serialize(this, 'attr', {}); - }, - // ### _setAttrs - // Sets multiple properties on this object at once. - // First, goes through all current properties and either merges - // or removes old properties. - // Then it goes through the remaining ones to be added and sets those properties. - _setAttrs: function (props, remove) { - props = can.simpleExtend({}, props); - var prop, - self = this, - newVal; - - // Batch all of the change events until we are done. - can.batch.start(); - // Merge current properties with the new ones. - this._each(function (curVal, prop) { - // You can not have a _cid property; abort. - if (prop === "_cid") { - return; - } - newVal = props[prop]; - - // If we are merging, remove the property if it has no value. - if (newVal === undefined) { - if (remove) { - self.removeAttr(prop); - } - return; - } - - // Run converter if there is one. Remove in 3.0. - if (self.__convert) { - newVal = self.__convert( prop, newVal ); - } - - if ( can.isMapLike(curVal) && mapHelpers.canMakeObserve(newVal) ) { - curVal.attr(newVal, remove); - // Otherwise just set. - } else if (curVal !== newVal) { - self.__set(prop, self.__type(newVal, prop), curVal); - } - - delete props[prop]; - }); - // Add remaining props. - for (prop in props) { - // Ignore _cid. - if (prop !== "_cid") { - newVal = props[prop]; - this._set(prop, newVal, true); - } - - } - can.batch.stop(); - return this; - }, - - serialize: function () { - return mapHelpers.serialize(this, 'serialize', {}); - }, - - - // ### _triggerChange - // A helper function used to trigger events on this map. - // If the map is bubbling, this will fire a change event. - // Otherwise, it only fires a "named" event. Triggers a - // "__keys" event if a property has been added or removed. - _triggerChange: function (attr, how, newVal, oldVal, batchNum) { - - if(bubble.isBubbling(this, "change")) { - can.batch.trigger(this, { - type: "change", - target: this, - batchNum: batchNum - }, [attr, how, newVal, oldVal]); - - } - - can.batch.trigger(this, { - type: attr, - target: this, - batchNum: batchNum - }, [newVal, oldVal]); - - if(how === "remove" || how === "add") { - can.batch.trigger(this, { - type: "__keys", - target: this, - batchNum: batchNum - }); - } - }, - - // ### _bindsetup and _bindteardown - // Placeholders for bind setup and teardown. - _bindsetup: function(){}, - _bindteardown: function(){}, - - // ### one - // Listens once to an event. - one: can.one, - - // ### bind - // Listens to an event on a map. - // If the event is a computed property, - // listen to the compute and forward its events - // to this map. - bind: function (eventName, handler) { - - var computedBinding = this._computedAttrs && this._computedAttrs[eventName]; - if (computedBinding && computedBinding.compute) { - if (!computedBinding.count) { - computedBinding.count = 1; - computedBinding.compute.bind("change", computedBinding.handler); - } else { - computedBinding.count++; - } - - } - - // Sets up bubbling if needed. - bubble.bind(this, eventName); - - return can.bindAndSetup.apply(this, arguments); - }, - - // ### unbind - // Stops listening to an event. - // If this is the last listener of a computed property, - // stop forwarding events of the computed property to this map. - unbind: function (eventName, handler) { - var computedBinding = this._computedAttrs && this._computedAttrs[eventName]; - if (computedBinding) { - if (computedBinding.count === 1) { - computedBinding.count = 0; - computedBinding.compute.unbind("change", computedBinding.handler); - } else { - computedBinding.count--; - } - - } - - // Teardown bubbling if needed. - bubble.unbind(this, eventName); - return can.unbindAndTeardown.apply(this, arguments); - - }, - - // ### compute - // Creates a compute that represents a value on this map. If the property is a function - // on the prototype, a "function" compute wil be created. - // Otherwise, a compute will be created that reads the observable attributes. - compute: function (prop) { - - if (can.isFunction(this.constructor.prototype[prop])) { - - return can.compute(this[prop], this); - } else { - - var reads = can.compute.read.reads(prop), - last = reads.length - 1; - - return can.compute(function (newVal) { - if (arguments.length) { - can.compute.read(this, reads.slice(0, last)) - .value.attr(reads[last].key, newVal); - } else { - return can.compute.read(this, reads, { - args: [] - }).value; - } - }, this); - } - - }, - - // ### each - // loops through all the key-value pairs on this map. - each: function () { - return can.each.apply(undefined, [this].concat(can.makeArray(arguments))); - }, - - // ### _each - // Iterator that does not trigger live binding. - _each: function (callback) { - var data = this.___get(); - for (var prop in data) { - if (data.hasOwnProperty(prop)) { - callback(data[prop], prop); - } - } - }, - - dispatch: can.dispatch - }); - -// ### etc -// Setup on/off aliases -Map.prototype.on = Map.prototype.bind; -Map.prototype.off = Map.prototype.unbind; -Map.on = Map.bind; -Map.off = Map.unbind; - -module.exports = exports = Map; +module.exports = can.Map = require('can-map'); diff --git a/map/map_benchmark.js b/map/map_benchmark.js deleted file mode 100644 index 27d3ce9570f..00000000000 --- a/map/map_benchmark.js +++ /dev/null @@ -1,41 +0,0 @@ -var can = require('can/util/util'); -require('can/map/map'); -require('can/list/list'); -var benchmarks = require('can/test/benchmarks'); - -var objects, map; -benchmarks.add('Adding a big array to an object', function () { - objects = []; - for (var i = 0; i < 10; i++) { - objects.push({ - prop: 'prop', - nest: { - prop: 'prop', - nest: { - prop: 'prop' - } - } - }); - } -}, function () { - map = new can.Map(); - map.attr('obj', objects); -}); - -var NumbersMap; -benchmarks.add('Overwriting defaults', function () { - NumbersMap = can.Map.extend({ - numbers: [1, 2, 3, 4, 5, 6], - foo: 'string', - bar: {}, - zed: false - }); -}, function () { - new NumbersMap(); - new NumbersMap({ - numbers: ['a', 'b', 'c', 'd'] - }); - new NumbersMap({ - foo: 'blah blah blah' - }); -}); diff --git a/map/map_helpers.js b/map/map_helpers.js deleted file mode 100644 index 5a45cd6c66a..00000000000 --- a/map/map_helpers.js +++ /dev/null @@ -1,194 +0,0 @@ -// # can/map/map_hepers -// Helper functions that are primarily used to serialize -// a map, or track the maps created from plain JavaScript objects. -// `can.Map` handles cycles in objects nicely! - -var can = require('can/util/util'); -require('can/util/object/isplain/isplain'); - -var mapHelpers = { - // ### mapHelpers.attrParts - // Parses attribute name into its parts. - attrParts: function (attr, keepKey) { - //Keep key intact - if (keepKey ) { - return [attr]; - } - // Split key on '.' - return typeof attr === "object" ? attr : ("" + attr) - .split("."); - }, - - // ### can.mapHelpers.canMakeObserve - // Determines if an object can be made into an observable. - canMakeObserve: function (obj) { - return obj && !can.isDeferred(obj) && (can.isArray(obj) || can.isPlainObject(obj) ); - }, - - // ### mapHelpers.serialize - // Serializes a Map or Map.List by recursively calling the `how` - // method on any child objects. This is able to handle - // cycles. - // `map` - the map or list to serialize. - // `how` - the method to call recursively. - // `where` - the target Object or Array that becomes the serialized result. - serialize: (function(){ - - // A temporary mapping of map cids to the serialized result. - var serializeMap = null; - - return function (map, how, where) { - var cid = can.cid(map), - firstSerialize = false; - - // If there isn't an existing serializeMap, this means - // this is the initial non-recursive call to this function. - // We mark this as the first call, and then setup the serializeMap. - // The serialize map is further devided into `how` because - // `.serialize` might call `.attr`. - if(!serializeMap) { - firstSerialize = true; - serializeMap = { - attr: {}, - serialize: {} - }; - } - - serializeMap[how][cid] = where; - // Go through each property. - map.each(function (val, name) { - // If the value is an `object`, and has an `attr` or `serialize` function. - var result, - isObservable = can.isMapLike(val), - serialized = isObservable && serializeMap[how][can.cid(val)]; - - if( serialized ) { - result = serialized; - } else { - // special attr or serializer - if(map["___"+how]) { - result = map["___"+how](name, val); - } else { - result = mapHelpers.getValue(map, name, val, how); - } - } - // this is probably removable - if(result !== undefined){ - where[name] = result; - } - }); - - if(firstSerialize) { - serializeMap = null; - } - return where; - }; - })(), - - // ## getValue - // If `val` is an observable, calls `how` on it; otherwise - // returns the value of `val`. - getValue: function(map, name, val, how){ - if( can.isMapLike(val) ) { - return val[how](); - } else { - return val; - } - }, - - // ## define - // A hook to call whenever a Map is defined. - // We need a better place for this. - define: null, - - // ## addComputedAttr - // Adds a compute so it will control the behavior of an - // attribute. Each computedAttrs object has: - // - `compute` - the compute that will be read and updated. - // - `count` - the number of bindings to this individual property. - // This is used to know when to bind `handler` to the compute. - // - `handler` - a function that when bound to `compute` forwards all - // events to `map`. - addComputedAttr: function(map, attrName, compute){ - map._computedAttrs[attrName] = { - compute: compute, - count: 0, - handler: function (ev, newVal, oldVal) { - map._triggerChange(attrName, "set", newVal, oldVal, ev.batchNum); - } - }; - }, - - // ### can.mapHelpers.addToMap - // Tracks map instances created from JS objects. - // This should be called whenever an instance is created for a particular object. - // This may return a `teardown` function that should be called after all instances - // might be created. - // - // While creating map instances from plain ole JS objects (POJOs), it's - // possible that the same JS object exists as two different properties and - // we want only one map instance created for one JS object. - // - // ``` - // var obj = {name: "I am everywhere"} - // var map = new can.Map({obj1: obj, obj2: obj}); - // ok( map.attr("obj1") === map.attr("obj2") ) - // ``` - // - // This works by temporarily adding a `cid` to any found POJO object - // and storing it in a temporary Object that maps those `cid`s to - // the POJO and the instance created for it. - // The `teardown` function removes those temporary `cid`s and - // clears the map for memory safety. - addToMap: function addToMap(obj, instance) { - var teardown; - - // Setup a fresh mapping if `madeMap` is missing. - if (!madeMap) { - teardown = teardownMap; - madeMap = {}; - } - - // Record if Object has a `_cid` before adding one. - var hasCid = obj._cid; - var cid = can.cid(obj); - - // Only update if there already isn't one already. - if (!madeMap[cid]) { - - madeMap[cid] = { - obj: obj, - instance: instance, - added: !hasCid - }; - } - return teardown; - }, - - // ### getMapFromObject - // Returns the map instance already created for this object `obj` or - // `undefined` if nothing has been already created. - getMapFromObject: function (obj) { - return madeMap && madeMap[obj._cid] && madeMap[obj._cid].instance; - } -}; - -// ## POJOs to Map instance helpers - -// ### madeMap -// A temporary map of Maps that have been made from plain JS objects. -// `{POJO_CID: {obj: POJO, instance: MAP, added: Boolean}}` -var madeMap = null; - -// ### teardownMap -// Clears out map of converted objects and removes temporary `cids`. -var teardownMap = function () { - for (var cid in madeMap) { - if (madeMap[cid].added) { - delete madeMap[cid].obj._cid; - } - } - madeMap = null; -}; - -module.exports = exports = mapHelpers; diff --git a/map/map_test.js b/map/map_test.js index 01d2c32bcc5..8320d78d1e7 100644 --- a/map/map_test.js +++ b/map/map_test.js @@ -1,369 +1 @@ -/* jshint asi:true*/ -require('can/map/map'); -require('can/compute/compute'); -require('can/list/list'); -require('steal-qunit'); - -QUnit.module('can/map'); - -test("Basic Map", 4, function () { - - var state = new can.Map({ - category: 5, - productType: 4 - }); - - state.bind("change", function (ev, attr, how, val, old) { - equal(attr, "category", "correct change name"); - equal(how, "set"); - equal(val, 6, "correct"); - equal(old, 5, "correct"); - }); - - state.attr("category", 6); - - state.unbind("change"); - -}); - -test("Nested Map", 5, function () { - var me = new can.Map({ - name: { - first: "Justin", - last: "Meyer" - } - }); - - ok(me.attr("name") instanceof can.Map); - - me.bind("change", function (ev, attr, how, val, old) { - equal(attr, "name.first", "correct change name") - equal(how, "set") - equal(val, "Brian", "correct") - equal(old, "Justin", "correct") - }) - - me.attr("name.first", "Brian"); - - me.unbind("change") - -}) - -test("remove attr", function () { - var state = new can.Map({ - category: 5, - productType: 4 - }); - state.removeAttr("category"); - deepEqual(can.Map.keys(state), ["productType"], "one property"); -}); - -test("remove attr on key with dot", function () { - var state = new can.Map({ - "key.with.dots": 12, - productType: 4 - }); - var state2 = new can.Map({ - "key.with.dots": 4, - key: { - "with": { - someValue: 20 - } - } - }); - state.removeAttr("key.with.dots"); - state2.removeAttr("key.with.someValue"); - deepEqual( can.Map.keys(state), ["productType"], "one property"); - deepEqual( can.Map.keys(state2), ["key.with.dots", "key"], "two properties"); - deepEqual( can.Map.keys( state2.key["with"] ) , [], "zero properties"); -}); - -test("nested event handlers are not run by changing the parent property (#280)", function () { - - var person = new can.Map({ - name: { - first: "Justin" - } - }) - person.bind("name.first", function (ev, newName) { - ok(false, "name.first should never be called") - //equal(newName, "hank", "name.first handler called back with correct new name") - }); - person.bind("name", function () { - ok(true, "name event triggered") - }) - - person.attr("name", { - first: "Hank" - }); - -}); - -test("cyclical objects (#521)", function () { - - var foo = {}; - foo.foo = foo; - - var fooed = new can.Map(foo); - - ok(true, "did not cause infinate recursion"); - - ok(fooed.attr('foo') === fooed, "map points to itself") - - var me = { - name: "Justin" - } - var references = { - husband: me, - friend: me - } - var ref = new can.Map(references) - - ok(ref.attr('husband') === ref.attr('friend'), "multiple properties point to the same thing") - -}) - -test('Getting attribute that is a can.compute should return the compute and not the value of the compute (#530)', function () { - var compute = can.compute('before'); - var map = new can.Map({ - time: compute - }); - - equal(map.time, compute, 'dot notation call of time is compute'); - equal(map.attr('time'), compute, '.attr() call of time is compute'); -}) - -test('_cid add to original object', function () { - var map = new can.Map(), - obj = { - 'name': 'thecountofzero' - }; - - map.attr('myObj', obj); - ok(!obj._cid, '_cid not added to original object'); -}) - -test("can.each used with maps", function () { - can.each(new can.Map({ - foo: "bar" - }), function (val, attr) { - - if (attr === "foo") { - equal(val, "bar") - } else { - ok(false, "no properties other should be called " + attr) - } - - }) -}) - -test("can.Map serialize triggers reading (#626)", function () { - var old = can.__observe; - - var attributesRead = []; - var readingTriggeredForKeys = false; - - can.__observe = function (object, attribute) { - if (attribute === "__keys") { - readingTriggeredForKeys = true; - } else { - attributesRead.push(attribute); - } - }; - - var testMap = new can.Map({ - cats: "meow", - dogs: "bark" - }); - - testMap.serialize(); - - - - ok(can.inArray("cats", attributesRead ) !== -1 && can.inArray( "dogs", attributesRead ) !== -1, "map serialization triggered __reading on all attributes"); - ok(readingTriggeredForKeys, "map serialization triggered __reading for __keys"); - - can.__observe = old; -}) - -test("Test top level attributes", 7, function () { - var test = new can.Map({ - 'my.enable': false, - 'my.item': true, - 'my.count': 0, - 'my.newCount': 1, - 'my': { - 'value': true, - 'nested': { - 'value': 100 - } - } - }); - - equal(test.attr('my.value'), true, 'correct'); - equal(test.attr('my.nested.value'), 100, 'correct'); - ok(test.attr("my.nested") instanceof can.Map); - - equal(test.attr('my.enable'), false, 'falsey (false) value accessed correctly'); - equal(test.attr('my.item'), true, 'truthey (true) value accessed correctly'); - equal(test.attr('my.count'), 0, 'falsey (0) value accessed correctly'); - equal(test.attr('my.newCount'), 1, 'falsey (1) value accessed correctly'); -}); - -test("computed properties don't cause memory leaks", function () { - var computeMap = can.Map.extend({ - 'name': can.compute(function(){ - return this.attr('first') + this.attr('last') - }) - }), - handler = function(){}, - map = new computeMap({ - first: 'Mickey', - last: 'Mouse' - }); - map.bind('name', handler); - map.bind('name', handler); - equal(map._computedAttrs.name.count, 2, '2 handlers listening to computed property'); - map.unbind('name', handler); - map.unbind('name', handler); - equal(map._computedAttrs.name.count, 0, '0 handlers listening to computed property'); - -}); - -test("serializing cycles", function(){ - var map1 = new can.Map({name: "map1"}); - var map2 = new can.Map({name: "map2"}); - - map1.attr("map2", map2); - map2.attr("map1", map1); - - var res = map1.serialize(); - equal(res.name, "map1"); - equal(res.map2.name, "map2"); -}); - -test("Unbinding from a map with no bindings doesn't throw an error (#1015)", function() { - expect(0); - - var test = new can.Map({}); - - try { - test.unbind('change'); - } catch(e) { - ok(false, 'No error should be thrown'); - } -}); - -test("Fast dispatch event still has target and type (#1082)", 4, function() { - var data = new can.Map({ - name: 'CanJS' - }); - - data.bind('change', function(ev){ - equal(ev.type, 'change'); - equal(ev.target, data); - }); - - data.bind('name', function(ev){ - equal(ev.type, 'name'); - equal(ev.target, data); - }); - - data.attr('name', 'David'); -}); - -test("map passed to Map constructor (#1166)", function(){ - var map = new can.Map({x: 1}); - var res = new can.Map(map); - deepEqual(res.attr(), { - x: 1 - }, "has the same properties"); -}); - -test("constructor passed to scope is threated as a property (#1261)", function(){ - var Constructor = can.Construct.extend({}); - - var Map = can.Map.extend({ - Todo: Constructor - }); - - var m = new Map(); - - equal(m.attr("Todo"), Constructor); -}); - -test('_bindings count maintained after calling .off() on undefined property (#1490) ', function () { - - var map = new can.Map({ - test: 1 - }); - - map.on('test', can.noop); - - equal(map._bindings, 1, 'The number of bindings is correct'); - - map.off('undefined_property'); - - equal(map._bindings, 1, 'The number of bindings is still correct'); -}); - -test("Should be able to get and set attribute named 'watch' on can.Map in Firefox", function() { - var map = new can.Map({}); - map.attr("watch"); - ok(true, "can have attribute named 'watch' on a can.Map instance"); -}); - -test("Should be able to get and set attribute named 'unwatch' on can.Map in Firefox", function() { - var map = new can.Map({}); - map.attr("unwatch"); - ok(true, "can have attribute named 'unwatch' on a can.Map instance"); -}); - -test('Creating map in compute dispatches all events properly', function() { - expect(2); - - var source = can.compute(0); - - var c = can.compute(function() { - var map = new can.Map(); - source(); - map.bind("foo", function(){ - ok(true); - }); - map.attr({foo: "bar"}); //DISPATCH - - return map; - }); - - c.bind("change",function(){}); - - can.batch.start(); - source(1); - can.batch.stop(); -}); - -test('should get an empty string property value correctly', function() { - var map = new can.Map({ - foo: 'foo', - '': 'empty string' - }); - - equal(map.attr(''), 'empty string'); -}); - - -test("can.Map::attr setting is observable", function() { - expect(0); - var c = can.compute(function() { - return new can.Map(); - }); - - c.bind('change', function() { - ok(false, "the compute should not be updated"); - }); - - var map = c(); - - // recomputes c - map.attr('foo', 'bar'); -}); +require('can-map/can-map_test'); diff --git a/map/test.html b/map/test.html deleted file mode 100644 index 68c4eb43c95..00000000000 --- a/map/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/map - -
                diff --git a/model/doc/bind.md b/model/doc/bind.md deleted file mode 100644 index 9450837cc53..00000000000 --- a/model/doc/bind.md +++ /dev/null @@ -1,25 +0,0 @@ -@function can.Model.bind bind -@parent can.Model.static -@description Listen for events on a Model class. - -@signature `can.Model.bind(eventType, handler)` -@param {String} eventType The type of event. It must be -`"created"`, `"updated"`, `"destroyed"`. -@param {function} handler A callback function -that gets called with the event and instance that was -created, destroyed, or updated. -@return {can.Model} The model constructor function. - -@body -`bind(eventType, handler(event, instance))` listens to -__created__, __updated__, __destroyed__ events on all -instances of the model. - -``` -Task.bind("created", function(ev, createdTask){ -this //-> Task - createdTask.attr("name") //-> "Dishes" -}) - -new Task({name: "Dishes"}).save(); -``` diff --git a/model/doc/create.md b/model/doc/create.md deleted file mode 100644 index 0963c39b0a4..00000000000 --- a/model/doc/create.md +++ /dev/null @@ -1,91 +0,0 @@ -@description Specifies how to create a new resource on the server. `create(serialized)` is called -by [can.Model.prototype.save save] if the model instance [can.Model.prototype.isNew is new]. -@function can.Model.create create -@parent can.Model.static - - -@signature `can.Model.create: function(serialized) -> deferred` - -Specify a function to create persistent instances. The function will -typically perform an AJAX request to a service that results in -creating a record in a database. - -@param {Object} serialized The [can.Map::serialize serialized] properties of -the model to create. -@return {can.Deferred} A Deferred that resolves to an object of attributes -that will be added to the created model instance. The object __MUST__ contain -an [can.Model.id id] property so that future calls to [can.Model.prototype.save save] -will call [can.Model.update]. - - -@signature `can.Model.create: "[METHOD] /path/to/resource"` - -Specify a HTTP method and url to create persistent instances. - -If you provide a URL, the Model will send a request to that URL using -the method specified (or POST if none is specified) when saving a -new instance on the server. (See below for more details.) - -@param {HttpMethod} METHOD An HTTP method. Defaults to `"POST"`. -@param {STRING} url The URL of the service to retrieve JSON data. - - -@signature `can.Model.create: {ajaxSettings}` - -Specify an options object that is used to make a HTTP request to create -persistent instances. - -@param {can.AjaxSettings} ajaxSettings A settings object that -specifies the options available to pass to [can.ajax]. - -@body - -`create(attributes) -> Deferred` is used by [can.Model::save save] to create a -model instance on the server. - -## Implement with a URL - -The easiest way to implement create is to give it the url -to post data to: - -``` -var Recipe = can.Model.extend({ - create: "/recipes" -},{}) -``` - -This lets you create a recipe like: - -``` -new Recipe({name: "hot dog"}).save(); -``` - - -## Implement with a Function - -You can also implement create by yourself. Create gets called -with `attrs`, which are the [can.Map::serialize serialized] model -attributes. Create returns a `Deferred` -that contains the id of the new instance and any other -properties that should be set on the instance. - -For example, the following code makes a request -to `POST /recipes.json {'name': 'hot+dog'}` and gets back -something that looks like: - -``` -{ - "id": 5, - "createdAt": 2234234329 -} -``` - -The code looks like: - -``` -can.Model.extend("Recipe", { - create : function( attrs ){ - return $.post("/recipes.json",attrs, undefined ,"json"); - } -},{}) -``` diff --git a/model/doc/deprecated_model.md b/model/doc/deprecated_model.md deleted file mode 100644 index 409b7ab63b4..00000000000 --- a/model/doc/deprecated_model.md +++ /dev/null @@ -1,41 +0,0 @@ -@function can.Model.model model -@parent can.Model.static - -@deprecated {2.1} Prior to 2.1, `.model` was used to convert ajax -responses into a data format useful for converting them into a can.Model instance -AND for converting them into that instance. In 2.1, [can.Model.parseModel] should -be used to convert the ajax response into a data format useful to [can.Model.model]. - -@description Convert raw data into a can.Model instance. If data's [can.Model.id id] -matches a item in the store's `id`, `data` is merged with the instance and the -instance is returned. - - -@signature `can.Model.model(data)` -@param {Object} data The data to convert to a can.Model instance. -@return {can.Model} An instance of can.Model made with the given data. - - -@body - -## Use - -`.models(data)` is used to create or retrieve a [can.Model] instance -with the data provided. If data matches an instance in the [can.Model.store], -that instance will be merged with the item's data and returneds - -For example - -``` -Task = can.Model.extend({},{}) - -var t1 = new Task({id: 1, name: "dishes"}) - -// Binding on a model puts it in the store -t1.bind("change", function(){}) - -var task = Task.model({id: 1, name : "dishes", complete : false}) - -t1 === task //-> true -t1.attr("complete") //-> false -``` diff --git a/model/doc/deprecated_models.md b/model/doc/deprecated_models.md deleted file mode 100644 index 0422acb2150..00000000000 --- a/model/doc/deprecated_models.md +++ /dev/null @@ -1,44 +0,0 @@ -@function can.Model.models models -@parent can.Model.static - -@deprecated {2.1} Prior to 2.1, `.models` was used to convert the ajax -responses into a data format useful for converting them into an observable -list AND for converting them into that list. In 2.1, [can.Model.parseModels] should -be used to convert the ajax responses into a data format useful to [can.Model.models]. - -@description Convert raw data into can.Model instances. Merge data with items in -the store if matches are found. - -@signature `can.Model.models(data[, oldList])` -@param {Array} data The raw data from a `[can.Model.findAll findAll()]` request. -@param {can.Model.List} [oldList] If supplied, this List will be updated with the data from -__data__. -@return {can.Model.List} A List of Models made from the raw data. - - -@body - -## Use - -`.models(data)` is used to create a [can.Model.List] of [can.Model] instances -with the data provided. If an item in data matches an instance in the [can.Model.store], -that instance will be merged with the item's data and inserted in the list. - -For example - -``` -Task = can.Model.extend({},{}) - -var t1 = new Task({id: 1, name: "dishes"}); - -// Binding on a model puts it in the store. -t1.bind("change", function(){}) - -var tasks = Task.models([ - {id: 1, name : "dishes", complete : false}, - {id: 2, name: "laundry", complete: true} -]) - -t1 === tasks.attr(0) //-> true -t1.attr("complete") //-> false -``` diff --git a/model/doc/destroy.md b/model/doc/destroy.md deleted file mode 100644 index 29c31e49006..00000000000 --- a/model/doc/destroy.md +++ /dev/null @@ -1,63 +0,0 @@ -@description Destroy a resource on the server. -@function can.Model.destroy destroy -@parent can.Model.static - -@signature `can.Model.destroy: function(id) -> deferred` - -If you provide a function, the Model will expect you to do your own AJAX requests. -@param { } id The ID of the resource to destroy. -@return {can.Deferred} A Deferred that resolves to the destroyed model. - -@signature `can.Model.destroy: "[METHOD] /path/to/resource"` - -If you provide a URL, the Model will send a request to that URL using -the method specified (or DELETE if none is specified) when deleting an -instance on the server. (See below for more details.) - -@return {can.Deferred} A Deferred that resolves to the destroyed model. - -@body -`destroy(id) -> Deferred` is used by [can.Model::destroy] remove a model -instance from the server. - -## Implement with a URL - -You can implement destroy with a string like: - -``` -Recipe = can.Model.extend({ - destroy : "/recipe/{id}" -},{}) -``` - -And use [can.Model::destroy] to destroy it like: - -``` -Recipe.findOne({id: 1}, function(recipe){ - recipe.destroy(); -}); -``` - -This sends a `DELETE` request to `/thing/destroy/1`. - -If your server does not support `DELETE` you can override it like: - -``` -Recipe = can.Model.extend({ - destroy : "POST /recipe/destroy/{id}" -},{}) -``` - -## Implement with a function - -Implement destroy with a function like: - -``` -Recipe = can.Model.extend({ - destroy : function(id){ - return $.post("/recipe/destroy/"+id,{}); - } -},{}) -``` - -Destroy just needs to return a deferred that resolves. \ No newline at end of file diff --git a/model/doc/findAll.md b/model/doc/findAll.md deleted file mode 100644 index d6aad4762c7..00000000000 --- a/model/doc/findAll.md +++ /dev/null @@ -1,151 +0,0 @@ -@description Retrieve multiple resources from a server. -@function can.Model.findAll findAll -@parent can.Model.static - -@signature `can.Model.findAll( params[, success[, error]] )` - -Retrieve multiple resources from a server. - -@param {Object} params Values to filter the request or results with. -@param {function(can.Model.List)} [success(list)] A callback to call on successful retrieval. The callback receives -a can.Model.List of the retrieved resources. -@param {function(can.AjaxSettings)} [error(xhr)] A callback to call when an error occurs. The callback receives the -XmlHttpRequest object. -@return {can.Deferred} A deferred that resolves to a [can.Model.List] of retrieved models. - - -@signature `can.Model.findAll: findAllData( params ) -> deferred` - -Implements `findAll` with a [can.Model.findAllData function]. This function -is passed to [can.Model.makeFindAll makeFindAll] to create the external -`findAll` method. - -``` -findAll: function(params){ - return $.get("/tasks",params) -} -``` - -@param {can.Model.findAllData} findAllData A function that accepts parameters -specifying a list of instance data to retrieve and returns a [can.Deferred] -that resolves to an array of those instances. - -@signature `can.Model.findAll: "[METHOD] /path/to/resource"` - -Implements `findAll` with a HTTP method and url to retrieve instance data. - - findAll: "GET /tasks" - -If `findAll` is implemented with a string, this gets converted to -a [can.Model.findAllData findAllData function] -which is passed to [can.Model.makeFindAll makeFindAll] to create the external -`findAll` method. - -@param {HttpMethod} METHOD An HTTP method. Defaults to `"GET"`. - -@param {STRING} url The URL of the service to retrieve JSON data. - -@return {JSON} The service should return a JSON object like: - -``` -{ - "data": [ - { "id" : 1, "name" : "do the dishes" }, - { "id" : 2, "name" : "mow the lawn" }, - { "id" : 3, "name" : "iron my shirts" } - ] -} -``` - -This object is passed to [can.Model.models] to turn it into instances. - -_Note: .findAll can also accept an array, but you -probably [should not be doing that](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)._ - - -@signature `can.Model.findAll: {ajaxSettings}` - -Implements `findAll` with a [can.AjaxSettings ajax settings object]. - -``` -findAll: {url: "/tasks", dataType: "json"} -``` - -If `findAll` is implemented with an object, it gets converted to -a [can.Model.findAllData findAllData function] -which is passed to [can.Model.makeFindAll makeFindAll] to create the external -`findAll` method. - -@param {can.AjaxSettings} ajaxSettings A settings object that -specifies the options available to pass to [can.ajax]. - -@body - -## Use - -`findAll( params, success(instances), error(xhr) ) -> Deferred` is used to retrieve model -instances from the server. After implementing `findAll`, use it to retrieve instances of the model -like: - -``` -Recipe.findAll({favorite: true}, function(recipes){ - recipes[0].attr('name') //-> "Ice Water" -}, function( xhr ){ - // called if an error -}) //-> Deferred -``` - -`findAll` uses [can.Model.parseModels parseModels] to parse the returned data. `can.Model.parseModels` -can be overwritten to handle non-standard data formats. - -Before you can use `findAll`, you must implement it. - -## Implement with a URL - -Implement findAll with a url like: - -``` -Recipe = can.Model.extend({ - findAll : "/recipes.json" -},{}); -``` - -The server should return data that looks like: - -``` -[ - {"id" : 57, "name": "Ice Water"}, - {"id" : 58, "name": "Toast"} -] -``` - -## Implement with an Object - -Implement findAll with an object that specifies the parameters to -`can.ajax` (jQuery.ajax) like: - -``` -Recipe = can.Model.extend({ - findAll : { - url: "/recipes.xml", - dataType: "xml" - } -},{}); -``` - -## Implement with a Function - -To implement with a function, `findAll` is passed __params__ to filter -the instances retrieved from the server and it should return a -deferred that resolves to an array of model data. For example: - -``` -Recipe = can.Model.extend({ - findAll : function(params){ - return $.ajax({ - url: '/recipes.json', - type: 'get', - dataType: 'json'}) - } -},{}); -``` diff --git a/model/doc/findAllData.md b/model/doc/findAllData.md deleted file mode 100644 index 95f274019df..00000000000 --- a/model/doc/findAllData.md +++ /dev/null @@ -1,30 +0,0 @@ -@typedef {function} can.Model.findAllData findAllData - -Retrieves a list of items for [can.Model.models], typically by making an -Ajax request. - -@param {Object} params Specifies the list to be retrieved. - -@return {can.Deferred} A deferred that resolves to a data structure -that can be understood by [can.Model.models]. - - - - -@body - -## Use - -Typically, `findAll` is implemented with a "string" or [can.AjaxSettings ajax settings object] like: - -``` -findAll: "GET /tasks" -``` - -or - -``` -findAll: {url: "/tasks", dataType: "custom"} -``` - -[can.Model.setup] converts diff --git a/model/doc/findOne.md b/model/doc/findOne.md deleted file mode 100644 index f24b4be6c88..00000000000 --- a/model/doc/findOne.md +++ /dev/null @@ -1,136 +0,0 @@ - -@description Retrieve a resource from a server. -@function can.Model.findOne findOne -@parent can.Model.static - -@signature `can.Model.findOne( params[, success[, error]] )` - -Retrieve a single instance from the server. - -@param {Object} params Values to filter the request or results with. -@param {function(can.Model)} [success(model)] A callback to call on successful retrieval. The callback receives -the retrieved resource as a can.Model. -@param {function(can.AjaxSettings)} [error(xhr)] A callback to call when an error occurs. The callback receives the -XmlHttpRequest object. -@return {can.Deferred} A deferred that resolves to a [can.Model] instance of the retrieved model - -@signature `can.Model.findOne: findOneData( params ) -> deferred` - -Implements `findOne` with a [can.Model.findOneData function]. This function -is passed to [can.Model.makeFindOne makeFindOne] to create the external -`findOne` method. - -``` -findOne: function(params){ - return $.get("/task/"+params.id) -} -``` - -@param {can.Model.findOneData} findOneData A function that accepts parameters -specifying an instance to retreive and returns a [can.Deferred] -that resolves to that instance. - -@signature `can.Model.findOne: "[METHOD] /path/to/resource"` - -Implements `findOne` with a HTTP method and url to retrieve an instance's data. - -``` -findOne: "GET /tasks/{id}" -``` - -If `findOne` is implemented with a string, this gets converted to -a [can.Model.makeFindOne makeFindOne function] -which is passed to [can.Model.makeFindOne makeFindOne] to create the external -`findOne` method. - -@param {HttpMethod} METHOD An HTTP method. Defaults to `"GET"`. - -@param {STRING} url The URL of the service to retrieve JSON data. - -@signature `can.Model.findOne: {ajaxSettings}` - -Implements `findOne` with a [can.AjaxSettings ajax settings object]. - - findOne: {url: "/tasks/{id}", dataType: "json"} - -If `findOne` is implemented with an object, it gets converted to -a [can.Model.makeFindOne makeFindOne function] -which is passed to [can.Model.makeFindOne makeFindOne] to create the external -`findOne` method. - -@param {can.AjaxSettings} ajaxSettings A settings object that -specifies the options available to pass to [can.ajax]. - -@body - -## Use - -`findOne( params, success(instance), error(xhr) ) -> Deferred` is used to retrieve a model -instance from the server. - -Use `findOne` like: - -``` -Recipe.findOne({id: 57}, function(recipe){ -recipe.attr('name') //-> "Ice Water" -}, function( xhr ){ -// called if an error -}) //-> Deferred -``` - -Before you can use `findOne`, you must implement it. - -## Implement with a URL - -Implement findAll with a url like: - -``` -Recipe = can.Model.extend({ - findOne : "/recipes/{id}.json" -},{}); -``` - - -If `findOne` is called like: - -``` -Recipe.findOne({id: 57}); -``` - -The server should return data that looks like: - -``` -{"id" : 57, "name": "Ice Water"} -``` - -## Implement with an Object - -Implement `findOne` with an object that specifies the parameters to -`can.ajax` (jQuery.ajax) like: - -``` -Recipe = can.Model.extend({ - findOne : { - url: "/recipes/{id}.xml", - dataType: "xml" - } -},{}) -``` - -## Implement with a Function - -To implement with a function, `findOne` is passed __params__ to specify -the instance retrieved from the server and it should return a -deferred that resolves to the model data. Also notice that you now need to -build the URL manually. For example: - -``` -Recipe = can.Model.extend({ - findOne : function(params){ - return $.ajax({ - url: '/recipes/' + params.id, - type: 'get', - dataType: 'json'}) - } -},{}) -``` diff --git a/model/doc/findOneData.md b/model/doc/findOneData.md deleted file mode 100644 index 9b1e5e85d56..00000000000 --- a/model/doc/findOneData.md +++ /dev/null @@ -1,4 +0,0 @@ -@typedef {function} can.Model.findOneData findOneData - -@param {Object} params -@return {can.Deferred} A deferred diff --git a/model/doc/id.md b/model/doc/id.md deleted file mode 100644 index 2f86a13d3c0..00000000000 --- a/model/doc/id.md +++ /dev/null @@ -1,11 +0,0 @@ -@property {String} can.Model.id id -@parent can.Model.static -The name of the id field. Defaults to `'id'`. Change this if it is something different. - -For example, it's common in .NET to use `'Id'`. Your model might look like: - -``` -Friend = can.Model.extend({ - id: "Id" -},{}); -``` diff --git a/model/doc/isNew.md b/model/doc/isNew.md deleted file mode 100644 index e3b2e79f413..00000000000 --- a/model/doc/isNew.md +++ /dev/null @@ -1,15 +0,0 @@ -@function can.Model.prototype.isNew isNew -@parent can.Model.prototype -@description Check if a Model has yet to be saved on the server. -@signature `model.isNew()` -@return {Boolean} Whether an instance has been saved on the server. -(This is determined by whether `id` has a value set yet.) - -@body -`isNew()` returns if the instance is has been created -on the server. This is essentially if the [can.Model.id] -property is null or undefined. - -``` -new Recipe({id: 1}).isNew() //-> false -``` \ No newline at end of file diff --git a/model/doc/list.md b/model/doc/list.md deleted file mode 100644 index 864f7edbf46..00000000000 --- a/model/doc/list.md +++ /dev/null @@ -1,53 +0,0 @@ -@property {can.Model.List} can.Model.static.List List -@parent can.Model.static - -@description Specifies the type of List that [can.Model.findAll findAll] -should return. - -@option {can.Model.List} A can.Model's List property is the -type of [can.List List] returned -from [can.Model.findAll findAll]. For example: - -``` -Task = can.Model.extend({ - findAll: "/tasks" -},{}) - -Task.findAll({}, function(tasks){ - tasks instanceof Task.List //-> true -}) -``` - -Overwrite a Model's `List` property to add custom -behavior to the lists provided to `findAll` like: - -``` -Task = can.Model.extend({ - findAll: "/tasks" -},{}) -Task.List = Task.List.extend({ - completed: function(){ - var count = 0; - this.each(function(task){ - if( task.attr("completed") ) count++; - }) - return count; - } -}) - -Task.findAll({}, function(tasks){ - tasks.completed() //-> 3 -}) -``` - -When [can.Model] is extended, -[can.Model.List] is extended and set as the extended Model's -`List` property. The extended list's [can.List.Map Map] property -is set to the extended Model. For example: - -``` -Task = can.Model.extend({ - findAll: "/tasks" -},{}) -Task.List.Map //-> Task -``` \ No newline at end of file diff --git a/model/doc/makeFindAll.md b/model/doc/makeFindAll.md deleted file mode 100644 index d0eb21165a4..00000000000 --- a/model/doc/makeFindAll.md +++ /dev/null @@ -1,83 +0,0 @@ -@function can.Model.makeFindAll makeFindAll -@parent can.Model.static - -`makeFindAll` is a hook that lets you define special `findAll` behavior. -It is a generator function that lets you return the function that will -actually be called when `findAll` is called. - -@signature `can.Model.makeFindAll: function(findAllData) -> findAll` - -Returns the external `findAll` method given the implemented [can.Model.findAllData findAllData] function. - -@param {can.Model.findAllData} findAllData - -[can.Model.findAll] is implemented with a `String`, [can.ajax ajax settings object], or -[can.Model.findAllData findAllData] function. If it is implemented as -a `String` or [can.ajax ajax settings object], those values are used -to create a [can.Model.findAllData findAllData] function. - -The [can.Model.findAllData findAllData] function is passed to `makeFindAll`. `makeFindAll` -should use `findAllData` internally to get the raw data for the request. - -@return {function(params,success,error):can.Deferred} - -Returns function that implements the external API of `findAll`. - -@body - -## Use - -When a user calls `MyModel.findAll({})`, the function returned by -`makeFindAll` will be called. Here you can specify what you want to happen -before the real request for data is made. Call the function passed in `findAllData` with `params` to make the AJAX request, or whatever the external request for data normally does. - -`makeFindAll` can be used to implement base models that perform special -behavior, like caching, or adding special parameters to the request object. `makeFindAll` is passed a [can.Model.findAllData findAllData] function that retrieves raw -data. It should return a function that when called, uses -the findAllData function to get the raw data and manually convert it to model instances with -[can.Model.models models]. - -## Caching - -The following uses `makeFindAll` to create a base `CachedModel`: - -```js -CachedModel = can.Model.extend({ - makeFindAll: function(findAllData){ - // A place to store requests - var cachedRequests = {}; - - return function(params, success, error){ - // is this not cached? - if(! cachedRequests[JSON.stringify(params)] ) { - var self = this; - // make the request for data, save deferred - cachedRequests[JSON.stringify(params)] = - findAllData(params).then(function(data){ - // convert the raw data into instances - return self.models(data) - }) - } - // get the saved request - var def = cachedRequests[JSON.stringify(params)] - // hookup success and error - def.then(success,error) - return def; - } - } -},{}) -``` - -The following Todo model will never request the same list of todo's twice: - -```js -Todo = CachedModel({ - findAll: "/todos" -},{}) - -// widget 1 -Todo.findAll({}) - -// widget 2 -Todo.findAll({}) -``` diff --git a/model/doc/makeFindOne.md b/model/doc/makeFindOne.md deleted file mode 100644 index 54f40c568fd..00000000000 --- a/model/doc/makeFindOne.md +++ /dev/null @@ -1,83 +0,0 @@ -@function can.Model.makeFindOne -@parent can.Model.static - -`makeFindOne` is a hook that lets you define special `findOne` behavior. -It is a generator function that lets you return the function that will -actually be called when `findOne` is called. - -@signature `can.Model.makeFindOne: function(findOneData) -> findOne` - -Returns the external `findOne` method given the implemented [can.Model.findOneData findOneData] function. - -@param {can.Model.findOneData} findOneData - -[can.Model.findOne] is implemented with a `String`, [can.ajax ajax settings object], or -[can.Model.findOneData findOneData] function. If it is implemented as -a `String` or [can.ajax ajax settings object], those values are used -to create a [can.Model.findOneData findOneData] function. - -The [can.Model.findOneData findOneData] function is passed to `makeFindOne`. `makeFindOne` -should use `findOneData` internally to get the raw data for the request. - -@return {function(params,success,error):can.Deferred} - -Returns function that implements the external API of `findOne`. - -@body - -## Use - -When a user calls `MyModel.findOne({})`, the function returned by -`makeFindOne` will be called. Here you can specify what you want to happen -before the real request for data is made. Call the function passed in `findOneData` with `params` to make the AJAX request, or whatever the external request for data normally does. - -`makeFindOne` can be used to implement base models that perform special -behavior, like caching, or adding special parameters to the request object. `makeFindOne` is passed a [can.Model.findOneData findOneData] function that retrieves raw -data. It should return a function that when called, uses -the findOneData function to get the raw data and convert it to a model instance with -[can.Model.model model]. - -## Caching - -The following uses `makeFindOne` to create a base `CachedModel`: - -```js -CachedModel = can.Model.extend({ - makeFindOne: function(findOneData){ - // A place to store requests - var cachedRequests = {}; - - return function(params, success, error){ - // is this not cached? - if(! cachedRequests[JSON.stringify(params)] ) { - var self = this; - // make the request for data, save deferred - cachedRequests[JSON.stringify(params)] = - findOneData(params).then(function(data){ - // convert the raw data into instances - return self.model(data) - }) - } - // get the saved request - var def = cachedRequests[JSON.stringify(params)] - // hookup success and error - def.then(success,error) - return def; - } - } -},{}) -``` - -The following Todo model will never request the same todo twice: - -```js -Todo = CachedModel({ - findOne: "/todos/{id}" -},{}) - -// widget 1 -Todo.findOne({id: 5}) - -// widget 2 -Todo.findOne({id: 5}) -``` diff --git a/model/doc/model.md b/model/doc/model.md deleted file mode 100644 index 1ca969e045a..00000000000 --- a/model/doc/model.md +++ /dev/null @@ -1,168 +0,0 @@ -@constructor can.Model -@parent canjs -@download can/model -@test can/model/test.html -@link ../docco/model/model.html docco - -@signature `can.Model([name,] staticProperties, instanceProperties)` -Create a can.Model constructor. (See [can.Construct] for more details on this syntax.) -@param {String} [name] If given, this will be the globally-available name of the constructor function. -@param {Object} staticProperties The static properties of the class. See below for properties with -special meanings to `can.Model`. -@param {Object} instanceProperties The instance properties of instances of the class. These will usually -be functions. -@return {Function} A can.Model constructor. - -@signature `new can.Model([options])` -Creates a new instance of _ModelConstructor_. -@param {Object} [options] Options to pass to `setup` or `init`. -@return {can.Model} A new instance of _ModelConstructor_. - -@body -Model adds service encapsulation to [can.Map]. Model lets you: - - - Get and modify data from the server - - Listen to changes by the server - - Keep track of all instances and prevent duplicates in the non-leaking [can.Model.store] - -## Get and modify data from the server - -can.Model makes connecting to a JSON REST service -really easy. The following models `todos` by -describing the services that can create, retrieve, -update, and delete todos. - -``` -Todo = can.Model.extend({ - findAll: 'GET /todos.json', - findOne: 'GET /todos/{id}.json', - create: 'POST /todos.json', - update: 'PUT /todos/{id}.json', - destroy: 'DELETE /todos/{id}.json' -},{}); -``` - -This lets you create, retrieve, update, and delete -todos programatically: - -__Create__ - -Create a todo instance and -call [can.Model::save save]\(success, error\) -to create the todo on the server. - -``` -// create a todo instance -var todo = new Todo({name: "do the dishes"}) - -// save it on the server -todo.save(); -``` - -__Retrieve__ - -Retrieve a list of todos from the server with -[can.Model.findAll findAll]\(params, success(items), error\): - -``` -Todo.findAll({}, function( todos ){ - - // print out the todo names - $.each(todos, function(i, todo){ - console.log( todo.name ); - }); -}); -``` - -Retrieve a single todo from the server with -[can.Model.findOne findOne]\(params, success(item), error\): - -``` -Todo.findOne({id: 5}, function( todo ){ - - // print out the todo name - console.log( todo.name ); -}); -``` - -__Update__ - -Once an item has been created on the server, -you can change its properties and call -save to update it on the server. - -``` -// update the todos' name -todo.attr('name','Take out the trash') - -// update it on the server -todo.save() -``` - -__Destroy__ - -Call [can.Model.prototype.destroy destroy]\(success, error\) -to delete an item on the server. - - todo.destroy() - -## Listen to changes in data - -Listening to changes in data is a critical part of -the [http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller Model-View-Controller] -architecture. can.Model lets you listen to when an item is created, updated, destroyed -or its properties are changed. Use -Model.[can.Model.bind bind]\(eventType, handler(event, model)\) -to listen to all events of type on a model and -model.[can.Model.prototype.bind bind]\(eventType, handler(event)\) -to listen to events on a specific instance. - -__Create__ - -``` -// listen for when any todo is created -Todo.bind('created', function( ev, todo ) {...}) - -// listen for when a specific todo is created -var todo = new Todo({name: 'do dishes'}) -todo.bind('created', function( ev ) {...}) -``` - -__Update__ - -``` -// listen for when any todo is updated -Todo.bind('updated', function( ev, todo ) {...}) - -// listen for when a specific todo is created -Todo.findOne({id: 6}, function( todo ) { - todo.bind('updated', function( ev ) {...}) -}) -``` - -__Destroy__ - -``` -// listen for when any todo is destroyed -Todo.bind('destroyed', function( ev, todo ) {...}) - -// listen for when a specific todo is destroyed -todo.bind('destroyed', function( ev ) {...}) -``` - -__Property Changes__ - -``` -// listen for when the name property changes -todo.bind('name', function(ev){ }) -``` - -__Listening with can.Control or can.Component__ - -You can use can.Control or the events property of can.Component to listen to model changes like: - -``` -Todos = can.Control.extend({ - "{Todo} updated" : function(Todo, ev, todo) {...} -}) -``` diff --git a/model/doc/model_bind.md b/model/doc/model_bind.md deleted file mode 100644 index 7994f5a7340..00000000000 --- a/model/doc/model_bind.md +++ /dev/null @@ -1,61 +0,0 @@ -@description Listen to events on this Model. -@function can.Model.prototype.bind bind -@parent can.Model.prototype -@signature `model.bind(eventName, handler)` -@param {String} eventName The event to bind to. -@param {function} handler The function to call when the -event occurs. __handler__ is passed the event and the -Model instance. -@return {can.Model} The Model, for chaining. - -@body -`bind(eventName, handler(ev, args...) )` is used to listen -to events on this model instance. Example: - -``` -Task = can.Model.extend() -var task = new Task({name : "dishes"}) -task.bind("name", function(ev, newVal, oldVal){}) -``` - -Use `bind` the -same as [can.Map::bind] which should be used as -a reference for listening to property changes. - -Bind on model can be used to listen to when -an instance is: - -- created -- updated -- destroyed - -like: - -``` -Task = can.Model.extend() -var task = new Task({name : "dishes"}) - -task.bind("created", function(ev, newTask){ - console.log("created", newTask) -}) -.bind("updated", function(ev, updatedTask){ - console.log("updated", updatedTask) -}) -.bind("destroyed", function(ev, destroyedTask){ - console.log("destroyed", destroyedTask) -}) - -// create, update, and destroy -task.save(function(){ - task.attr('name', "do dishes") - .save(function(){ - task.destroy() - }) -}); -``` - -`bind` also extends the inherited -behavior of [can.Map::bind] to track the number -of event bindings on this object which is used to store -the model instance. When there are no bindings, the -model instance is removed from the store, freeing memory. \ No newline at end of file diff --git a/model/doc/model_destroy.md b/model/doc/model_destroy.md deleted file mode 100644 index d6e173140e5..00000000000 --- a/model/doc/model_destroy.md +++ /dev/null @@ -1,40 +0,0 @@ -@function can.Model.prototype.destroy destroy -@parent can.Model.prototype -@description Destroy a Model on the server. -@signature `model.destroy([success[, error]])` -@param {function} [success] A callback to call on successful destruction. The callback receives -the can.Model as it was just prior to destruction. -@param {function} [error] A callback to call when an error occurs. The callback receives the -XmlHttpRequest object. -@return {can.Deferred} A Deferred that resolves to the Model as it was before destruction. - -@body -Destroys the instance by calling -[can.Model.destroy] with the id of the instance. - -``` -recipe.destroy(success, error); -``` - -This triggers "destroyed" events on the instance and the -Model constructor function which can be listened to with -[can.Model::bind] and [can.Model.bind]. - -``` -Recipe = can.Model.extend({ - destroy : "DELETE /services/recipes/{id}", - findOne : "/services/recipes/{id}" -},{}) - -Recipe.bind("destroyed", function(){ - console.log("a recipe destroyed"); -}); - -// get a recipe -Recipe.findOne({id: 5}, function(recipe){ - recipe.bind("destroyed", function(){ - console.log("this recipe destroyed") - }) - recipe.destroy(); -}) -``` diff --git a/model/doc/model_save.md b/model/doc/model_save.md deleted file mode 100644 index 7907ed1fe58..00000000000 --- a/model/doc/model_save.md +++ /dev/null @@ -1,68 +0,0 @@ -@function can.Model.prototype.save save -@parent can.Model.prototype -@description Save a model back to the server. -@signature `model.save([success[, error]])` -@param {function} [success] A callback to call on successful save. The callback receives -the can.Model after saving. -@param {function} [error] A callback to call when an error occurs. The callback receives the -XmlHttpRequest object. -@return {can.Deferred} A Deferred that resolves to the Model after it has been saved. - -@body -`model.save([success(model)],[error(xhr)])` creates or updates -the model instance using [can.Model.create] or -[can.Model.update] depending if the instance -[can.Model::isNew has an id or not]. - -## Using `save` to create an instance. - -If `save` is called on an instance that does not have -an [can.Model.id id] property, it calls [can.Model.create] -with the instance's properties. It also [can.trigger triggers] -a "created" event on the instance and the model. - -``` -// create a model instance -var todo = new Todo({name: "dishes"}); - -// listen when the instance is created -todo.bind("created", function(ev){ - this //-> todo -}); - -// save it on the server -todo.save(function(todo){ - console.log("todo", todo, "created"); -}); -``` - -## Using `save` to update an instance. - -If save is called on an instance that has -an [can.Model.id id] property, it calls [can.Model.update] -with the instance's properties. When the save is complete, -it triggers an "updated" event on the instance and the instance's model. - -Instances with an -__id__ are typically retrieved with [can.Model.findAll] or -[can.Model.findOne]. - -``` -// get a created model instance -Todo.findOne({id: 5},function(todo){ - - // listen when the instance is updated - todo.bind("updated", function(ev){ - this //-> todo - }) - - // update the instance's property - todo.attr("complete", true) - - // save it on the server - todo.save(function(todo){ - console.log("todo", todo, "updated") - }); - -}); -``` diff --git a/model/doc/model_unbind.md b/model/doc/model_unbind.md deleted file mode 100644 index 81afd2451b5..00000000000 --- a/model/doc/model_unbind.md +++ /dev/null @@ -1,27 +0,0 @@ -@function can.Model.prototype.unbind unbind -@parent can.Model.prototype -@description Stop listening to events on this Model. -@signature `model.unbind(eventName[, handler])` -@param {String} eventName The event to unbind from. -@param {function} [handler] A handler previously bound with `bind`. -If __handler__ is not passed, `unbind` will remove all handlers -for the given event. -@return {can.Model} The Model, for chaining. - -@body -`unbind(eventName, handler)` removes a listener -attached with [can.Model::bind]. - -``` -var handler = function(ev, createdTask){ - -} -task.bind("created", handler) -task.unbind("created", handler) -``` - -You have to pass the same function to `unbind` that you -passed to `bind`. - -Unbind will also remove the instance from the store -if there are no other listeners. \ No newline at end of file diff --git a/model/doc/parseModel.md b/model/doc/parseModel.md deleted file mode 100644 index f4b7e201a2c..00000000000 --- a/model/doc/parseModel.md +++ /dev/null @@ -1,72 +0,0 @@ -@function can.Model.parseModel parseModel -@parent can.Model.static -@description Convert raw data into an object that can be used to -create a [can.Model] instance. - -@signature `can.Model.parseModel( data, xhr )` -@release 2.1 - - -@param {Object} data The data to convert to a can.Model instance. -@param {XMLHTTPRequest} xhr The XMLHTTPRequest object used to make the request. -@return {Object} An object of properties to set at the [can.Model::attr attributes] -of a model instance. - -@signature `parseModel: "PROPERTY"` - -Creates a `parseModel` function that looks for the attributes object in the PROPERTY -property of raw instance data. - -@body - -## Use - -`can.Model.parseModel(data, xhr)` is used to -convert the raw response of a [can.Model.findOne findOne], -[can.Model.update update], and [can.Model.create create] request -into an object that [can.Model.model] can use to create -a model instances. - -This method is never called directly. Instead the deferred returned -by `findOne`, `update`, and `create` is piped into `parseModel`. If `findOne` was called, -the result of that is sent to [can.Model.model]. - -If your server is returning data in non-standard way, -overwriting `can.Model.parseModel` is the best way to normalize it. - -## Expected data format - -By default, [can.Model.model] expects data to be a name-value pair -object like: - -``` -{id: 1, name : "dishes"} -``` - -If your data does not look like this, you probably want to overwrite `parseModel`. - -## Overwriting parseModel - -If your service returns data like: - -``` -{ thingsToDo: {name: "dishes", id: 5} } -``` - -You will want to overwrite `parseModel` to pass the model what it expects like: - -``` -Task = can.Model.extend({ - parseModel: function(data){ - return data.thingsToDo; - } -},{}); -``` - -You could also do this like: - -``` -Task = can.Model.extend({ - parseModel: "thingsToDo" -},{}); -``` diff --git a/model/doc/parseModels.md b/model/doc/parseModels.md deleted file mode 100644 index 3c62eef28f1..00000000000 --- a/model/doc/parseModels.md +++ /dev/null @@ -1,96 +0,0 @@ -@function can.Model.parseModels parseModels -@parent can.Model.static -@description Convert raw xhr data into an array or object that can be used to -create a [can.Model.List]. -@release 2.1 - -@signature `can.Model.parseModels(data, xhr)` - -@param { } data The raw data from a `[can.Model.findAll findAll()]` request. - -@param {XMLHTTPRequest} [xhr] The XMLHTTPRequest object used to make the request. - -@return {Array|Object} A JavaScript Object or Array that [can.Model.models] -can convert into the Model's List. - -@signature `parseModels: "PROPERTY"` - -Creates a `parseModels` function that looks for the array of instance data in the PROPERTY -property of the raw response data of [can.Model.findAll]. - -@body - -## Use - -`can.Model.parseModels(data, xhr)` is used to -convert the raw response of a [can.Model.findAll] request -into an object or Array that [can.Model.models] can use to create -a [can.Model.List] of model instances. - -This method is never called directly. Instead the deferred returned -by findAll is piped into `parseModels` and the result of that -is sent to [can.Model.models]. - -If your server is returning data in non-standard way, -overwriting `can.Model.parseModels` is the best way to normalize it. - -## Expected data format - -By default, [can.Model.models] expects data to be an array of name-value pair -objects like: - -``` -[{id: 1, name : "dishes"},{id:2, name: "laundry"}, ...] -``` - -It can also take an object with additional data about the array like: - -``` -{ - count: 15000 //how many total items there might be - data: [{id: 1, name : "justin"},{id:2, name: "brian"}, ...] -} -``` - -In this case, models will return a [can.Model.List] of instances found in -data, but with additional properties as expandos on the list: - -``` -var tasks = Task.models({ - count : 1500, - data : [{id: 1, name: 'dishes'}, ...] -}) -tasks.attr("name") // -> 'dishes' -tasks.count // -> 1500 -``` - -If your data does not look like one of these formats, overwrite `parseModels`. - -## Overwriting parseModels - -If your service returns data like: - -``` -{ thingsToDo: [{name: "dishes", id: 5}] } -``` - -You will want to overwrite `parseModels` to pass the models what it expects like: - -``` -Task = can.Model.extend({ - parseModels: function(data){ - return data.thingsToDo; - } -},{}); -``` - -You could also do this like: - -``` -Task = can.Model.extend({ - parseModels: "thingsToDo" -},{}); -``` - -`can.Model.models` passes each instance's data to `can.Model.model` to -create the individual instances. diff --git a/model/doc/removeAttr.md b/model/doc/removeAttr.md deleted file mode 100644 index 2a2c3c911e3..00000000000 --- a/model/doc/removeAttr.md +++ /dev/null @@ -1,28 +0,0 @@ -@property {Boolean} can.Model.removeAttr removeAttr -@parent can.Model.static -Sets whether model conversion should remove non existing attributes or merge with -the existing attributes. The default is `false`. -For example, if `Task.findOne({ id: 1 })` returns - -``` -{ id: 1, name: 'Do dishes', index: 1, color: ['red', 'blue'] } -``` - -for the first request and - -``` -{ id: 1, name: 'Really do dishes', color: ['green'] } -``` - -for the next request, the actual model attributes would look like: - -``` -{ id: 1, name: 'Really do dishes', index: 1, color: ['green', 'blue'] } -``` - -Because the attributes of the original model and the updated model will -be merged. Setting `removeAttr` to `true` will result in model attributes like - -``` -{ id: 1, name: 'Really do dishes', color: ['green'] } -``` diff --git a/model/doc/resource.md b/model/doc/resource.md deleted file mode 100644 index 0f95a5aa511..00000000000 --- a/model/doc/resource.md +++ /dev/null @@ -1,64 +0,0 @@ -@property {String} can.Model.resource resource -@parent can.Model.static - -@description Define a restful resource URL. - -@option {String} - -A string URL to a restful resource. If the resource -is specified as `"resource"` and the model's [can.Model.id id] is -`"id"`, resource will implement [can.Model]'s ajax methods as follows: - - - [can.Model.findAll] - `"GET resource"` - - [can.Model.findOne] - `"GET resource/{id}"` - - [can.Model.create] - `"POST resource"` - - [can.Model.update] - `"PUT resource/{id}"` - - [can.Model.destroy] - `"DELETE resource/{id}"` - -Setting the `resource` property will not overwrite other implemented -ajax methods, however will overwrite inherited ajax methods. - -@body - -## Use - -For each of the names (create, update, destroy, findOne, and findAll) use the -URL provided by the `resource` property. For example: - -``` -Todo = can.Model.extend({ - resource: "/todos" -}, {}); -``` - -Will create a can.Model that is identical to: - -``` -Todo = can.Model.extend({ - findAll: "GET /todos", - findOne: "GET /todos/{id}", - create: "POST /todos", - update: "PUT /todos/{id}", - destroy: "DELETE /todos/{id}" -},{}); -``` - -Inherited AJAX methods will be overwritten when using the `resource` property. For example, inheriting our Todo model: - -``` -SpecialTodo = Todo.extend({ - resource: "/specialTodos" -}, {}); -``` - -Will create a Todo model identical to: - -``` -SpecialTodo = can.Model.extend({ - findAll: "GET /specialTodos", - findOne: "GET /specialTodos/{id}", - create: "POST /specialTodos", - update: "PUT /specialTodos/{id}", - destroy: "DELETE /specialTodos/{id}" -}, {}); -``` diff --git a/model/doc/setup.md b/model/doc/setup.md deleted file mode 100644 index 13890b79e3c..00000000000 --- a/model/doc/setup.md +++ /dev/null @@ -1,5 +0,0 @@ -@hide -@function can.Model.setup -@parent can.Model.static - -Configures \ No newline at end of file diff --git a/model/doc/store.md b/model/doc/store.md deleted file mode 100644 index 5fb535f7eb9..00000000000 --- a/model/doc/store.md +++ /dev/null @@ -1,88 +0,0 @@ -@property {Object} can.Model.store store -@parent can.Model.static - -@description A non-leaking global store of can.Model instances. - -@body - -## Use - -The model store is used to store instances of a model. It serves two purposes: - -1. Preventing duplicate instances of a model from creating duplicate instance copies that get out of sync. -1. Cleaning up old unused instances so that the size of this store remains minimal, and applications don't slowly collect memory over time without releasing it. - -From a development perspective, the store can be used as a global hash to look up model instances. Instances are stored by their id. A model's store property thus looks like this: - - { - "34535": {id: "34535", name: "Bob", _bindings: 3}, - "34536": {id: "34536", name: "Mike", _bindings: 3}, - } - -The store is typically not used in application code, but rather is an internal feature of can.Model. However, it is possible to use it for looking up model instances or overriding the default behavior of the store to do something special. - -### Duplicate Instances - -The main reason to have a global store is to prevent duplicate instances from being created. - -#### The problem - -For example, an application could retrieve and display two lists of todos in the same page. First a list of todos due tomorrow: - - Todo.findAll({due: "tomorrow"}) - -That response might look like: - - [ - {id: 2, name: "finish writing docs", urgency: "low", due: "tomorrow", completed: false}, - {id: 7, name: "sell my car", urgency: "high", due: "tomorrow", completed: false} - ] - -Next a list of todos that are urgent: - -``` -Todo.findAll({priority: "high"}) -``` - -That response might look like: - - [ - {id: 5, name: "take dog to the vet", urgency: "high", due: "next week", completed: false}, - {id: 7, name: "sell my car", urgency: "high", due: "tomorrow", completed: false} - ] - -As you can see, there is one todo that appears in both lists - sell my car. Without a can.Model.store, these would both be treated as independent model instances. - -If these todos are displayed in separate lists in the page, and a user marks "sell my car" as completed in the "due tomorrow" list, which causes the completed property to toggle to true, the other todo would not reflect this change. This is a big problem! - -#### The solution - -In can.Model, whenever the first model response is received, each item in the response will be added to the store, keyed off the [can.Model.id] property. When the second model response is received, instead of creating a second instance, it will check the store for the given id (7) and find the pre-existing model instance. A new instance won't be created. Instead, the same instance will be reused in the second response. - -Therefore, when the user marks the completed property as true in the "due tomorrow" list, the other list, which is displaying the same todo instance, will reflect this change. Both lists are showing the same instance, and via live binding, will reflect the changed property visually in the DOM. Magic! - -#### Future updates - -Similarly, if another duplicate instance is retrieved later via another findAll, and it has a new `due` property, the store instance will be updated with that new property, and all displayed instances will update themselves. There will only be one instance with each unique ID in the page at any time. - -### Non-leaking Store - -The problem with a global store is that it grows in size over the lifecycle of a heavily used application without ever releasing its memory. can.Model.store solves this problem by removing any instances that are not being used anymore. - -#### How it works - -Each can.Model instance has a `_bindings` property, which is a reference counter keeping track of how many times this instance has been bound to. There are two ways _bindings gets incremented: - -1. If the instance is bound to directly: - - todo.bind('name', function(){}) - -2. If properties of the instance are bound to via live binding in a template: - - Name: {{name}} - -The reverse is also true. _bindings gets decremented whenever `unbind` is called manually, or, more commonly, whenever a part of the DOM connected to live bindings gets removed. Removing live-bound portions of the DOM cause the live bindings to be removed via calls to `unbind`, which decrements the _bindings count. - -What this means is whenever a model is being "shown" in the page, it has _bindings > 0. Whenever that model is no longer being "shown", its _bindings get decremented down to 0. There is an internal _cleanup that will periodically delete any instances that have _bindings === 0, allowing browser garbage collection to free up that memory. - -The result is that in long running applications that stream large amounts of data, it is safe to assume that this store will not cause memory to increase over time. \ No newline at end of file diff --git a/model/doc/unbind.md b/model/doc/unbind.md deleted file mode 100644 index 5a0ddb1e285..00000000000 --- a/model/doc/unbind.md +++ /dev/null @@ -1,25 +0,0 @@ -@function can.Model.unbind unbind -@parent can.Model.static -@description Stop listening for events on a Model class. - -@signature `can.Model.unbind(eventType, handler)` -@param {String} eventType The type of event. It must be -`"created"`, `"updated"`, `"destroyed"`. -@param {function} handler A callback function -that was passed to `bind`. -@return {can.Model} The model constructor function. - -@body -`unbind(eventType, handler)` removes a listener -attached with [can.Model.bind]. - -``` -var handler = function(ev, createdTask){ - -} -Task.bind("created", handler) -Task.unbind("created", handler) -``` - -You have to pass the same function to `unbind` that you -passed to `bind`. \ No newline at end of file diff --git a/model/doc/update.md b/model/doc/update.md deleted file mode 100644 index af907aa4f69..00000000000 --- a/model/doc/update.md +++ /dev/null @@ -1,91 +0,0 @@ -@description Update a resource on the server. -@function can.Model.update update -@parent can.Model.static -@signature `can.Model.update: "[METHOD] /path/to/resource"` -If you provide a URL, the Model will send a request to that URL using -the method specified (or PUT if none is specified) when updating an -instance on the server. (See below for more details.) -@return {can.Deferred} A Deferred that resolves to the updated model. - -@signature `can.Model.update: function(id, serialized) -> can.Deffered` -If you provide a function, the Model will expect you to do your own AJAX requests. -@param { } id The ID of the model to update. -@param {Object} serialized The [can.Map::serialize serialized] properties of -the model to update. -@return {can.Deferred} A Deferred that resolves to the updated model. - -@body -`update( id, attrs ) -> Deferred` is used by [can.Model::save save] to -update a model instance on the server. - -## Implement with a URL - -The easist way to implement update is to just give it the url to `PUT` data to: - -``` -Recipe = can.Model.extend({ - update: "/recipes/{id}" -},{}); -``` - -This lets you update a recipe like: - -``` -Recipe.findOne({id: 1}, function(recipe){ - recipe.attr('name','salad'); - recipe.save(); -}) -``` - -This will make an XHR request like: - -``` -PUT /recipes/1 -name=salad -``` - -If your server doesn't use PUT, you can change it to post like: - -``` -Recipe = can.Model.extend({ - update: "POST /recipes/{id}" -},{}); -``` - -The server should send back an object with any new attributes the model -should have. For example if your server updates the "updatedAt" property, it -should send back something like: - -``` -// PUT /recipes/4 {name: "Food"} -> -{ - updatedAt : "10-20-2011" -} -``` - -## Implement with a Function - -You can also implement update by yourself. Update takes the `id` and -`attributes` of the instance to be updated. Update must return -a [can.Deferred Deferred] that resolves to an object that contains any -properties that should be set on the instance. - -For example, the following code makes a request -to '/recipes/5.json?name=hot+dog' and gets back -something that looks like: - -``` -{ - updatedAt: "10-20-2011" -} -``` - -The code looks like: - -``` -Recipe = can.Model.extend({ - update : function(id, attrs ) { - return $.post("/recipes/"+id+".json",attrs, null,"json"); - } -},{}); -``` diff --git a/model/model.js b/model/model.js index 38bf56e459d..97ffd520f31 100644 --- a/model/model.js +++ b/model/model.js @@ -1,685 +1,3 @@ -var can = require('can/util/util'); -require('can/map/map'); -require('can/list/list'); +var can = require('../util/can'); -/** @add can.Model **/ -// ## model.js -// (Don't steal this file directly in your code.) - -// ## pipe -// `pipe` lets you pipe the results of a successful deferred -// through a function before resolving the deferred. -var pipe = function (def, thisArg, func) { - // The piped result will be available through a new Deferred. - var d = new can.Deferred(); - def.then(function () { - var args = can.makeArray(arguments), - success = true; - - try { - // Pipe the results through the function. - args[0] = func.apply(thisArg, args); - } catch (e) { - success = false; - // The function threw an error, so reject the Deferred. - d.rejectWith(d, [e].concat(args)); - } - if (success) { - // Resolve the new Deferred with the piped value. - d.resolveWith(d, args); - } - }, function () { - // Pass on the rejection if the original Deferred never resolved. - d.rejectWith(this, arguments); - }); - - // `can.ajax` returns a Deferred with an abort method to halt the AJAX call. - if (typeof def.abort === 'function') { - d.abort = function () { - return def.abort(); - }; - } - - // Return the new (piped) Deferred. - return d; -}, - - // ## modelNum - // When new model constructors are set up without a full name, - // `modelNum` lets us name them uniquely (to keep track of them). - modelNum = 0, - - // ## getId - getId = function (inst) { - // `can.__observe` makes a note that `id` was just read. - can.__observe(inst, inst.constructor.id); - // Use `__get` instead of `attr` for performance. (But that means we have to remember to call `can.__observe`.) - return inst.___get(inst.constructor.id); - }, - - // ## ajax - // This helper method makes it easier to make an AJAX call from the configuration of the Model. - ajax = function (ajaxOb, data, type, dataType, success, error) { - - var params = {}; - - // A string here would be something like `"GET /endpoint"`. - if (typeof ajaxOb === 'string') { - // Split on spaces to separate the HTTP method and the URL. - var parts = ajaxOb.split(/\s+/); - params.url = parts.pop(); - if (parts.length) { - params.type = parts.pop(); - } - } else { - // If the first argument is an object, just load it into `params`. - can.extend(params, ajaxOb); - } - - // If the `data` argument is a plain object, copy it into `params`. - params.data = typeof data === "object" && !can.isArray(data) ? - can.extend(params.data || {}, data) : data; - - // Substitute in data for any templated parts of the URL. - params.url = can.sub(params.url, params.data, true); - - return can.ajax(can.extend({ - type: type || 'post', - dataType: dataType || 'json', - success: success, - error: error - }, params)); - }, - - // ## makeRequest - // This function abstracts making the actual AJAX request away from the Model. - makeRequest = function (modelObj, type, success, error, method) { - var args; - - // If `modelObj` is an Array, it it means we are coming from - // the queued request, and we're passing already-serialized data. - if (can.isArray(modelObj)) { - // In that case, modelObj's signature will be `[modelObj, serializedData]`, so we need to unpack it. - args = modelObj[1]; - modelObj = modelObj[0]; - } else { - // If we aren't supplied with serialized data, we'll make our own. - args = modelObj.serialize(); - } - args = [args]; - - var deferred, - model = modelObj.constructor, - jqXHR; - - // When calling `update` and `destroy`, the current ID needs to be the first parameter in the AJAX call. - if (type === 'update' || type === 'destroy') { - args.unshift(getId(modelObj)); - } - jqXHR = model[type].apply(model, args); - - // Make sure that can.Model can react to the request before anything else does. - deferred = pipe(jqXHR, modelObj, function (data) { - // `method` is here because `"destroyed" !== "destroy" + "d"`. - // TODO: Do something smarter/more consistent here? - modelObj[method || type + "d"](data, jqXHR); - return modelObj; - }); - - // Hook up `abort` - if (jqXHR.abort) { - deferred.abort = function () { - jqXHR.abort(); - }; - } - - deferred.then(success, error); - return deferred; - }, - - converters = { - // ## models - // The default function for converting into a list of models. Needs to be stored separate - // because we will reference it in models static `setup`, too. - models: function (instancesRawData, oldList, xhr) { - // Increment reqs counter so new instances will be added to the store. - // (This is cleaned up at the end of the method.) - can.Model._reqs++; - - // If there is no data, we can't really do anything with it. - if (!instancesRawData) { - return; - } - - // If the "raw" data is already a List, it's not raw. - if (instancesRawData instanceof this.List) { - return instancesRawData; - } - - var self = this, - // `tmp` will hold the models before we push them onto `modelList`. - tmp = [], - // `ML` (see way below) is just `can.Model.List`. - ListClass = self.List || ML, - modelList = oldList instanceof can.List ? oldList : new ListClass(), - - // Check if we were handed an Array or a model list. - rawDataIsList = instancesRawData instanceof ML, - - // Get the "plain" objects from the models from the list/array. - raw = rawDataIsList ? instancesRawData.serialize() : instancesRawData; - - raw = self.parseModels(raw, xhr); - - if(raw.data) { - instancesRawData = raw; - raw = raw.data; - } - - if (typeof raw === 'undefined' || !can.isArray(raw)) { - throw new Error('Could not get any raw data while converting using .models'); - } - - //!steal-remove-start - if (!raw.length) { - can.dev.warn("model.js models has no data."); - } - //!steal-remove-end - - // If there was anything left in the list we were given, get rid of it. - if (modelList.length) { - modelList.splice(0); - } - - // If we pushed these directly onto the list, it would cause a change event for each model. - // So, we push them onto `tmp` first and then push everything at once, causing one atomic change event that contains all the models at once. - can.each(raw, function (rawPart) { - tmp.push(self.model(rawPart, xhr)); - }); - modelList.push.apply(modelList, tmp); - - // If there was other stuff on `instancesRawData`, let's transfer that onto `modelList` too. - if (!can.isArray(instancesRawData)) { - can.each(instancesRawData, function (val, prop) { - if (prop !== 'data') { - modelList.attr(prop, val); - } - }); - } - // Clean up the store on the next turn of the event loop. (`this` is a model constructor.) - setTimeout(can.proxy(this._clean, this), 1); - return modelList; - }, - // ## model - // A function that, when handed a plain object, turns it into a model. - model: function (attributes, oldModel, xhr) { - // If there're no properties, there can be no model. - if (!attributes) { - return; - } - - // If this object knows how to serialize, parse, or access itself, we'll use that instead. - if (typeof attributes.serialize === 'function') { - attributes = attributes.serialize(); - } else { - attributes = this.parseModel(attributes, xhr); - } - - var id = attributes[this.id]; - // Models from the store always have priority - // 0 is a valid ID. - if((id || id === 0) && this.store[id]) { - oldModel = this.store[id]; - } - - var model = oldModel && can.isFunction(oldModel.attr) ? - // If this model is in the store already, just update it. - oldModel.attr(attributes, this.removeAttr || false) : - // Otherwise, we need a new model. - new this(attributes); - - return model; - } - }, - - // ## makeParser - // This object describes how to take the data from an AJAX request and prepare it for `models` and `model`. - // These functions are meant to be overwritten (if necessary) in an extended model constructor. - makeParser = { - parseModel: function (prop) { - return function (attributes) { - return prop ? can.getObject(prop, attributes) : attributes; - }; - }, - parseModels: function (prop) { - return function (attributes) { - if(can.isArray(attributes)) { - return attributes; - } - - prop = prop || 'data'; - - var result = can.getObject(prop, attributes); - if(!can.isArray(result)) { - throw new Error('Could not get any raw data while converting using .models'); - } - return result; - }; - } - }, - - // ## ajaxMethods - // This object describes how to make an AJAX request for each ajax method (`create`, `update`, etc.) - // Each AJAX method is an object in `ajaxMethods` and can have the following properties: - // - // - `url`: Which property on the model contains the default URL for this method. - // - `type`: The default HTTP request method. - // - `data`: A method that takes the arguments from `makeRequest` (see above) and returns a data object for use in the AJAX call. - ajaxMethods = { - create: { - url: "_shortName", - type: "post" - }, - update: { - // ## update.data - data: function (id, attrs) { - attrs = attrs || {}; - - // `this.id` is the property that represents the ID (and is usually `"id"`). - var identity = this.id; - - // If the value of the property being used as the ID changed, - // indicate that in the request and replace the current ID property. - if (attrs[identity] && attrs[identity] !== id) { - attrs["new" + can.capitalize(id)] = attrs[identity]; - delete attrs[identity]; - } - attrs[identity] = id; - - return attrs; - }, - type: "put" - }, - destroy: { - type: 'delete', - // ## destroy.data - data: function (id, attrs) { - attrs = attrs || {}; - // `this.id` is the property that represents the ID (and is usually `"id"`). - attrs.id = attrs[this.id] = id; - return attrs; - } - }, - findAll: { - url: "_shortName" - }, - findOne: {} - }, - // ## ajaxMaker - // Takes a method defined just above and a string that describes how to call that method - // and makes a function that calls that method with the given data. - // - // - `ajaxMethod`: The object defined above in `ajaxMethods`. - // - `str`: The string the configuration provided (such as `"/recipes.json"` for a `findAll` call). - ajaxMaker = function (ajaxMethod, str) { - return function (data) { - data = ajaxMethod.data ? - // If the AJAX method mentioned above has its own way of getting `data`, use that. - ajaxMethod.data.apply(this, arguments) : - // Otherwise, just use the data passed in. - data; - - // Make the AJAX call with the URL, data, and type indicated by the proper `ajaxMethod` above. - return ajax(str || this[ajaxMethod.url || "_url"], data, ajaxMethod.type || "get"); - }; - }, - // ## createURLFromResource - // For each of the names (create, update, destroy, findOne, and findAll) use the - // URL provided by the `resource` property. For example: - // - // ToDo = can.Model.extend({ - // resource: "/todos" - // }, {}); - // - // Will create a can.Model that is identical to: - // - // ToDo = can.Model.extend({ - // findAll: "GET /todos", - // findOne: "GET /todos/{id}", - // create: "POST /todos", - // update: "PUT /todos/{id}", - // destroy: "DELETE /todos/{id}" - // },{}); - // - // - `model`: the can.Model that has the resource property - // - `method`: a property from the ajaxMethod object - createURLFromResource = function(model, name) { - if (!model.resource) { return; } - - var resource = model.resource.replace(/\/+$/, ""); - if (name === "findAll" || name === "create") { - return resource; - } else { - return resource + "/{" + model.id + "}"; - } - }; - -// # can.Model -// A can.Map that connects to a RESTful interface. -/** @static */ -can.Model = can.Map.extend({ - // `fullName` identifies the model type in debugging. - fullName: "can.Model", - _reqs: 0, - // ## can.Model.setup - setup: function (base, fullName, staticProps, protoProps) { - // Assume `fullName` wasn't passed. (`can.Model.extend({ ... }, { ... })`) - // This is pretty usual. - if (typeof fullName !== "string") { - protoProps = staticProps; - staticProps = fullName; - } - // Assume no static properties were passed. (`can.Model.extend({ ... })`) - // This is really unusual for a model though, since there's so much configuration. - if (!protoProps) { - //!steal-remove-start - can.dev.warn("can/model/model.js: can.Model extended without static properties."); - //!steal-remove-end - protoProps = staticProps; - } - - // Create the model store here, in case someone wants to use can.Model without inheriting from it. - this.store = {}; - - can.Map.setup.apply(this, arguments); - if (!can.Model) { - return; - } - - // `List` is just a regular can.Model.List that knows what kind of Model it's hooked up to. - if(staticProps && staticProps.List) { - this.List = staticProps.List; - this.List.Map = this; - } else { - this.List = base.List.extend({ - Map: this - }, {}); - } - - var self = this, - clean = can.proxy(this._clean, self); - - // Go through `ajaxMethods` and set up static methods according to their configurations. - can.each(ajaxMethods, function (method, name) { - // Check the configuration for this ajaxMethod. - // If the configuration isn't a function, it should be a string (like `"GET /endpoint"`) - // or an object like `{url: "/endpoint", type: 'GET'}`. - - //if we have a string(like `"GET /endpoint"`) or an object(ajaxSettings) set in the static definition(not inherited), - //convert it to a function. - if(staticProps && staticProps[name] && (typeof staticProps[name] === 'string' || typeof staticProps[name] === 'object')) { - self[name] = ajaxMaker(method, staticProps[name]); - } - //if we have a resource property set in the static definition, but check if function exists already - else if(staticProps && staticProps.resource && !can.isFunction(staticProps[name])) { - self[name] = ajaxMaker(method, createURLFromResource(self, name)); - } - - // There may also be a "maker" function (like `makeFindAll`) that alters the behavior of acting upon models - // by changing when and how the function we just made with `ajaxMaker` gets called. - // For example, you might cache responses and only make a call when you don't have a cached response. - if (self["make" + can.capitalize(name)]) { - // Use the "maker" function to make the new "ajaxMethod" function. - var newMethod = self["make" + can.capitalize(name)](self[name]); - // Replace the "ajaxMethod" function in the configuration with the new one. - // (`_overwrite` just overwrites a property in a given Construct.) - can.Construct._overwrite(self, base, name, function () { - // Increment the numer of requests... - can.Model._reqs++; - // ...make the AJAX call (and whatever else you're doing)... - var def = newMethod.apply(this, arguments); - // ...and clean up the store. - var then = def.then(clean, clean); - // Pass along `abort` so you can still abort the AJAX call. - then.abort = def.abort; - - return then; - }); - } - }); - - var hasCustomConverter = {}; - - // Set up `models` and `model`. - can.each(converters, function(converter, name) { - var parseName = "parse" + can.capitalize(name), - dataProperty = (staticProps && staticProps[name]) || self[name]; - - // For legacy e.g. models: 'someProperty' we set the `parseModel(s)` property - // to the given string and set .model(s) to the original converter - if(typeof dataProperty === 'string') { - self[parseName] = dataProperty; - can.Construct._overwrite(self, base, name, converter); - } else if((staticProps && staticProps[name])) { - hasCustomConverter[parseName] = true; - } - }); - - // Sets up parseModel(s) - can.each(makeParser, function(maker, parseName) { - var prop = (staticProps && staticProps[parseName]) || self[parseName]; - // e.g. parseModels: 'someProperty' make a default parseModel(s) - if(typeof prop === 'string') { - can.Construct._overwrite(self, base, parseName, maker(prop)); - } else if( (!staticProps || !can.isFunction(staticProps[parseName])) && !self[parseName] ) { - var madeParser = maker(); - madeParser.useModelConverter = hasCustomConverter[parseName]; - // Add a default parseModel(s) if there is none - can.Construct._overwrite(self, base, parseName, madeParser); - } - }); - - // Make sure we have a unique name for this Model. - if (self.fullName === "can.Model" || !self.fullName) { - self.fullName = "Model" + (++modelNum); - } - - can.Model._reqs = 0; - this._url = this._shortName + "/{" + this.id + "}"; - }, - _ajax: ajaxMaker, - _makeRequest: makeRequest, - // ## can.Model._clean - // `_clean` cleans up the model store after a request happens. - _clean: function () { - can.Model._reqs--; - // Don't clean up unless we have no pending requests. - if (!can.Model._reqs) { - for (var id in this.store) { - // Delete all items in the store without any event bindings. - if (!this.store[id]._bindings) { - delete this.store[id]; - } - } - } - return arguments[0]; - }, - models: converters.models, - model: converters.model - }, - /** @prototype */ - { - // ## can.Model#setup - setup: function (attrs) { - // Try to add things as early as possible to the store (#457). - // This is the earliest possible moment, even before any properties are set. - var id = attrs && attrs[this.constructor.id]; - if (can.Model._reqs && id != null) { - this.constructor.store[id] = this; - } - can.Map.prototype.setup.apply(this, arguments); - }, - // ## can.Model#isNew - // Something is new if its ID is `null` or `undefined`. - isNew: function () { - var id = getId(this); - // 0 is a valid ID. - // TODO: Why not `return id === null || id === undefined;`? - return !(id || id === 0); // If `null` or `undefined` - }, - // ## can.Model#save - // `save` calls `create` or `update` as necessary, based on whether a model is new. - save: function (success, error) { - return makeRequest(this, this.isNew() ? 'create' : 'update', success, error); - }, - // ## can.Model#destroy - // Acts like can.Map.destroy but it also makes an AJAX call. - destroy: function (success, error) { - // If this model is new, don't make an AJAX call. - // Instead, we have to construct the Deferred ourselves and return it. - if (this.isNew()) { - var self = this; - var def = can.Deferred(); - def.then(success, error); - - return def.done(function (data) { - self.destroyed(data); - }).resolve(self); - } - - // If it isn't new, though, go ahead and make a request. - return makeRequest(this, 'destroy', success, error, 'destroyed'); - }, - // ## can.Model#bind and can.Model#unbind - // These aren't actually implemented here, but their setup needs to be changed to account for the store. - _bindsetup: function () { - var modelInstance = this.___get(this.constructor.id); - if (modelInstance != null) { - this.constructor.store[modelInstance ] = this; - } - return can.Map.prototype._bindsetup.apply(this, arguments); - }, - _bindteardown: function () { - delete this.constructor.store[getId(this)]; - return can.Map.prototype._bindteardown.apply(this, arguments); - }, - // Change the behavior of `___set` to account for the store. - ___set: function (prop, val) { - can.Map.prototype.___set.call(this, prop, val); - // If we add or change the ID, update the store accordingly. - // TODO: shouldn't this also delete the record from the old ID in the store? - if (prop === this.constructor.id && this._bindings) { - this.constructor.store[getId(this)] = this; - } - } - }); - -// Returns a function that knows how to prepare data from `findAll` or `findOne` calls. -// `name` should be either `model` or `models`. -var makeGetterHandler = function (name) { - return function (data, readyState, xhr) { - return this[name](data, null, xhr); - }; -}, -// Handle data returned from `create`, `update`, and `destroy` calls. -createUpdateDestroyHandler = function (data) { - if(this.parseModel.useModelConverter) { - return this.model(data); - } - - return this.parseModel(data); -}; - -var responseHandlers = { - makeFindAll: makeGetterHandler("models"), - makeFindOne: makeGetterHandler("model"), - makeCreate: createUpdateDestroyHandler, - makeUpdate: createUpdateDestroyHandler, - makeDestroy: createUpdateDestroyHandler -}; - -// Go through the response handlers and make the actual "make" methods. -can.each(responseHandlers, function (method, name) { - can.Model[name] = function (oldMethod) { - return function () { - var args = can.makeArray(arguments), - // If args[1] is a function, we were only passed one argument before success and failure callbacks. - oldArgs = can.isFunction(args[1]) ? args.splice(0, 1) : args.splice(0, 2), - // Call the AJAX method (`findAll` or `update`, etc.) and pipe it through the response handler from above. - def = pipe(oldMethod.apply(this, oldArgs), this, method); - - def.then(args[0], args[1]); - return def; - }; - }; -}); - -// ## can.Model.created, can.Model.updated, and can.Model.destroyed -// Livecycle methods for models. -can.each([ - "created", - "updated", - "destroyed" -], function (funcName) { - // Each of these is pretty much the same, except for the events they trigger. - can.Model.prototype[funcName] = function (attrs) { - var self = this, - constructor = self.constructor; - - // Update attributes if attributes have been passed - if(attrs && typeof attrs === 'object') { - this.attr(can.isFunction(attrs.attr) ? attrs.attr() : attrs); - } - - // triggers change event that bubble's like - // handler( 'change','1.destroyed' ). This is used - // to remove items on destroyed from Model Lists. - // but there should be a better way. - can.dispatch.call(this, {type:funcName, target: this}, []); - - //!steal-remove-start - can.dev.log("Model.js - " + constructor.shortName + " " + funcName); - //!steal-remove-end - - // Call event on the instance's Class - can.dispatch.call(constructor, funcName, [this]); - }; -}); - - -// # can.Model.List -// Model Lists are just like `Map.List`s except that when their items are -// destroyed, they automatically get removed from the List. -var ML = can.Model.List = can.List.extend({ - // ## can.Model.List.setup - // On change or a nested named event, setup change bubbling. - // On any other type of event, setup "destroyed" bubbling. - _bubbleRule: function(eventName, list) { - var bubbleRules = can.List._bubbleRule(eventName, list); - bubbleRules.push('destroyed'); - return bubbleRules; - } -},{ - setup: function (params) { - // If there was a plain object passed to the List constructor, - // we use those as parameters for an initial findAll. - if (can.isPlainObject(params) && !can.isArray(params)) { - can.List.prototype.setup.apply(this); - this.replace(can.isDeferred(params) ? params : this.constructor.Map.findAll(params)); - } else { - // Otherwise, set up the list like normal. - can.List.prototype.setup.apply(this, arguments); - } - this.bind('destroyed', can.proxy(this._destroyed, this)); - }, - _destroyed: function (ev, attr) { - if (/\w+/.test(attr)) { - var index; - while((index = this.indexOf(ev.target)) > -1) { - this.splice(index, 1); - } - } - } -}); - -module.exports = exports = can.Model; +module.exports = can.Model = require('can-model'); diff --git a/model/model_list.md b/model/model_list.md deleted file mode 100644 index 5be3049abdf..00000000000 --- a/model/model_list.md +++ /dev/null @@ -1,157 +0,0 @@ -@constructor can.Model.List -@inherits can.List -@parent canjs -@download can/model -@test can/model/qunit.html - -@description A list connected to can.Model's CRUD abilities. - -@signature `new can.Model.List()` - -Create an empty model list. - -@signature `new can.Model.List( [models] )` - -Create a model list with the provided model instances. - -@param {Array.} [models] An array of [can.Model] instances -or Objects that will be converted to the list's [can.List.Map Map type]. - -@signature `new can.Model.List( deferred )` - -Create a model list with the results of `deferred`. - -@param {Deferred.Array.} deferred A promise that will -resolve to an array. Once the promise resolves, the `List` will have its -contents replaced as if `new can.Model.List(array)` had been called. - -@signature `new can.Model.List( params )` - -Create an initially empty model list, but use the model's [can.Model.findAll findAll] -to get a list of models and add it to this empty list. - -@param {Object} params Params that are passed to -the [can.List.Map Map property's] [can.Model.findAll findAll] method. - -@body - -## Use - -`can.Model.List` is a [can.List] associated with a [can.Model]. `can.Model.List`s -are just like [can.List] except they have a few super-powers: - - - They are returned by [can.Model.findAll findAll]. - - They automatically remove "destroyed" items. - - They can retrieve items from the server (similar to `findAll`). - -## Defining a model list - -When [can.Model] is extended, `can.Model.List` is automatically extended and set as that model's -[can.Model.static.List List property]. Typically, a `can.Model.List` is -defined for you. For example: - - Task = can.Model.extend({ - findAll: "/tasks" - },{}) - new Task.List instanceof can.Model.List //-> true - -This List type is returned by [can.Model.findAll findAll]: - - Task.findAll({}, function(tasks){ - tasks instanceof Task.List //-> true - }) - -The List's [can.List.Map Map] property points to the extended [can.Model]: - - Task = can.Model.extend({ - findAll: "/tasks" - },{}); - Task.List.Map //-> Task - -Defining custom `can.Model.Lists` allows you to extend lists with helper -functions for a list of a specific type. The following -adds the ability to retrieve the number of completed and remaining todos: - - Todo.List = Todo.List.extend({ - completed: function() { - var completed = 0; - this.each(function(i, todo) { - completed += todo.attr('complete') ? 1 : 0 - }) - return completed; - }, - remaining: function() { - return this.attr('length') - this.completed(); - } - }) - - Todo.findAll({}, function(todos) { - todos.completed() // -> 0 - todos.remaining() // -> 2 - }); - -## Creating a model list instance - -If you use [can.Model.findAll findAll], it calls back with and resolves to a model list: - - var def = Task.findAll({}, function(tasks){ - tasks instanceof Task.List //-> true - }); - - def.then(function(tasks){ - tasks instanceof Task.List //-> true - }) - -To create an empty model list yourself, use `new {model_name}.List()` like: - - var todos = new Todo.List(); - todos.attr("length") //-> 0 - -You can also pass an array to instantiate the list with the array data using -`new {model_name}.List(ARRAY)`. It can be either an array of models: - - var todo1 = new Todo( { name: "Do the dishes", id: 1 } ), - todo2 = new Todo( { name: "Wash floors", id: 2 } ) - var todos = new Todo.List( [todo1, todo2] ); - -...or an array of objects. If objects are provided, model list will convert -them to models. The following does the same thing as the previous example: - - var todos = new Todo.List( [ - { name: "Do the dishes", id: 1 }, - { name: "Wash floors", id: 2 } - ] ); - -If, instead of using an array, a model list is created with a plain -JavaScript object like: - - var todos = new Todo.List({due: "today"}); - -The object is assumed to be -parameters to the list's Model's [can.Model.findAll findAll] -method. An empty list will be returned, but `Todo.findAll` will -be called. The items it returns will be inserted into the -list. - - var todos = new Todo.List({due: "today"}); - todos.attr("length") //-> 0 - - todos.bind("length", function(){ - console.log("items added to the list") - }) - - -## Removing models from model list - -One advantage that `can.Model.List` has over a traditional `can.List` -is that when you destroy a model, if it is in that list, it will automatically -be removed from the list. - - // Listen for when something is removed from the todos list. - todos.bind("remove", function( ev, oldVals, indx ) { - console.log("todo"+indx+" removed") - }) - - todos.attr(0).destroy(); // console shows "todo 0 removed" - - diff --git a/model/model_test.js b/model/model_test.js index 1ddc3358b0a..6991f2a58e9 100644 --- a/model/model_test.js +++ b/model/model_test.js @@ -1,1744 +1 @@ -/* global Person: true */ -/* global CustomId: true */ -/* global Test: true */ -/* global ObjectDef: true */ -/* global Abortion: true */ -/* global Storage: true */ -/* global Base: true */ -/* global Product: true */ -/* global Organisation: true */ -/* global My: true */ -require('can/model/model'); -require('can/util/fixture/fixture'); -require('can/test/test'); -require('steal-qunit'); - -QUnit.module('can/model', { - setup: function () {} -}); - -test('shadowed id', function () { - var MyModel = can.Model.extend({ - id: 'foo' - }, { - foo: function () { - return this.attr('foo'); - } - }); - var newModel = new MyModel({}); - ok(newModel.isNew(), 'new model is isNew'); - var oldModel = new MyModel({ - foo: 'bar' - }); - ok(!oldModel.isNew(), 'old model is not new'); - equal(oldModel.foo(), 'bar', 'method can coexist with attribute'); -}); -test('findAll deferred', function () { - can.Model('Person', { - findAll: function (params, success, error) { - var self = this; - return can.ajax({ - url: '/people', - data: params, - fixture: can.test.fixture('model/test/people.json'), - dataType: 'json' - }) - .pipe(function (data) { - return self.models(data); - }); - } - }, {}); - stop(); - var people = Person.findAll({}); - people.then(function (people) { - equal(people.length, 1, 'we got a person back'); - equal(people[0].name, 'Justin', 'Got a name back'); - equal(people[0].constructor.shortName, 'Person', 'got a class back'); - start(); - }); -}); -test('findAll rejects non-array (#384)', function () { - var Person = can.Model.extend({ - findAll: function (params, success, error) { - var dfd = can.Deferred(); - setTimeout(function () { - dfd.resolve({ - stuff: {} - }); - }, 100); - return dfd; - } - }, {}); - stop(); - Person.findAll({}) - .then(function () { - ok(false, 'This should not succeed'); - }, function (err) { - ok(err instanceof Error, 'Got an error'); - equal(err.message, 'Could not get any raw data while converting using .models'); - start(); - }); -}); - -asyncTest('findAll deferred reject', function () { - // This test is automatically paused - function rejectDeferred(df) { - setTimeout(function () { - df.reject(); - }, 100); - } - - function resolveDeferred(df) { - setTimeout(function () { - df.resolve(); - }, 100); - } - can.Model('Person', { - findAll: function (params, success, error) { - var df = can.Deferred(); - if (params.resolve) { - resolveDeferred(df); - } else { - rejectDeferred(df); - } - return df; - } - }, {}); - var people_reject = Person.findAll({ - resolve: false - }); - var people_resolve = Person.findAll({ - resolve: true - }); - setTimeout(function () { - people_reject.done(function () { - ok(false, 'This deferred should be rejected'); - }); - people_reject.fail(function () { - ok(true, 'The deferred is rejected'); - }); - people_resolve.done(function () { - ok(true, 'This deferred is resolved'); - }); - people_resolve.fail(function () { - ok(false, 'The deferred should be resolved'); - }); - // continue the test - start(); - }, 200); -}); -if (window.jQuery) { - asyncTest('findAll abort', function () { - expect(4); - var df; - can.Model('Person', { - findAll: function (params, success, error) { - df = can.Deferred(); - df.then(function () { - ok(!params.abort, 'not aborted'); - }, function () { - ok(params.abort, 'aborted'); - }); - return df.promise({ - abort: function () { - df.reject(); - } - }); - } - }, {}); - Person.findAll({ - abort: false - }) - .done(function () { - ok(true, 'resolved'); - }); - var resolveDf = df; - var abortPromise = Person.findAll({ - abort: true - }) - .fail(function () { - ok(true, 'failed'); - }); - setTimeout(function () { - resolveDf.resolve(); - abortPromise.abort(); - // continue the test - start(); - }, 200); - }); -} -test('findOne deferred', function () { - if (window.jQuery) { - can.Model('Person', { - findOne: function (params, success, error) { - var self = this; - return can.ajax({ - url: '/people/5', - data: params, - fixture: can.test.fixture('model/test/person.json'), - dataType: 'json' - }) - .pipe(function (data) { - return self.model(data); - }); - } - }, {}); - } else { - can.Model('Person', { - findOne: can.test.fixture('model/test/person.json') - }, {}); - } - stop(); - var person = Person.findOne({}); - person.then(function (person) { - equal(person.name, 'Justin', 'Got a name back'); - equal(person.constructor.shortName, 'Person', 'got a class back'); - start(); - }); -}); -test('save deferred', function () { - can.Model('Person', { - create: function (attrs, success, error) { - return can.ajax({ - url: '/people', - data: attrs, - type: 'post', - dataType: 'json', - fixture: function () { - return { - id: 5 - }; - }, - success: success - }); - } - }, {}); - var person = new Person({ - name: 'Justin' - }), - personD = person.save(); - stop(); - personD.then(function (person) { - start(); - equal(person.id, 5, 'we got an id'); - }); -}); -test('update deferred', function () { - can.Model('Person', { - update: function (id, attrs, success, error) { - return can.ajax({ - url: '/people/' + id, - data: attrs, - type: 'post', - dataType: 'json', - fixture: function () { - return { - thing: 'er' - }; - }, - success: success - }); - } - }, {}); - var person = new Person({ - name: 'Justin', - id: 5 - }), - personD = person.save(); - stop(); - personD.then(function (person) { - start(); - equal(person.thing, 'er', 'we got updated'); - }); -}); -test('destroy deferred', function () { - can.Model('Person', { - destroy: function (id, success, error) { - return can.ajax({ - url: '/people/' + id, - type: 'post', - dataType: 'json', - fixture: function () { - return { - thing: 'er' - }; - }, - success: success - }); - } - }, {}); - var person = new Person({ - name: 'Justin', - id: 5 - }), - personD = person.destroy(); - stop(); - personD.then(function (person) { - start(); - equal(person.thing, 'er', 'we got destroyed'); - }); -}); -test('models', function () { - can.Model('Person', { - prettyName: function () { - return 'Mr. ' + this.name; - } - }); - var people = Person.models([{ - id: 1, - name: 'Justin' - }]); - equal(people[0].prettyName(), 'Mr. Justin', 'wraps wrapping works'); -}); -test('.models with custom id', function () { - can.Model('CustomId', { - findAll: can.test.path('model/test/customids.json'), - id: '_id' - }, { - getName: function () { - return this.name; - } - }); - var results = CustomId.models([{ - '_id': 1, - 'name': 'Justin' - }, { - '_id': 2, - 'name': 'Brian' - }]); - equal(results.length, 2, 'Got two items back'); - equal(results[0].name, 'Justin', 'First name right'); - equal(results[1].name, 'Brian', 'Second name right'); -}); -/* - test("async setters", function(){ - - - can.Model("Test.AsyncModel",{ - setName : function(newVal, success, error){ - - - setTimeout(function(){ - success(newVal) - }, 100) - } - }); - - var model = new Test.AsyncModel({ - name : "justin" - }); - equal(model.name, "justin","property set right away") - - //makes model think it is no longer new - model.id = 1; - - var count = 0; - - model.bind('name', function(ev, newName){ - equal(newName, "Brian",'new name'); - equal(++count, 1, "called once"); - ok(new Date() - now > 0, "time passed") - start(); - }) - var now = new Date(); - model.attr('name',"Brian"); - stop(); - })*/ -test('binding', 2, function () { - can.Model('Person'); - var inst = new Person({ - foo: 'bar' - }); - inst.bind('foo', function (ev, val) { - ok(true, 'updated'); - equal(val, 'baz', 'values match'); - }); - inst.attr('foo', 'baz'); -}); -test('auto methods', function () { - //turn off fixtures - can.fixture.on = false; - var School = can.Model.extend('Jquery.Model.Models.School', { - findAll: can.test.path('model/test/{type}.json'), - findOne: can.test.path('model/test/{id}.json'), - create: 'GET ' + can.test.path('model/test/create.json'), - update: 'GET ' + can.test.path('model/test/update{id}.json') - }, {}); - stop(); - School.findAll({ - type: 'schools' - }, function (schools) { - ok(schools, 'findAll Got some data back'); - equal(schools[0].constructor.shortName, 'School', 'there are schools'); - School.findOne({ - id: '4' - }, function (school) { - ok(school, 'findOne Got some data back'); - equal(school.constructor.shortName, 'School', 'a single school'); - new School({ - name: 'Highland' - }) - .save(function (school) { - equal(school.name, 'Highland', 'create gets the right name'); - school.attr({ - name: 'LHS' - }) - .save(function () { - start(); - equal(school.name, 'LHS', 'create gets the right name'); - can.fixture.on = true; - }); - }); - }); - }); -}); -test('isNew', function () { - var p = new Person(); - ok(p.isNew(), 'nothing provided is new'); - var p2 = new Person({ - id: null - }); - ok(p2.isNew(), 'null id is new'); - var p3 = new Person({ - id: 0 - }); - ok(!p3.isNew(), '0 is not new'); -}); -test('findAll string', function () { - can.fixture.on = false; - can.Model('Test.Thing', { - findAll: can.test.path('model/test/findAll.json') + '' - }, {}); - stop(); - Test.Thing.findAll({}, function (things) { - equal(things.length, 1, 'got an array'); - equal(things[0].id, 1, 'an array of things'); - start(); - can.fixture.on = true; - }); -}); - -test('Model events', function () { - expect(12); - var order = 0; - can.Model('Test.Event', { - create: function (attrs) { - var def = new can.Deferred(); - def.resolve({ - id: 1 - }); - return def; - }, - update: function (id, attrs, success) { - var def = new can.Deferred(); - def.resolve(attrs); - return def; - }, - destroy: function (id, success) { - var def = new can.Deferred(); - def.resolve({}); - return def; - } - }, {}); - stop(); - Test.Event.bind('created', function (ev, passedItem) { - ok(this === Test.Event, 'got model'); - ok(passedItem === item, 'got instance'); - equal(++order, 1, 'order'); - passedItem.save(); - }) - .bind('updated', function (ev, passedItem) { - equal(++order, 2, 'order'); - ok(this === Test.Event, 'got model'); - ok(passedItem === item, 'got instance'); - passedItem.destroy(); - }) - .bind('destroyed', function (ev, passedItem) { - equal(++order, 3, 'order'); - ok(this === Test.Event, 'got model'); - ok(passedItem === item, 'got instance'); - start(); - }); - var item = new Test.Event(); - item.bind('created', function () { - ok(true, 'created'); - }) - .bind('updated', function () { - ok(true, 'updated'); - }) - .bind('destroyed', function () { - ok(true, 'destroyed'); - }); - item.save(); -}); - -test('removeAttr test', function () { - can.Model('Person'); - var person = new Person({ - foo: 'bar' - }); - equal(person.foo, 'bar', 'property set'); - person.removeAttr('foo'); - equal(person.foo, undefined, 'property removed'); - var attrs = person.attr(); - equal(attrs.foo, undefined, 'attrs removed'); -}); -test('save error args', function () { - var Foo = can.Model.extend('Testin.Models.Foo', { - create: '/testinmodelsfoos.json' - }, {}); - var st = '{type: "unauthorized"}'; - can.fixture('/testinmodelsfoos.json', function (request, response) { - response(401, st); - }); - stop(); - new Foo({}) - .save(function () { - ok(false, 'success should not be called'); - start(); - }, function (jQXHR) { - ok(true, 'error called'); - ok(jQXHR.getResponseHeader, 'jQXHR object'); - start(); - }); -}); -test('object definitions', function () { - can.Model('ObjectDef', { - findAll: { - url: '/test/place', - dataType: 'json' - }, - findOne: { - url: '/objectdef/{id}', - timeout: 1000 - }, - create: {}, - update: {}, - destroy: {} - }, {}); - can.fixture('GET /objectdef/{id}', function (original) { - equal(original.timeout, 1000, 'timeout set'); - return { - yes: true - }; - }); - can.fixture('GET /test/place', function (original) { - return [original.data]; - }); - stop(); - ObjectDef.findOne({ - id: 5 - }, function () { - start(); - }); - stop(); - // Do find all, pass some attrs - ObjectDef.findAll({ - start: 0, - count: 10, - myflag: 1 - }, function (data) { - start(); - equal(data[0].myflag, 1, 'my flag set'); - }); - stop(); - // Do find all with slightly different attrs than before, - // and notice when leaving one out the other is still there - ObjectDef.findAll({ - start: 0, - count: 10 - }, function (data) { - start(); - equal(data[0].myflag, undefined, 'my flag is undefined'); - }); -}); -test('aborting create update and destroy', function () { - stop(); - var delay = can.fixture.delay; - can.fixture.delay = 1000; - can.fixture('POST /abort', function () { - ok(false, 'we should not be calling the fixture'); - return {}; - }); - can.Model('Abortion', { - create: 'POST /abort', - update: 'POST /abort', - destroy: 'POST /abort' - }, {}); - var deferred = new Abortion({ - name: 'foo' - }) - .save(function () { - ok(false, 'success create'); - start(); - }, function () { - ok(true, 'create error called'); - deferred = new Abortion({ - name: 'foo', - id: 5 - }) - .save(function () { - ok(false, 'save called'); - start(); - }, function () { - ok(true, 'error called in update'); - deferred = new Abortion({ - name: 'foo', - id: 5 - }) - .destroy(function () {}, function () { - ok(true, 'destroy error called'); - can.fixture.delay = delay; - start(); - }); - setTimeout(function () { - deferred.abort(); - }, 10); - }); - setTimeout(function () { - deferred.abort(); - }, 10); - }); - setTimeout(function () { - deferred.abort(); - }, 10); -}); -test('store binding', function () { - can.Model('Storage'); - var s = new Storage({ - id: 1, - thing: { - foo: 'bar' - } - }); - ok(!Storage.store[1], 'not stored'); - var func = function () {}; - s.bind('foo', func); - ok(Storage.store[1], 'stored'); - s.unbind('foo', func); - ok(!Storage.store[1], 'not stored'); - var s2 = new Storage({}); - s2.bind('foo', func); - s2.attr('id', 5); - ok(Storage.store[5], 'stored'); - s2.unbind('foo', func); - ok(!Storage.store[5], 'not stored'); -}); -test('store ajax binding', function () { - var Guy = can.Model.extend({ - findAll: '/guys', - findOne: '/guy/{id}' - }, {}); - can.fixture('GET /guys', function () { - return [{ - id: 1 - }]; - }); - can.fixture('GET /guy/{id}', function () { - return { - id: 1 - }; - }); - stop(); - can.when(Guy.findOne({ - id: 1 - }), Guy.findAll()) - .then(function (guyRes, guysRes2) { - equal(guyRes.id, 1, 'got a guy id 1 back'); - equal(guysRes2[0].id, 1, 'got guys w/ id 1 back'); - ok(guyRes === guysRes2[0], 'guys are the same'); - // check the store is empty - setTimeout(function () { - var id; - start(); - for (id in Guy.store) { - ok(false, 'there should be nothing in the store'); - } - }, 1); - }); -}); -test('store instance updates', function () { - var Guy, updateCount; - Guy = can.Model.extend({ - findAll: 'GET /guys' - }, {}); - updateCount = 0; - can.fixture('GET /guys', function () { - var guys = [{ - id: 1, - updateCount: updateCount, - nested: { - count: updateCount - } - }]; - updateCount++; - return guys; - }); - stop(); - Guy.findAll({}, function (guys) { - start(); - guys[0].bind('updated', function () {}); - ok(Guy.store[1], 'instance stored'); - equal(Guy.store[1].updateCount, 0, 'updateCount is 0'); - equal(Guy.store[1].nested.count, 0, 'nested.count is 0'); - }); - Guy.findAll({}, function (guys) { - equal(Guy.store[1].updateCount, 1, 'updateCount is 1'); - equal(Guy.store[1].nested.count, 1, 'nested.count is 1'); - }); -}); -/* - test("store instance update removed fields", function(){ -var Guy, updateCount, remove; - -Guy = can.Model.extend({ - findAll : 'GET /guys' -},{}); -remove = false; - -can.fixture("GET /guys", function(){ - var guys = [{id: 1, name: 'mikey', age: 35, likes: ['soccer', 'fantasy baseball', 'js', 'zelda'], dislikes: ['backbone', 'errors']}]; - if(remove) { - delete guys[0].name; - guys[0].likes = []; - delete guys[0].dislikes; - } - remove = true; - return guys; -}); -stop(); -Guy.findAll({}, function(guys){ - start(); - guys[0].bind('updated', function(){}); - ok(Guy.store[1], 'instance stored'); - equal(Guy.store[1].name, 'mikey', 'name is mikey') - equal(Guy.store[1].likes.length, 4, 'mikey has 4 likes') - equal(Guy.store[1].dislikes.length, 2, 'mikey has 2 dislikes') -}) -Guy.findAll({}, function(guys){ - equal(Guy.store[1].name, undefined, 'name is undefined') - equal(Guy.store[1].likes.length, 0, 'no likes') - equal(Guy.store[1].dislikes, undefined, 'dislikes removed') -}) - -}) - */ -test('templated destroy', function () { - var MyModel = can.Model.extend({ - destroy: '/destroyplace/{id}' - }, {}); - can.fixture('/destroyplace/{id}', function (original) { - ok(true, 'fixture called'); - equal(original.url, '/destroyplace/5', 'urls match'); - return {}; - }); - stop(); - new MyModel({ - id: 5 - }) - .destroy(function () { - start(); - }); - can.fixture('/product/{id}', function (original) { - equal(original.data.id, 9001, 'Changed ID is correctly set.'); - start(); - return {}; - }); - Base = can.Model.extend({ - id: '_id' - }, {}); - Product = Base({ - destroy: 'DELETE /product/{id}' - }, {}); - new Product({ - _id: 9001 - }) - .destroy(); - stop(); -}); -test('extended templated destroy', function () { - var MyModel = can.Model({ - destroy: '/destroyplace/{attr1}/{attr2}/{id}' - }, {}); - can.fixture('/destroyplace/{attr1}/{attr2}/{id}', function (original) { - ok(true, 'fixture called'); - equal(original.url, '/destroyplace/foo/bar/5', 'urls match'); - return {}; - }); - stop(); - new MyModel({ - id: 5, - attr1: 'foo', - attr2: 'bar' - }) - .destroy(function () { - start(); - }); - can.fixture('/product/{attr3}/{id}', function (original) { - equal(original.data.id, 9001, 'Changed ID is correctly set.'); - start(); - return {}; - }); - Base = can.Model({ - id: '_id' - }, {}); - Product = Base({ - destroy: 'DELETE /product/{attr3}/{id}' - }, {}); - new Product({ - _id: 9001, - attr3: 'great' - }) - .destroy(); - stop(); -}); -test('overwrite makeFindAll', function () { - var store = {}; - var LocalModel = can.Model.extend({ - makeFindOne: function (findOne) { - return function (params, success, error) { - var def = new can.Deferred(), - data = store[params.id]; - def.then(success, error); - // make the ajax request right away - var findOneDeferred = findOne(params); - if (data) { - var instance = this.model(data); - findOneDeferred.then(function (data) { - instance.updated(data); - }, function () { - can.trigger(instance, 'error', data); - }); - def.resolve(instance); - } else { - findOneDeferred.then(can.proxy(function (data) { - var instance = this.model(data); - store[instance[this.id]] = data; - def.resolve(instance); - }, this), function (data) { - def.reject(data); - }); - } - return def; - }; - } - }, { - updated: function (attrs) { - can.Model.prototype.updated.apply(this, arguments); - store[this[this.constructor.id]] = this.serialize(); - } - }); - can.fixture('/food/{id}', function (settings) { - return count === 0 ? { - id: settings.data.id, - name: 'hot dog' - } : { - id: settings.data.id, - name: 'ice water' - }; - }); - var Food = LocalModel({ - findOne: '/food/{id}' - }, {}); - stop(); - var count = 0; - Food.findOne({ - id: 1 - }, function (food) { - count = 1; - ok(true, 'empty findOne called back'); - food.bind('name', function () { - ok(true, 'name changed'); - equal(count, 2, 'after last find one'); - equal(this.name, 'ice water'); - start(); - }); - Food.findOne({ - id: 1 - }, function (food2) { - count = 2; - ok(food2 === food, 'same instances'); - equal(food2.name, 'hot dog'); - }); - }); -}); -test('inheriting unique model names', function () { - var Foo = can.Model.extend({}); - var Bar = can.Model.extend({}); - ok(Foo.fullName !== Bar.fullName, 'fullNames not the same'); -}); -test('model list attr', function () { - can.Model('Person', {}, {}); - var list1 = new Person.List(), - list2 = new Person.List([ - new Person({ - id: 1 - }), - new Person({ - id: 2 - }) - ]); - equal(list1.length, 0, 'Initial empty list has length of 0'); - list1.attr(list2); - equal(list1.length, 2, 'Merging using attr yields length of 2'); -}); -test('destroying a model impact the right list', function () { - can.Model('Person', { - destroy: function (id, success) { - var def = new can.Deferred(); - def.resolve({}); - return def; - } - }, {}); - can.Model('Organisation', { - destroy: function (id, success) { - var def = new can.Deferred(); - def.resolve({}); - return def; - } - }, {}); - var people = new Person.List([ - new Person({ - id: 1 - }), - new Person({ - id: 2 - }) - ]), - orgs = new Organisation.List([ - new Organisation({ - id: 1 - }), - new Organisation({ - id: 2 - }) - ]); - // you must be bound to the list to get this - people.bind('length', function () {}); - orgs.bind('length', function () {}); - // set each person to have an organization - people[0].attr('organisation', orgs[0]); - people[1].attr('organisation', orgs[1]); - equal(people.length, 2, 'Initial Person.List has length of 2'); - equal(orgs.length, 2, 'Initial Organisation.List has length of 2'); - orgs[0].destroy(); - equal(people.length, 2, 'After destroying orgs[0] Person.List has length of 2'); - equal(orgs.length, 1, 'After destroying orgs[0] Organisation.List has length of 1'); -}); -test('uses attr with isNew', function () { - // TODO this does not seem to be consistent expect(2); - var old = can.__observe; - can.__observe = function (object, attribute) { - if (attribute === 'id') { - ok(true, 'used attr'); - } - }; - var m = new can.Model({ - id: 4 - }); - m.isNew(); - can.__observe = old; -}); -test('extends defaults by calling base method', function () { - var M1 = can.Model.extend({ - defaults: { - foo: 'bar' - } - }, {}); - var M2 = M1({}); - equal(M2.defaults.foo, 'bar'); -}); -test('.models updates existing list if passed', 4, function () { - var Model = can.Model.extend({}); - var list = Model.models([{ - id: 1, - name: 'first' - }, { - id: 2, - name: 'second' - }]); - list.bind('add', function (ev, newData) { - equal(newData.length, 3, 'Got all new items at once'); - }); - var newList = Model.models([{ - id: 3, - name: 'third' - }, { - id: 4, - name: 'fourth' - }, { - id: 5, - name: 'fifth' - }], list); - equal(list, newList, 'Lists are the same'); - equal(newList.attr('length'), 3, 'List has new items'); - equal(list[0].name, 'third', 'New item is the first one'); -}); -test('calling destroy with unsaved model triggers destroyed event (#181)', function () { - var MyModel = can.Model.extend({}, {}), - newModel = new MyModel(), - list = new MyModel.List(), - deferred; - // you must bind to a list for this feature - list.bind('length', function () {}); - list.push(newModel); - equal(list.attr('length'), 1, 'List length as expected'); - deferred = newModel.destroy(); - ok(deferred, '.destroy returned a Deferred'); - equal(list.attr('length'), 0, 'Unsaved model removed from list'); - deferred.done(function (data) { - ok(data === newModel, 'Resolved with destroyed model as described in docs'); - }); -}); -test('model removeAttr (#245)', function () { - var MyModel = can.Model.extend({}), - model; - can.Model._reqs++; - // pretend it is live bound - model = MyModel.model({ - id: 0, - index: 2, - name: 'test' - }); - model = MyModel.model({ - id: 0, - name: 'text updated' - }); - equal(model.attr('name'), 'text updated', 'attribute updated'); - equal(model.attr('index'), 2, 'Index attribute still remains'); - MyModel = can.Model.extend({ - removeAttr: true - }, {}); - can.Model._reqs++; - // pretend it is live bound - model = MyModel.model({ - id: 0, - index: 2, - name: 'test' - }); - model = MyModel.model({ - id: 0, - name: 'text updated' - }); - equal(model.attr('name'), 'text updated', 'attribute updated'); - deepEqual(model.attr(), { - id: 0, - name: 'text updated' - }, 'Index attribute got removed'); -}); -test('.model on create and update (#301)', function () { - var MyModel = can.Model.extend({ - create: 'POST /todo', - update: 'PUT /todo', - model: function (data) { - return can.Model.model.call(this, data.item); - } - }, {}), - id = 0, - updateTime; - can.fixture('POST /todo', function (original, respondWith, settings) { - id++; - return { - item: can.extend(original.data, { - id: id - }) - }; - }); - can.fixture('PUT /todo', function (original, respondWith, settings) { - updateTime = new Date() - .getTime(); - return { - item: { - updatedAt: updateTime - } - }; - }); - stop(); - - MyModel.bind('created', function (ev, created) { - start(); - deepEqual(created.attr(), { - id: 1, - name: 'Dishes' - }, '.model works for create'); - }) - .bind('updated', function (ev, updated) { - start(); - deepEqual(updated.attr(), { - id: 1, - name: 'Laundry', - updatedAt: updateTime - }, '.model works for update'); - }); - var instance = new MyModel({ - name: 'Dishes' - }), - saveD = instance.save(); - stop(); - saveD.then(function () { - instance.attr('name', 'Laundry') - .save(); - }); -}); -test('List params uses findAll', function () { - stop(); - can.fixture('/things', function (request) { - equal(request.data.param, 'value', 'params passed'); - return [{ - id: 1, - name: 'Thing One' - }]; - }); - var Model = can.Model.extend({ - findAll: '/things' - }, {}); - var items = new Model.List({ - param: 'value' - }); - items.bind('add', function (ev, items, index) { - equal(items[0].name, 'Thing One', 'items added'); - start(); - }); -}); - -test('destroy not calling callback for new instances (#403)', function () { - var Recipe = can.Model.extend({}, {}); - expect(1); - stop(); - new Recipe({ - name: 'mow grass' - }) - .destroy(function (recipe) { - ok(true, 'Destroy called'); - start(); - }); -}); - -test('.model should always serialize Observes (#444)', function () { - var ConceptualDuck = can.Model.extend({ - defaults: { - sayeth: 'Abstractly \'quack\'' - } - }, {}); - var ObserveableDuck = can.Map({}, {}); - equal('quack', ConceptualDuck.model(new ObserveableDuck({ - sayeth: 'quack' - })) - .sayeth); -}); - -test('string configurable model and models functions (#128)', function () { - var StrangeProp = can.Model.extend({ - model: 'foo', - models: 'bar' - }, {}); - var strangers = StrangeProp.models({ - bar: [{ - foo: { - id: 1, - name: 'one' - } - }, { - foo: { - id: 2, - name: 'two' - } - }] - }); - deepEqual(strangers.attr(), [{ - id: 1, - name: 'one' - }, { - id: 2, - name: 'two' - }]); -}); - -test('create deferred does not resolve to the same instance', function () { - var Todo = can.Model.extend({ - create: function () { - var def = new can.Deferred(); - def.resolve({ - id: 5 - }); - return def; - } - }, {}); - var handler = function () {}; - var t = new Todo({ - name: 'Justin' - }); - t.bind('name', handler); - var def = t.save(); - stop(); - def.then(function (todo) { - ok(todo === t, 'same instance'); - start(); - ok(Todo.store[5] === t, 'instance put in store'); - t.unbind('name', handler); - }); -}); - -test("Model#save should not replace attributes with their default values (#560)", function () { - - can.fixture("POST /person.json", function (request, response) { - - return { - createdAt: "now" - }; - }); - - var Person = can.Model.extend({ - update: 'POST /person.json' - }, { - name: 'Example name' - }); - - var person = new Person({ - id: 5, - name: 'Justin' - }), - personD = person.save(); - - stop(); - - personD.then(function (person) { - start(); - equal(person.name, "Justin", "Model name attribute value is preserved after save"); - - }); -}); - -test(".parseModel as function on create and update (#560)", function () { - var MyModel = can.Model.extend({ - create: 'POST /todo', - update: 'PUT /todo', - parseModel: function (data) { - return data.item; - } - }, { - aDefault: "foo" - }), - id = 0, - updateTime; - - can.fixture('POST /todo', function (original, respondWith, settings) { - id++; - return { - item: can.extend(original.data, { - id: id - }) - }; - }); - can.fixture('PUT /todo', function (original, respondWith, settings) { - updateTime = new Date() - .getTime(); - return { - item: { - updatedAt: updateTime - } - }; - }); - - stop(); - MyModel.bind('created', function (ev, created) { - start(); - deepEqual(created.attr(), { - id: 1, - name: 'Dishes', - aDefault: "bar" - }, '.model works for create'); - }) - .bind('updated', function (ev, updated) { - start(); - deepEqual(updated.attr(), { - id: 1, - name: 'Laundry', - updatedAt: updateTime - }, '.model works for update'); - }); - - var instance = new MyModel({ - name: 'Dishes', - aDefault: "bar" - }), - saveD = instance.save(); - - stop(); - saveD.then(function () { - instance.attr('name', 'Laundry'); - instance.removeAttr("aDefault"); - instance.save(); - }); - -}); - -test(".parseModel as string on create and update (#560)", function () { - var MyModel = can.Model.extend({ - create: 'POST /todo', - update: 'PUT /todo', - parseModel: "item" - }, { - aDefault: "foo" - }), - id = 0, - updateTime; - - can.fixture('POST /todo', function (original, respondWith, settings) { - id++; - return { - item: can.extend(original.data, { - id: id - }) - }; - }); - can.fixture('PUT /todo', function (original, respondWith, settings) { - updateTime = new Date() - .getTime(); - return { - item: { - updatedAt: updateTime - } - }; - }); - - stop(); - MyModel.bind('created', function (ev, created) { - start(); - deepEqual(created.attr(), { - id: 1, - name: 'Dishes', - aDefault: "bar" - }, '.model works for create'); - }) - .bind('updated', function (ev, updated) { - start(); - deepEqual(updated.attr(), { - id: 1, - name: 'Laundry', - updatedAt: updateTime - }, '.model works for update'); - }); - - var instance = new MyModel({ - name: 'Dishes', - aDefault: "bar" - }), - saveD = instance.save(); - - stop(); - saveD.then(function () { - instance.attr('name', 'Laundry'); - instance.removeAttr("aDefault"); - instance.save(); - }); - -}); - -test("parseModels and findAll", function () { - - var array = [{ - id: 1, - name: "first" - }]; - - can.fixture("/mymodels", function () { - return array; - }); - - var MyModel = can.Model.extend({ - findAll: "/mymodels", - parseModels: function (raw, xhr) { - - // only check this if jQuery because its deferreds can resolve with multiple args - if (window.jQuery) { - ok(xhr, "xhr object provided"); - } - equal(array, raw, "got passed raw data"); - return { - data: raw, - count: 1000 - }; - } - }, {}); - - stop(); - - MyModel.findAll({}, function (models) { - equal(models.count, 1000); - start(); - }); - -}); - -test("parseModels and parseModel and findAll", function () { - - can.fixture("/mymodels", function () { - return { - myModels: [{ - myModel: { - id: 1, - name: "first" - } - }] - }; - }); - - var MyModel = can.Model.extend({ - findAll: "/mymodels", - parseModels: "myModels", - parseModel: "myModel" - }, {}); - - stop(); - - MyModel.findAll({}, function (models) { - deepEqual(models.attr(), [{ - id: 1, - name: "first" - }], "correct models returned"); - start(); - }); - -}); - -test("findAll rejects when parseModels returns non-array data #1662", function(){ - can.fixture("/mymodels", function () { - return { - status: 'success', - message: '' - }; - }); - - var MyModel = can.Model.extend({ - findAll: "/mymodels", - parseModels: function(raw) { - raw.data = undefined; - return raw; - } - }, {}); - - stop(); - - MyModel.findAll({}) - .then(function(){ - ok(false, 'This should not succeed'); - start(); - }, function(err){ - ok(err instanceof Error, 'Got an error'); - equal(err.message, 'Could not get any raw data while converting using .models'); - start(); - }); -}); - -test("Nested lists", function(){ - var Teacher = can.Model.extend({}); - var teacher = new Teacher(); - teacher.attr("locations", [{id: 1, name: "Chicago"}, {id: 2, name: "LA"}]); - ok(!(teacher.attr('locations') instanceof Teacher.List), 'nested list is not an instance of Teacher.List'); - ok(!(teacher.attr('locations')[0] instanceof Teacher), 'nested map is not an instance of Teacher'); -}); - -test("#501 - resource definition - create", function() { - can.fixture("/foods", function() { - return []; - }); - - var FoodModel = can.Model.extend({ - resource: "/foods" - }, {}); - - stop(); - var steak = new FoodModel({name: "steak"}); - steak.save(function(food) { - equal(food.name, "steak", "create created the correct model"); - start(); - }); -}); - -test("#501 - resource definition - findAll", function() { - can.fixture("/drinks", function() { - return [{ - id: 1, - name: "coke" - }]; - }); - - var DrinkModel = can.Model.extend({ - resource: "/drinks" - }, {}); - - stop(); - DrinkModel.findAll({}, function(drinks) { - deepEqual(drinks.attr(), [{ - id: 1, - name: "coke" - }], "findAll returned the correct models"); - start(); - }); -}); - -test("#501 - resource definition - findOne", function() { - can.fixture("GET /clothes/{id}", function() { - return [{ - id: 1, - name: "pants" - }]; - }); - - var ClothingModel = can.Model.extend({ - resource: "/clothes" - }, {}); - - stop(); - ClothingModel.findOne({id: 1}, function(item) { - equal(item[0].name, "pants", "findOne returned the correct model"); - start(); - }); -}); - -test("#501 - resource definition - remove trailing slash(es)", function() { - can.fixture("POST /foods", function() { - return []; - }); - - var FoodModel = can.Model.extend({ - resource: "/foods//////" - }, {}); - - stop(); - var steak = new FoodModel({name: "steak"}); - steak.save(function(food) { - equal(food.name, "steak", "removed trailing '/' and created the correct model"); - start(); - }); -}); - -test("model list destroy after calling replace", function(){ - expect(2); - var map = new can.Model({name: "map1"}); - var map2 = new can.Model({name: "map2"}); - var list = new can.Model.List([map, map2]); - list.bind('destroyed', function(ev){ - ok(true, 'trigger destroyed'); - }); - can.trigger(map, 'destroyed'); - list.replace([map2]); - can.trigger(map2, 'destroyed'); -}); - -test("a model defined with a fullName has findAll working (#1034)", function(){ - var List = can.List.extend(); - - can.Model.extend("My.Model",{ - List: List - },{}); - - equal(List.Map, My.Model, "list's Map points to My.Model"); - -}); - -test("providing parseModels works", function(){ - var MyModel = can.Model.extend({ - parseModel: "modelData" - },{}); - - var data = MyModel.parseModel({modelData: {id: 1}}); - equal(data.id,1, "correctly used parseModel"); -}); - -test('#1089 - resource definition - inheritance', function() { - can.fixture('GET /things/{id}', function() { - return { id: 0, name: 'foo' }; - }); - - var Base = can.Model.extend(); - var Thing = Base.extend({ - resource: '/things' - }, {}); - - stop(); - Thing.findOne({ id: 0 }, function(thing) { - equal(thing.name, 'foo', 'found model in inherited model'); - start(); - }, function(e, msg) { - ok(false, msg); - start(); - }); -}); - -test('#1089 - resource definition - CRUD overrides', function() { - can.fixture('GET /foos/{id}', function() { - return { id: 0, name: 'foo' }; - }); - - can.fixture('POST /foos', function() { - return { id: 1 }; - }); - - can.fixture('PUT /foos/{id}', function() { - return { id: 1, updated: true }; - }); - - can.fixture('GET /bars', function() { - return [{}]; - }); - - var Thing = can.Model.extend({ - resource: '/foos', - findAll: 'GET /bars', - update: { - url: '/foos/{id}', - type: 'PUT' - }, - create: function() { - return can.ajax({ - url: '/foos', - type: 'POST' - }); - } - }, {}); - - var alldfd = Thing.findAll(), - onedfd = Thing.findOne({ id: 0 }), - postdfd = new Thing().save(); - - stop(); - can.when(alldfd, onedfd, postdfd) - .then(function(things, thing, newthing) { - equal(things.length, 1, 'findAll override called'); - equal(thing.name, 'foo', 'resource findOne called'); - equal(newthing.id, 1, 'post override called with function'); - - newthing.save(function(res) { - ok(res.updated, 'put override called with object'); - start(); - }); - }) - .fail(function() { - ok(false, 'override request failed'); - start(); - }); -}); - -test("findAll not called if List constructor argument is deferred (#1074)", function() { - var count = 0; - var Foo = can.Model.extend({ - findAll: function() { - count++; - return can.Deferred(); - } - }, {}); - new Foo.List(Foo.findAll()); - equal(count, 1, "findAll called only once."); -}); - -test("static methods do not get overwritten with resource property set (#1309)", function() { - var Base = can.Model.extend({ - resource: '/path', - findOne: function() { - var dfd = can.Deferred(); - dfd.resolve({ - text: 'Base findAll' - }); - return dfd; - } - }, {}); - - stop(); - - Base.findOne({}).then(function(model) { - ok(model instanceof Base); - deepEqual(model.attr(), { - text: 'Base findAll' - }); - start(); - }, function() { - ok(false, 'Failed handler should not be called.'); - }); -}); - -test("parseModels does not get overwritten if already implemented in base class (#1246, #1272)", 5, function() { - var Base = can.Model.extend({ - findOne: function() { - var dfd = can.Deferred(); - dfd.resolve({ - text: 'Base findOne' - }); - return dfd; - }, - parseModel: function(attributes) { - deepEqual(attributes, { - text: 'Base findOne' - }, 'parseModel called'); - attributes.parsed = true; - return attributes; - } - }, {}); - var Extended = Base.extend({}, {}); - - stop(); - - Extended.findOne({}).then(function(model) { - ok(model instanceof Base); - ok(model instanceof Extended); - deepEqual(model.attr(), { - text: 'Base findOne', - parsed: true - }); - start(); - }, function() { - ok(false, 'Failed handler should not be called.'); - }); - - var Third = Extended.extend({ - findOne: function() { - var dfd = can.Deferred(); - dfd.resolve({ - nested: { - text: 'Third findOne' - } - }); - return dfd; - }, - - parseModel: 'nested' - }, {}); - - Third.findOne({}).then(function(model) { - equal(model.attr('text'), 'Third findOne', 'correct findOne used'); - }); -}); - -test("Models with no id (undefined or null) are not placed in store (#1358)", function(){ - var MyStandardModel = can.Model.extend({}); - var MyCustomModel = can.Model.extend({id:"ID"}, {}); - - var myID = null; - var instanceNull = new MyStandardModel ({id:myID}); - var instanceUndefined = new MyStandardModel ({}); - var instanceCustom = new MyCustomModel({ID:myID}); - - - instanceNull.bind('change', function(){}); - instanceUndefined.bind('change', function(){}); - instanceCustom.bind('change', function(){}); - - - ok(typeof MyStandardModel.store[instanceNull.id] === "undefined", "Model should not be added to store when id is null"); - ok(typeof MyStandardModel.store[instanceUndefined.id] === "undefined", "Model should not be added to store when id is undefined"); - ok(typeof MyCustomModel.store[instanceCustom[instanceCustom.constructor.id]] === "undefined", "Model should not be added to store when id is null"); - -}); - -test("Models should be removed from store when instance.removeAttr('id') is called", function(){ - var Task = can.Model.extend({},{}); - var t1 = new Task({id: 1, name: "MyTask"}); - - t1.bind('change', function(){}); - ok(Task.store[t1.id].name === "MyTask", "Model should be in store"); - - t1.removeAttr("id"); - ok(typeof Task.store[t1.id] === "undefined", "Model should be removed from store when `id` is removed"); - -}); +require('can-model/model_test'); diff --git a/model/test.html b/model/test.html deleted file mode 100644 index 92d0b0ec588..00000000000 --- a/model/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/model - -
                diff --git a/model/test/4.json b/model/test/4.json deleted file mode 100644 index d05cb425e13..00000000000 --- a/model/test/4.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "id": 4, - "name" : "adler" -} \ No newline at end of file diff --git a/model/test/associations_test.js b/model/test/associations_test.js deleted file mode 100644 index ddfe22a4a7e..00000000000 --- a/model/test/associations_test.js +++ /dev/null @@ -1,96 +0,0 @@ -var MyTest; -module('jquery/model/associations', { - setup: function () { - $.Model('MyTest.Person', { - serialize: function () { - return 'My name is ' + this.name; - } - }); - $.Model('MyTest.Loan'); - $.Model('MyTest.Issue'); - $.Model('MyTest.Customer', { - attributes: { - person: 'MyTest.Person.model', - loans: 'MyTest.Loan.models', - issues: 'MyTest.Issue.models' - }, - update: function (id, attrs) { - return $.ajax({ - url: '/people/' + id, - data: attrs, - type: 'post', - dataType: 'json', - fixture: function () { - return [{ - loansAttr: attrs.loans, - personAttr: attrs.person - }]; - } - }); - } - }, {}); - } -}); -test('associations work', function () { - var c = new MyTest.Customer({ - id: 5, - person: { - id: 1, - name: 'Justin' - }, - issues: [], - loans: [{ - amount: 1000, - id: 2 - }, { - amount: 19999, - id: 3 - }] - }); - equal(c.person.name, 'Justin', 'association present'); - equal(c.person.Class, MyTest.Person, 'belongs to association typed'); - equal(c.issues.length, 0); - equal(c.loans.length, 2); - equal(c.loans[0].Class, MyTest.Loan); -}); -test('Model association serialize on save', function () { - var c = new MyTest.Customer({ - id: 5, - person: { - id: 1, - name: 'thecountofzero' - }, - issues: [], - loans: [] - }), - cSave = c.save(); - stop(); - cSave.then(function (customer) { - start(); - equal(customer.personAttr, 'My name is thecountofzero', 'serialization works'); - }); -}); -test('Model.List association serialize on save', function () { - var c = new MyTest.Customer({ - id: 5, - person: { - id: 1, - name: 'thecountofzero' - }, - issues: [], - loans: [{ - amount: 1000, - id: 2 - }, { - amount: 19999, - id: 3 - }] - }), - cSave = c.save(); - stop(); - cSave.then(function (customer) { - start(); - ok(true, 'called back'); - equal(customer.loansAttr.constructor, can.List, 'we get an observe list back'); - }); -}); diff --git a/model/test/create.json b/model/test/create.json deleted file mode 100644 index 734e4916c8f..00000000000 --- a/model/test/create.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "id": 4, - "name" : "Highland" -} \ No newline at end of file diff --git a/model/test/findAll.json b/model/test/findAll.json deleted file mode 100644 index 8dc242fa822..00000000000 --- a/model/test/findAll.json +++ /dev/null @@ -1,4 +0,0 @@ -[{ - "id" : 1, - "name" : "Thing 1" -}] diff --git a/model/test/people.json b/model/test/people.json deleted file mode 100644 index b5f86a2e0cf..00000000000 --- a/model/test/people.json +++ /dev/null @@ -1,4 +0,0 @@ -[{ - "id" : 5, - "name" : "Justin" -}] diff --git a/model/test/person.json b/model/test/person.json deleted file mode 100644 index ea58f4ea70d..00000000000 --- a/model/test/person.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "id" : 5, - "name" : "Justin" -} diff --git a/model/test/schools.json b/model/test/schools.json deleted file mode 100644 index 1b596fa06c0..00000000000 --- a/model/test/schools.json +++ /dev/null @@ -1,4 +0,0 @@ -[{ - "id": 1, - "name" : "adler" -}] diff --git a/model/test/update4.json b/model/test/update4.json deleted file mode 100644 index 9f705c98e33..00000000000 --- a/model/test/update4.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "id": 4, - "name" : "LHS" -} \ No newline at end of file diff --git a/package.backup.json b/package.backup.json new file mode 100644 index 00000000000..f23aa4adf83 --- /dev/null +++ b/package.backup.json @@ -0,0 +1,204 @@ +{ + "name": "can", + "title": "CanJS", + "description": "MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.", + "version": "3.0.0-pre.1", + "keywords": [ + "CanJS", + "DoneJS" + ], + "author": { + "name": "Bitovi", + "email": "contact@bitovi.com", + "web": "http://bitovi.com/" + }, + "homepage": "http://canjs.com/", + "repository": { + "type": "git", + "url": "git@github.com:canjs/canjs.git", + "github": "https://github.com/canjs/canjs" + }, + "dependencies": { + "can-simple-dom": "^0.3.0", + "jquery": ">=1.9.0 <1.12.0 || >=2.1.0 <2.3.0" + }, + "devDependencies": { + "bitovi-tools": "^0.1.1", + "browserify": "~8.1.0", + "connect": "^2.14.4", + "documentjs": "~0.3.2", + "grunt": "~0.4.0", + "grunt-banner": "^0.3.1", + "grunt-cli": "~0.1.7", + "grunt-contrib-clean": "~0.5.0", + "grunt-contrib-connect": "~0.3.0", + "grunt-contrib-jshint": "^1.0.0", + "grunt-contrib-uglify": "~0.2.1", + "grunt-docco2": "git://github.com/shcarrico/grunt-docco.git#e4de54886ed5c421b2e26e7a2aeba1f73e889733", + "grunt-jsbeautifier": "~0.2.6", + "grunt-plato": "~0.2.1", + "grunt-release-steps": "~0.3.7", + "grunt-shell": "~0.5.0", + "grunt-simple-mocha": "^0.4.0", + "grunt-string-replace": "~0.2.2", + "jquery-ui": "^1.10.5", + "jquerypp": "^2.0.0", + "lodash": "2.4.1", + "qunit": "^0.9.0", + "qunitjs": "^1.22.0", + "requirejs": "^2.1.22", + "rimraf": "2.1", + "steal": "^0.15.0", + "steal-benchmark": "~0.0.1", + "steal-qunit": "0.0.2", + "steal-tools": "0.15.3", + "testee": "^0.2.4", + "zombie": "^4.2.1" + }, + "scripts": { + "test": "grunt test", + "debug": "iron-node node_modules/grunt-cli/bin/grunt jslint" + }, + "demos": [ + "http://canjs.us/#examples", + "http://canjs.us/recipes" + ], + "licenses": [ + { + "type": "MIT", + "url": "http://opensource.org/licenses/mit-license.php" + } + ], + "main": "./can", + "system": { + "ignoreBrowser": true, + "main": "can", + "map": { + "can/can": "can", + "jquery/jquery": "jquery", + "steal-benchmark/steal-benchmark": "steal-benchmark", + "can-simple-dom/can-simple-dom": "can-simple-dom", + "jquery-ui/jquery-ui": "jquery-ui", + "steal-qunit/steal-qunit": "steal-qunit", + "can/util/vdom/vdom": "@empty" + }, + "paths": {}, + "meta": { + "jquery": { + "format": "global", + "exports": "jQuery" + }, + "can/util/vdom/vdom": { + "sideBundle": true + } + }, + "ext": { + "stache": "can/view/stache/system" + }, + "buildConfig": { + "map": { + "can/util/util": "can/util/domless/domless" + } + }, + "npmIgnore": [ + "grunt-docco2", + "testee", + "zombie", + "bitovi-tools", + "steal", + "steal-tools", + "connect", + "grunt", + "grunt-cli", + "grunt-contrib-clean", + "grunt-contrib-connect", + "grunt-contrib-jshint", + "grunt-contrib-uglify", + "grunt-jsbeautifier", + "grunt-plato", + "grunt-release-steps", + "grunt-shell", + "grunt-simple-mocha", + "grunt-string-replace", + "rimraf", + "lodash", + "browserify", + "grunt-banner", + "documentjs" + ], + "envs": { + "server-development": { + "map": { + "can/util/vdom/vdom": "can/util/vdom/vdom" + }, + "meta": { + "jquery": { + "format": "global", + "exports": "jQuery", + "deps": [ + "can/util/vdom/vdom" + ] + } + } + }, + "server-production": { + "map": { + "can/util/vdom/vdom": "can/util/vdom/vdom" + }, + "meta": { + "jquery": { + "format": "global", + "deps": [ + "can/util/vdom/vdom" + ] + } + } + }, + "build-development": { + "map": { + "can/util/vdom/vdom": "can/util/vdom/vdom" + }, + "meta": { + "jquery": { + "format": "global", + "exports": "jQuery", + "deps": [ + "can/util/vdom/vdom" + ] + } + } + } + } + }, + "browser": { + "./can": "./dist/cjs/can", + "./component/component": "./dist/cjs/component/component", + "./construct/construct": "./dist/cjs/construct/construct", + "./map/map": "./dist/cjs/map/map", + "./list/list": "./dist/cjs/list/list", + "./list/promise/promise": "./dist/cjs/list/promise/promise", + "./compute/compute": "./dist/cjs/compute/compute", + "./model/model": "./dist/cjs/model/model", + "./view/view": "./dist/cjs/view/view", + "./view/stache/stache": "./dist/cjs/view/stache/stache", + "./control/control": "./dist/cjs/control/control", + "./route/route": "./dist/cjs/route/route", + "./route/pushstate/pushstate": "./dist/cjs/route/pushstate/pushstate", + "./map/define/define": "./dist/cjs/map/define/define", + "./list/sort/sort": "./dist/cjs/list/sort/sort", + "./util/fixture/fixture": "./dist/cjs/util/fixture/fixture", + "./view/bindings/bindings": "./dist/cjs/view/bindings/bindings", + "./view/live/live": "./dist/cjs/view/live/live", + "./view/scope/scope": "./dist/cjs/view/scope/scope", + "./util/tests/tests": "./dist/cjs/util/tests/tests", + "./util/object/object": "./dist/cjs/util/object/object", + "./util/function/function": "./dist/cjs/util/function/function", + "./view/autorender/autorender": "./dist/cjs/view/autorender/autorender", + "./util/domless/domless": "./dist/cjs/util/domless/domless", + "./view/stache/system": "./dist/cjs/view/stache/system", + "./util/event": "./dist/cjs/util/event", + "./util/vdom/build_fragment/build_fragment": "./dist/cjs/util/vdom/build_fragment/build_fragment", + "./util/vdom/document/document": "./dist/cjs/util/vdom/document/document", + "./view/import/import": "./dist/cjs/view/import/import" + } +} \ No newline at end of file diff --git a/package.json b/package.json index f23aa4adf83..5c2270d649e 100644 --- a/package.json +++ b/package.json @@ -1,204 +1,71 @@ { - "name": "can", - "title": "CanJS", - "description": "MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.", - "version": "3.0.0-pre.1", - "keywords": [ - "CanJS", - "DoneJS" - ], - "author": { - "name": "Bitovi", - "email": "contact@bitovi.com", - "web": "http://bitovi.com/" - }, - "homepage": "http://canjs.com/", - "repository": { - "type": "git", - "url": "git@github.com:canjs/canjs.git", - "github": "https://github.com/canjs/canjs" - }, - "dependencies": { - "can-simple-dom": "^0.3.0", - "jquery": ">=1.9.0 <1.12.0 || >=2.1.0 <2.3.0" - }, - "devDependencies": { - "bitovi-tools": "^0.1.1", - "browserify": "~8.1.0", - "connect": "^2.14.4", - "documentjs": "~0.3.2", - "grunt": "~0.4.0", - "grunt-banner": "^0.3.1", - "grunt-cli": "~0.1.7", - "grunt-contrib-clean": "~0.5.0", - "grunt-contrib-connect": "~0.3.0", - "grunt-contrib-jshint": "^1.0.0", - "grunt-contrib-uglify": "~0.2.1", - "grunt-docco2": "git://github.com/shcarrico/grunt-docco.git#e4de54886ed5c421b2e26e7a2aeba1f73e889733", - "grunt-jsbeautifier": "~0.2.6", - "grunt-plato": "~0.2.1", - "grunt-release-steps": "~0.3.7", - "grunt-shell": "~0.5.0", - "grunt-simple-mocha": "^0.4.0", - "grunt-string-replace": "~0.2.2", - "jquery-ui": "^1.10.5", - "jquerypp": "^2.0.0", - "lodash": "2.4.1", - "qunit": "^0.9.0", - "qunitjs": "^1.22.0", - "requirejs": "^2.1.22", - "rimraf": "2.1", - "steal": "^0.15.0", - "steal-benchmark": "~0.0.1", - "steal-qunit": "0.0.2", - "steal-tools": "0.15.3", - "testee": "^0.2.4", - "zombie": "^4.2.1" - }, - "scripts": { - "test": "grunt test", - "debug": "iron-node node_modules/grunt-cli/bin/grunt jslint" - }, - "demos": [ - "http://canjs.us/#examples", - "http://canjs.us/recipes" - ], - "licenses": [ - { - "type": "MIT", - "url": "http://opensource.org/licenses/mit-license.php" - } - ], - "main": "./can", - "system": { - "ignoreBrowser": true, - "main": "can", - "map": { - "can/can": "can", - "jquery/jquery": "jquery", - "steal-benchmark/steal-benchmark": "steal-benchmark", - "can-simple-dom/can-simple-dom": "can-simple-dom", - "jquery-ui/jquery-ui": "jquery-ui", - "steal-qunit/steal-qunit": "steal-qunit", - "can/util/vdom/vdom": "@empty" - }, - "paths": {}, - "meta": { - "jquery": { - "format": "global", - "exports": "jQuery" - }, - "can/util/vdom/vdom": { - "sideBundle": true - } - }, - "ext": { - "stache": "can/view/stache/system" - }, - "buildConfig": { - "map": { - "can/util/util": "can/util/domless/domless" - } - }, - "npmIgnore": [ - "grunt-docco2", - "testee", - "zombie", - "bitovi-tools", - "steal", - "steal-tools", - "connect", - "grunt", - "grunt-cli", - "grunt-contrib-clean", - "grunt-contrib-connect", - "grunt-contrib-jshint", - "grunt-contrib-uglify", - "grunt-jsbeautifier", - "grunt-plato", - "grunt-release-steps", - "grunt-shell", - "grunt-simple-mocha", - "grunt-string-replace", - "rimraf", - "lodash", - "browserify", - "grunt-banner", - "documentjs" - ], - "envs": { - "server-development": { - "map": { - "can/util/vdom/vdom": "can/util/vdom/vdom" - }, - "meta": { - "jquery": { - "format": "global", - "exports": "jQuery", - "deps": [ - "can/util/vdom/vdom" - ] - } - } - }, - "server-production": { - "map": { - "can/util/vdom/vdom": "can/util/vdom/vdom" - }, - "meta": { - "jquery": { - "format": "global", - "deps": [ - "can/util/vdom/vdom" - ] - } - } - }, - "build-development": { - "map": { - "can/util/vdom/vdom": "can/util/vdom/vdom" - }, - "meta": { - "jquery": { - "format": "global", - "exports": "jQuery", - "deps": [ - "can/util/vdom/vdom" - ] - } - } - } - } - }, - "browser": { - "./can": "./dist/cjs/can", - "./component/component": "./dist/cjs/component/component", - "./construct/construct": "./dist/cjs/construct/construct", - "./map/map": "./dist/cjs/map/map", - "./list/list": "./dist/cjs/list/list", - "./list/promise/promise": "./dist/cjs/list/promise/promise", - "./compute/compute": "./dist/cjs/compute/compute", - "./model/model": "./dist/cjs/model/model", - "./view/view": "./dist/cjs/view/view", - "./view/stache/stache": "./dist/cjs/view/stache/stache", - "./control/control": "./dist/cjs/control/control", - "./route/route": "./dist/cjs/route/route", - "./route/pushstate/pushstate": "./dist/cjs/route/pushstate/pushstate", - "./map/define/define": "./dist/cjs/map/define/define", - "./list/sort/sort": "./dist/cjs/list/sort/sort", - "./util/fixture/fixture": "./dist/cjs/util/fixture/fixture", - "./view/bindings/bindings": "./dist/cjs/view/bindings/bindings", - "./view/live/live": "./dist/cjs/view/live/live", - "./view/scope/scope": "./dist/cjs/view/scope/scope", - "./util/tests/tests": "./dist/cjs/util/tests/tests", - "./util/object/object": "./dist/cjs/util/object/object", - "./util/function/function": "./dist/cjs/util/function/function", - "./view/autorender/autorender": "./dist/cjs/view/autorender/autorender", - "./util/domless/domless": "./dist/cjs/util/domless/domless", - "./view/stache/system": "./dist/cjs/view/stache/system", - "./util/event": "./dist/cjs/util/event", - "./util/vdom/build_fragment/build_fragment": "./dist/cjs/util/vdom/build_fragment/build_fragment", - "./util/vdom/document/document": "./dist/cjs/util/vdom/document/document", - "./view/import/import": "./dist/cjs/view/import/import" - } -} \ No newline at end of file + "name": "can", + "title": "CanJS", + "description": "MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.", + "version": "3.0.0-pre.1", + "keywords": [ + "CanJS", + "DoneJS" + ], + "author": { + "name": "Bitovi", + "email": "contact@bitovi.com", + "web": "http://bitovi.com/" + }, + "homepage": "http://canjs.com/", + "repository": { + "type": "git", + "url": "git@github.com:canjs/canjs.git", + "github": "https://github.com/canjs/canjs" + }, + "dependencies": { + "can-component": "^3.0.0-pre.1", + "can-compute": "^3.0.0-pre.2", + "can-construct": "^3.0.0-pre.3", + "can-control": "^3.0.0-pre.1", + "can-event": "^3.0.0-pre.3", + "can-list": "^3.0.0-pre.1", + "can-map": "^3.0.0-pre.2", + "can-model": "^2.3.11", + "can-route": "github:canjs/can-route", + "can-simple-dom": "^0.3.0" + }, + "devDependencies": { + "steal": "^0.16.4", + "steal-qunit": "^0.1.1", + "steal-tools": "^0.16.2" + }, + "scripts": { + "test": "testee test/index.html --browsers firefox", + "build": "node build.js" + }, + "demos": [ + "http://canjs.us/#examples", + "http://canjs.us/recipes" + ], + "licenses": [ + { + "type": "MIT", + "url": "http://opensource.org/licenses/mit-license.php" + } + ], + "main": "./can", + "system": { + "npmAlgorithm": "flat", + "main": "can", + "paths": {}, + "meta": { + "can/util/vdom/vdom": { + "sideBundle": true + } + }, + "ext": { + "stache": "can/view/stache/system" + }, + "buildConfig": { + "map": { + "can/util/util": "can/util/domless/domless" + } + } + } +} diff --git a/route/demo.html b/route/demo.html deleted file mode 100644 index 5a682c2fc52..00000000000 --- a/route/demo.html +++ /dev/null @@ -1,247 +0,0 @@ - - - - can.route demo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

                window.location.hash

                -
                - " -
                -
                Change the demo's hash (try it) or push the back or - forward - button. Notice how can.route's attributes change.

                can.route.attr(data)

                -
                -
                
                -			    
                -
                Add or change the Route Data's properties (except for the - special route property). Notice how the hash changes.

                can.route(route, default)

                  can.route uses these routes to match and create urls. Edit the - default values. Show Me

                  can.route.link(name, data)

                  Returns a link that - updates the hash with a url determined by the routes. Specifically: - . - Try it: -

                  can.route.url(data)

                  Returns a hash url determined by the routes. Specifically: - . - Go there!
                  -
                  - -
                  -
                  - - - - - - -

                  Events

                  -
                  can.route.bind("change", function(ev, attr, how, newVal, oldVal){
                  -  log(ev.batchNum, attr, how, newVal, oldVal)
                  -})
                  - - - - - - - - -
                  batchNumattrhownewValoldVal
                  -
                  - - - - -
                  -
                  - - - diff --git a/route/docs/map.html b/route/docs/map.html deleted file mode 100644 index 152b87623ea..00000000000 --- a/route/docs/map.html +++ /dev/null @@ -1,157 +0,0 @@ - -
                  - - - diff --git a/route/docs/map.md b/route/docs/map.md deleted file mode 100644 index 26dd8e1f15b..00000000000 --- a/route/docs/map.md +++ /dev/null @@ -1,153 +0,0 @@ -@function can.route.map map -@parent can.route.static - -Assign a can.Map instance that acts as can.route's internal can.Map. The purpose for this is to cross-bind a top level state object (Application State) to the can.route. - -@signature `can.route.map(MapConstructor)` - -@param {can.Map} MapConstructor A can.Map constructor function. A new can.Map instance will be created and used as the can.Map internal to can.route. - -@signature `can.route.map(mapInstance)` - -@param {can.Map} mapInstance A can.Map instance, used as the can.Map internal to can.route. - -@body - -## Background - -One of the biggest challenges in a complex application is getting all the different parts of the app to talk to each other simply, cleanly, and reliably. - -An elegant way to solve this problem is using the [Observer Pattern](http://en.wikipedia.org/wiki/Observer_pattern). A single object, which can be called [Application State](https://www.youtube.com/watch?v=LrzK4exG5Ss), holds the high level state of the application. - -## Use - -`can.route.map` provides an easy to way make your Application State object cross-bound to `can.route`, using an internal can.Map instance, which is serialized into the hash (or pushstate URLs). - - var appState = new can.Map({ - petType: "dog", - storeId: 2 - }); - - can.route.map(appState); - -## When to call it - -Call `can.route.map` at the start of the application lifecycle, before any calls to `can.route.bind`. This is because `can.route.map` creates a new internal `can.Map`, replacing the default `can.Map` instance, so binding has to occur on this new object. - - var appState = new can.Map({ - graphType: "line", - currencyType: "USD" - }); - - can.route.map(appState); - -## Demo - -The following shows creating an appState that loads data at page load, has a virtual property 'locationIds' which serializes an array, and synchronizes the appState to can.route: - -@demo can/route/docs/map.html - -## Using arrays and can.Lists - -If the Application State contains a property which is any non-primitive type, its useful to use the [can.Map.prototype.define define] plugin to define how that property will serialize. `can.route` calls [can.Map.prototype.serialize serialize] internally to turn the Application State object into URL params. - -The following example shows a flags property, which is an array of string-based flags: - - var AppState = can.Map.extend({ - define: { - flags: { - // return a string friendly format - serialize: function(){ - return this.attr('flags').join(','); - }, - // convert a stringified object into an array - set: function(val){ - if(val === ""){ - return []; - } - var arr = val; - if(typeof val === "string"){ - arr = val.split(',') - } - return arr; - } - } - }); - - var appState = new AppState({ - flags: [] - }); - - can.route.map(appState); - -## Complete example - -The following example shows loading some metadata on page load, which must be loaded as part of the Application State before the components can be initialized - -It also shows an example of a "virtual" property on the AppState, locationIds, which is the serialized version of a non-serializeable can.List, locations. A setter is defined on locationIds, which will translate changes in locationIds back to the locations can.List. - - var AppState = can.Map.extend({ - define: { - locations: { - // don't serialize this property at all in the route - serialize: false - }, - // virtual property that contains a comma separated list of ids - // based on locations that are selected - locationIds: { - - // comma separated list of ids - serialize: function(){ - var selected = this.attr('locations').filter( - function(location){ - return location.attr('selected'); - }); - var ids = []; - selected.each(function(item){ - ids.push(item.attr('id')); - }) - return selected.join(','); - }, - - // toggle selected from a comma separated list of ids - set: function(val){ - var arr = val; - if(typeof val === "string"){ - arr = val.split(',') - } - // for each id, toggle any matched location - this.attr('locations').each(function(location){ - if(arr.indexOf(location.attr('id')) !== -1){ - location.attr('selected', true); - } else { - location.attr('selected', false) - } - }) - } - } - } - }); - - // initialize and call map first, so anything binding to can.route - // will work correctly - var appState = new AppState(); - can.route.map(appState); - - // GET /locations - var locations = new Location.List({}); - - // when the data is ready, set the locations property - locations.done(function(){ - var appState.attr('locations', locations) - - // call ready after the appState is fully initialized - can.route.ready(); - }) - -## Why - -The Application State object, which is cross-bound to the can.route via `can.route.map` and represents the overall state of the application, has several obvious uses: - -* It is passed into the various components and used to communicate their own internal state. -* It provides deep linking and back button support. As the URL changes, Application State changes cause changes in application components. -* It provides the ability to "save" the current state of the page, by serializing the Application State object and saving it on the backend, then restoring with that object to load this saved state. diff --git a/route/historytabs.html b/route/historytabs.html deleted file mode 100644 index 17196fa6918..00000000000 --- a/route/historytabs.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - can.route history tabs - - - -
                  - -
                  - Recipe 1 Content -
                  -
                  - Recipe 2 Content -
                  -
                  - Recipe 3 Content -
                  - - -
                  - Task 1 Content -
                  -
                  - Task 2 Content -
                  -
                  - Task 3 Content -
                  -
                  - - - - diff --git a/route/route.html b/route/route.html deleted file mode 100644 index 1dada2d6746..00000000000 --- a/route/route.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - can.Route - - - -

                  can.Route Demo

                  - -
                  -
                  -
                  - Use these links to modify the hash or change it directly in the address bar: -

                  - URLs with a registered path: - #!pages/val1/val2/val3 - #!pages/val1// - #!pages/// - #!/val1/val2 -
                  - URLs without paths: - #!pages// - #!/// -
                  - Empty hash: - #! -
                  -

                  Hash update events:

                  -
                  -
                  -
                  -
                  -
                  - Value1:
                  - Value2:
                  - Value3: - -
                  -
                  -

                  Data update events:

                  -
                  -
                  - - - diff --git a/route/route.md b/route/route.md deleted file mode 100644 index 1612f5486fe..00000000000 --- a/route/route.md +++ /dev/null @@ -1,258 +0,0 @@ -@function can.route can.route -@group can.route.static static -@inherits can.Map -@download can/route -@test can/route/test.html -@parent canjs -@group can.route.plugins plugins -@link ../docco/route/route.html docco - -@description Manage browser history and -client state by synchronizing the window.location.hash with -a [can.Map]. - -@signature `can.route( template [, defaults] )` - -Create a route matching rule. - -@param {String} template the fragment identifier to match. The fragment identifier -should start with either a character (a-Z) or colon (:). Examples: - - can.route(":foo") - can.route("foo/:bar") - -@param {Object} [defaults] an object of default values -@return {can.route} - -@body - -## Use - -Watch this video for an overview of can.route's functionality and an example showing how to connect two tab widgets to the browser's history: - - - -In the following CanJS community we also talk about web application routing: - - - -## Background Information - -To support the browser's back button and bookmarking -in an Ajax application, most applications use -the window.location.hash. By -changing the hash (via a link or JavaScript), -one is able to add to the browser's history -without changing the page. - -This provides the basics needed to -create history enabled Ajax websites. However, -`can.route` addresses several other needs such as: - - - Pretty urls (actually hashes) - - Keeping routes independent of application code - - Listening to specific parts of the history changing - - Setup / Teardown of widgets. - -## How it works - -can.route is a [can.Map] that represents the -window.location.hash as an -object. For example, if the hash looks like: - - #!type=videos&id=5 - -the data in can.route looks like: - - { type: 'videos', id: 5 } - - -`can.route` keeps the state of the hash in-sync with the `data` contained within -`can.route`. - -## can.Map - -`can.route` is a [can.Map]. Understanding -`can.Map` is essential for using `can.route` correctly. - -You can listen to changes in an Observe with `bind(eventName, handler(ev, args...))` and -change can.route's properties with -[can.Map.prototype.attr attr]. - -### Listening to changes in can.route - -Listen to changes in history -by [can.Map.prototype.bind bind]ing to -changes in can.route like: - - can.route.bind('change', function(ev, attr, how, newVal, oldVal) { - - }) - - - `attr` - the name of the changed attribute - - `how` - the type of Observe change event (add, set or remove) - - `newVal`/`oldVal` - the new and old values of the attribute - -### Updating can.route - -Create changes in the route data with [can.Map.prototype.attr attr] like: - - can.route.attr('type','images'); - -Or change multiple properties at once like: - - can.route.attr({type: 'pages', id: 5}, true) - -When you make changes to can.route, they will automatically -change the hash. - -## Creating a Route - -Use can.route(url, defaults) to create a -route. A route is a mapping from a url to -an object (that is the can.route's state). -In order to map to a specific properties in the url, -prepend a colon to the name of the property like: - - can.route( "#!content/:type" ) - - -If no routes are added, or no route is matched, -can.route's data is updated with the [can.deparam deparamed] -hash. - - location.hash = "#!type=videos"; - // can.route -> {type : "videos"} - -Once routes are added and the hash changes, -can.route looks for matching routes and uses them -to update can.route's data. - - can.route( "#!content/:type" ); - location.hash = "#!content/images"; - // can.route -> {type : "images"} - can.route.attr( "type", "songs" ) - // location.hash -> "#!content/songs" - -Default values can be added to a route: - - can.route("content/:type",{type: "videos" }); - location.hash = "#!content/" - // can.route -> {type : "videos"} - // location.hash -> "#!content/" - -Defaults can also be set on the root page of your app: - - can.route( "", { page: "index" } ); - location.hash = "#!"; - // can.route.attr() -> { page: "index" } - // location.hash -> "#!" - -## Initializing can.route - -After your application has created all of its routes, call [can.route.ready] -to set can.route's data to match the current hash: - - can.route.ready() - -## Changing the route. - -Typically, you don't set location.hash -directly. Instead, you can change properties on can.route -like: - - can.route.attr('type', 'videos') - -This will automatically look up the appropriate -route and update the hash. - -Often, you want to create links. can.route provides -the [can.route.link] and [can.route.url] helpers to make this -easy: - - can.route.link("Videos", {type: 'videos'}) - -## Demo - -The following demo shows the relationship between `window.location.hash`, -routes given to `can.data`, -`can.route`'s data, and events on `can.data`. Most properties -are editable so experiment! - -@iframe can/route/demo.html 980 - -## IE Compatibility - -Internet Explorer 6 and 7 does not support `window.onhashchange`. -Even Internet Explorer 8 running in IE7 compatibility mode reports `true` -for `onhashchange` in window, even though the event isn't supported. - -If you are using jQuery, you can include Ben Alman's [HashChange Plugin](http://benalman.com/projects/jquery-hashchange-plugin/) -to support the event in the unsupported browser(s). - -## Using routes with `can.Control` - -Using templated event handlers, it is possible to listen to changes to -`can.route` within `can.Control`. This is convenient as it allows the -control to listen to and make changes whenever the route is modified, -even outside of the control itself. - - // create the route - can.route("#!content/:type") - - can.Control({ - // the route has changed - "{can.route} change": function(ev, attr, how, newVal, oldVal) { - if (attr === "type") { - // the route has a type - } - } - }); - -### Creating and binding routes with `can.Control.route` - -Using [can.Control.route], a builtin plugin to CanJS, cuts down on the amount -of code needed to work with `can.route` in `can.Control`. With this plugin, it is possible -to both create routes and bind to `can.route` at the same time. Instead of creating -several routes to handle changes to __type__ and __id__, write something like this -in a control: - - can.Control({ - // the route is empty - "route": function(data) { - - }, - // the route has a type - ":type route": function(data) { - - }, - // the route has a type and id - ":type/:id route": function(data) { - - } - }); - - -### Getting more specific with the `can.Map.delegate` plugin - -Sometimes, you might only want to trigger a function when the route changes -only once, even if the route change gets called multiple times. By using the -[can.Map.delegate] plugin, this is extremely easy. This plugin allows you to -listen to change, set, add, and remove on `can.route`. - -If you wanted to, say, show a list of recipes when __type__ was set to recipe -and show a specific recipe when __id__ was set, you could do something like: - - can.Control({ - "{can.route} type=recipe set": - function( ev, prop, how, newVal, oldVal ) { - // show list of recipes - }, - "recipe/:id": function(data) { - // show a single recipe - } - }); - -If we didn't only listen to when recipe is set, then every time we chose to -show a single recipe, we would create and show the list of recipes again which -would not be very efficient. diff --git a/route/route_map.md b/route/route_map.md deleted file mode 100644 index a7a275d253e..00000000000 --- a/route/route_map.md +++ /dev/null @@ -1,153 +0,0 @@ -@function can.route.map map -@parent can.route.static - -Assign a can.Map instance that acts as can.route's internal can.Map. The purpose for this is to cross-bind a top level state object (Application State) to the can.route. - -@signature `can.route.map(MapConstructor)` - -@param {can.Map} MapConstructor A can.Map constructor function. A new can.Map instance will be created and used as the can.Map internal to can.route. - -@signature `can.route.map(mapInstance)` - -@param {can.Map} mapInstance A can.Map instance, used as the can.Map internal to can.route. - -@body - -## Background - -One of the biggest challenges in a complex application is getting all the different parts of the app to talk to each other simply, cleanly, and reliably. - -An elegant way to solve this problem is using the [Observer Pattern](http://en.wikipedia.org/wiki/Observer_pattern). A single object, which can be called [Application State](https://www.youtube.com/watch?v=LrzK4exG5Ss), holds the high level state of the application. - -## Use - -`can.route.map` provides an easy to way make your Application State object cross-bound to `can.route`, using an internal can.Map instance, which is serialized into the hash (or pushstate URLs). - - var appState = new can.Map({ - petType: "dog", - storeId: 2 - }); - - can.route.map(appState); - -## When to call it - -Call `can.route.map` at the start of the application lifecycle, before any calls to `can.route.bind`. This is because `can.route.map` creates a new internal `can.Map`, replacing the default `can.Map` instance, so binding has to occur on this new object. - - var appState = new can.Map({ - graphType: "line", - currencyType: "USD" - }); - - can.route.map(appState); - -## Demo - -The following shows creating an appState that loads data at page load, has a virtual property 'locationIds' which serializes an array, and synchronizes the appState to can.route: - -@demo can/route/docs/map.html - -## Using arrays and can.Lists - -If the Application State contains a property which is any non-primitive type, its useful to use the [can.Map.prototype.define define] plugin to define how that property will serialize. `can.route` calls [can.Map.prototype.serialize serialize] internally to turn the Application State object into URL params. - -The following example shows a flags property, which is an array of string-based flags: - - var AppState = can.Map.extend({ - define: { - flags: { - // return a string friendly format - serialize: function(){ - return this.attr('flags').join(','); - }, - // convert a stringified object into an array - set: function(val){ - if(val === ""){ - return []; - } - var arr = val; - if(typeof val === "string"){ - arr = val.split(',') - } - return arr; - } - } - }); - - var appState = new AppState({ - flags: [] - }); - - can.route.map(appState); - -## Complete example - -The following example shows loading some metadata on page load, which must be loaded as part of the Application State before the components can be initialized - -It also shows an example of a "virtual" property on the AppState, locationIds, which is the serialized version of a non-serializeable can.List, locations. A setter is defined on locationIds, which will translate changes in locationIds back to the locations can.List. - - var AppState = can.Map.extend({ - define: { - locations: { - // don't serialize this property at all in the route - serialize: false - }, - // virtual property that contains a comma separated list of ids - // based on locations that are selected - locationIds: { - - // comma separated list of ids - serialize: function(){ - var selected = this.attr('locations').filter( - function(location){ - return location.attr('selected'); - }); - var ids = []; - selected.each(function(item){ - ids.push(item.attr('id')); - }) - return selected.join(','); - }, - - // toggle selected from a comma separated list of ids - set: function(val){ - var arr = val; - if(typeof val === "string"){ - arr = val.split(',') - } - // for each id, toggle any matched location - this.attr('locations').each(function(location){ - if(arr.indexOf(location.attr('id')) !== -1){ - location.attr('selected', true); - } else { - location.attr('selected', false) - } - }) - } - } - } - }); - - // initialize and call map first, so anything binding to can.route - // will work correctly - var appState = new AppState(); - can.route.map(appState); - - // GET /locations - var locations = new Location.List({}); - - // when the data is ready, set the locations property - locations.done(function(){ - var appState.attr('locations', locations) - - // call ready after the appState is fully initialized - can.route.ready(); - }) - -## Why - -The Application State object, which is cross-bound to the can.route via `can.route.map` and represents the overall state of the application, has several obvious uses: - -* It is passed into the various components and used to communicate their own internal state. -* It provides deep linking and back button support. As the URL changes, Application State changes cause changes in application components. -* It provides the ability to "save" the current state of the page, by serializing the Application State object and saving it on the backend, then restoring with that object to load this saved state. \ No newline at end of file diff --git a/route/test.html b/route/test.html deleted file mode 100644 index 37b3a6095b8..00000000000 --- a/route/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/route - -
                  diff --git a/route/testing.html b/route/testing.html deleted file mode 100644 index d8cf6b3f12f..00000000000 --- a/route/testing.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - can.route test page - - -

                  This is a dummy page to use
                  for testing route goodness

                  - - - - diff --git a/test/all-tests.js b/test/all-tests.js deleted file mode 100644 index 25dc701c64a..00000000000 --- a/test/all-tests.js +++ /dev/null @@ -1,23 +0,0 @@ -require('can/test/test'); -require('can/component/component_test'); -require('can/construct/construct_test'); -require('can/map/map_test'); -require('can/list/list_test'); -require('can/list/promise/promise_test'); -require('can/compute/compute_test'); -require('can/model/model_test'); -require('can/view/view_test'); -require('can/view/stache/stache_test'); -require('can/control/control_test'); -require('can/route/route_test'); -require('can/control/route/route_test'); -require('can/route/pushstate/pushstate_test'); -require('can/map/define/define_test'); -require('can/list/sort/sort_test'); -require('can/util/object/object_test'); -require('can/util/fixture/fixture_test'); -require('can/view/bindings/bindings_test'); -require('can/view/live/live_test'); -require('can/view/scope/scope_test'); -require('can/util/string/string_test'); -require('can/util/attr/attr_test'); diff --git a/test/amd/jquery.html b/test/amd/jquery.html deleted file mode 100644 index 1fb2be6e429..00000000000 --- a/test/amd/jquery.html +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - jQuery Test Suite - - - - -

                  jQuery Test Suite

                  - -

                  - -
                  -

                  -
                    -
                    -
                    - - - - - - \ No newline at end of file diff --git a/test/benchmarks.js b/test/benchmarks.js deleted file mode 100644 index c04667034b2..00000000000 --- a/test/benchmarks.js +++ /dev/null @@ -1,34 +0,0 @@ -require("benchmark") - -var suite = new Benchmark.Suite; - -suite.on('cycle', function(event) { - console.log(String(event.target)); -}); - -var benchmarks = { - add: function(name, setup, benchmark, teardown){ - if(!benchmark){ - benchmark = setup; - setup = undefined - } - suite.add(name, benchmark, { - setup: setup, - teardown: teardown - }); - return this; - }, - run: function(){ - suite.run({ 'async': true, 'queued': true }); - }, - suite: suite, - on: function(){ - return suite.on.apply(this, arguments) - } -}; - - -benchmarks.run(); - - -module.exports = benchmarks; diff --git a/test/builders/browserify/main.js b/test/builders/browserify/main.js deleted file mode 100644 index 914b3fa282b..00000000000 --- a/test/builders/browserify/main.js +++ /dev/null @@ -1,18 +0,0 @@ -var can = require("../../../dist/cjs/can.js"); - -can.Component.extend({ - tag: "hello-world", - template: "{{message}}", - scope: { - message: "Hello World" - }, - events: { - init: function() { - // Access scope from $.fn.scope. - var scope = this.element.scope(); - scope.attr("name", "Matthew"); - } - } -}); - -window.can = can; diff --git a/test/builders/browserify/prod.html b/test/builders/browserify/prod.html deleted file mode 100644 index 99b1f8a4d87..00000000000 --- a/test/builders/browserify/prod.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/test/builders/browserify/test.js b/test/builders/browserify/test.js deleted file mode 100644 index b0eaca3dc5e..00000000000 --- a/test/builders/browserify/test.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Test that the dist works - */ -var assert = require("assert"), - connect = require("connect"), - path = require("path"), - Browser = require('zombie'), - rmdir = require("rimraf"); - -var browserify = require('browserify'); -var fs = require('fs'), path = require("path"); - - -// Helper -var find = function(browser, property, callback, done){ - var start = new Date(); - var check = function(){ - if(browser.window && browser.window[property]) { - callback(browser.window[property]); - } else if(new Date() - start < 2000){ - setTimeout(check, 20); - } else { - done("failed to find "+property+" in "+browser.window.location.href); - } - }; - check(); -}; - -var open = function(url, callback, done){ - var server = connect().use(connect.static(path.join(__dirname,"../../.."))).listen(8081); - var browser = new Browser(); - browser.visit("http://localhost:8081/"+url) - .then(function(){ - callback(browser, function(err){ - server.close(); - done(err); - }) - }).catch(function(e){ - server.close(); - done(e) - }); -}; - - -describe("browserify", function(){ - it("is able to build an app that does not use templates", function(done){ - this.timeout(10000); - var b = browserify(); - b.add(path.join(__dirname, "main.js")); - var out = fs.createWriteStream(path.join(__dirname, "out.js")); - b.bundle().pipe(out); - out.on('finish', function(){ - open("test/builders/browserify/prod.html", function(browser, close) { - find(browser, "PAGE_READY", function(m){ - var helloWorld = browser.document.getElementsByTagName("hello-world")[0]; - assert.equal(helloWorld.innerHTML, "Hello World", "right text"); - close(); - }, close); - }, done); - }); - - }); -}); - diff --git a/test/builders/steal-tools/app.html b/test/builders/steal-tools/app.html deleted file mode 100644 index d1ba246a3bd..00000000000 --- a/test/builders/steal-tools/app.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - -

                    Steal-Tools Build Suite

                    - -

                    - -
                    -

                    -
                      -
                      - - - diff --git a/test/builders/steal-tools/app.js b/test/builders/steal-tools/app.js deleted file mode 100644 index 96443339eaf..00000000000 --- a/test/builders/steal-tools/app.js +++ /dev/null @@ -1,10 +0,0 @@ -import helloworld from "helloworld.stache!stache"; -import "can/view/autorender/"; - -function capitalize(txt){ - return txt.toUpperCase(); -} - -window.MODULE = { - stache: helloworld({message: "Hi"}, { capitalize: capitalize }) -}; diff --git a/test/builders/steal-tools/bundle/components/one/one.js b/test/builders/steal-tools/bundle/components/one/one.js deleted file mode 100644 index 4fc4f873e88..00000000000 --- a/test/builders/steal-tools/bundle/components/one/one.js +++ /dev/null @@ -1,3 +0,0 @@ -import tmpl from "./one.stache!"; - - diff --git a/test/builders/steal-tools/bundle/components/one/one.stache b/test/builders/steal-tools/bundle/components/one/one.stache deleted file mode 100644 index 8dabb7790a2..00000000000 --- a/test/builders/steal-tools/bundle/components/one/one.stache +++ /dev/null @@ -1 +0,0 @@ -
                      One
                      diff --git a/test/builders/steal-tools/bundle/config.js b/test/builders/steal-tools/bundle/config.js deleted file mode 100644 index 284f92f68ac..00000000000 --- a/test/builders/steal-tools/bundle/config.js +++ /dev/null @@ -1,49 +0,0 @@ -(function () { - // taking from HTML5 Shiv v3.6.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed - var supportsUnknownElements = false; - - (function () { - try { - var a = document.createElement('a'); - a.innerHTML = ''; - - supportsUnknownElements = a.childNodes.length == 1 || (function () { - // assign a false positive if unable to shiv - (document.createElement)('a'); - var frag = document.createDocumentFragment(); - return ( - typeof frag.cloneNode == 'undefined' || - typeof frag.createDocumentFragment == 'undefined' || - typeof frag.createElement == 'undefined' - ); - }()); - } catch (e) { - // assign a false positive if detection fails => unable to shiv - supportsUnknownElements = false; - } - }()); - - - steal.config({ - map: { - "jquery/jquery": "jquery", - "can/util/util": "can/util/jquery/jquery", - "stache": "can/view/stache/system" - }, - paths: { - "jquery": "../../../../node_modules/jquery/dist/jquery.js", - "can/*": "../../../../*.js" - }, - ext: { - stache: "can/view/stache/system" - }, - bundle: [ "components/one/one" ] - }); - System.buildConfig = { - map: { - "jquery/jquery": "jquery", - "can/util/util": "can/util/domless/domless", - "stache": "can/view/stache/system" - } - }; -})(); diff --git a/test/builders/steal-tools/bundle/main.js b/test/builders/steal-tools/bundle/main.js deleted file mode 100644 index 54c4381381c..00000000000 --- a/test/builders/steal-tools/bundle/main.js +++ /dev/null @@ -1,6 +0,0 @@ -import can from "can/"; -import tmpl from "template.stache!stache"; - -window.MODULE = { - html: tmpl().childNodes[0].innerHTML -}; diff --git a/test/builders/steal-tools/bundle/prod.html b/test/builders/steal-tools/bundle/prod.html deleted file mode 100644 index 989b7748801..00000000000 --- a/test/builders/steal-tools/bundle/prod.html +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/test/builders/steal-tools/bundle/template.stache b/test/builders/steal-tools/bundle/template.stache deleted file mode 100644 index 15e4af9a4b2..00000000000 --- a/test/builders/steal-tools/bundle/template.stache +++ /dev/null @@ -1 +0,0 @@ -
                      Main
                      diff --git a/test/builders/steal-tools/config.js b/test/builders/steal-tools/config.js deleted file mode 100644 index 2987070e775..00000000000 --- a/test/builders/steal-tools/config.js +++ /dev/null @@ -1,48 +0,0 @@ -(function () { - // taking from HTML5 Shiv v3.6.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed - var supportsUnknownElements = false; - - (function () { - try { - var a = document.createElement('a'); - a.innerHTML = ''; - - supportsUnknownElements = a.childNodes.length == 1 || (function () { - // assign a false positive if unable to shiv - (document.createElement)('a'); - var frag = document.createDocumentFragment(); - return ( - typeof frag.cloneNode == 'undefined' || - typeof frag.createDocumentFragment == 'undefined' || - typeof frag.createElement == 'undefined' - ); - }()); - } catch (e) { - // assign a false positive if detection fails => unable to shiv - supportsUnknownElements = false; - } - }()); - - - steal.config({ - map: { - "jquery/jquery": "jquery", - "can/util/util": "can/util/jquery/jquery", - "stache": "can/view/stache/system" - }, - paths: { - "jquery": "../../../node_modules/jquery/dist/jquery.js", - "can/*": "../../../*.js" - }, - ext: { - stache: "view/stache/stache.js" - } - }); - System.buildConfig = { - map: { - "jquery/jquery": "jquery", - "can/util/util": "can/util/domless/domless", - "stache": "can/view/stache/system" - } - }; -})(); diff --git a/test/builders/steal-tools/helloworld.stache b/test/builders/steal-tools/helloworld.stache deleted file mode 100644 index 1a8d694201c..00000000000 --- a/test/builders/steal-tools/helloworld.stache +++ /dev/null @@ -1,2 +0,0 @@ - -

                      {{templateName}} {{capitalize message}}

                      diff --git a/test/builders/steal-tools/helpers.js b/test/builders/steal-tools/helpers.js deleted file mode 100644 index 59fec57f5f8..00000000000 --- a/test/builders/steal-tools/helpers.js +++ /dev/null @@ -1,6 +0,0 @@ -var can = require('can/util/'); -var stache = require('can/view/stache/'); - -can.stache.registerHelper("templateName", function(){ - return "Stache"; -}); diff --git a/test/builders/steal-tools/import/app.js b/test/builders/steal-tools/import/app.js deleted file mode 100644 index 9a179d44912..00000000000 --- a/test/builders/steal-tools/import/app.js +++ /dev/null @@ -1 +0,0 @@ -var tmpl = require("./app.stache!stache"); diff --git a/test/builders/steal-tools/import/app.stache b/test/builders/steal-tools/import/app.stache deleted file mode 100644 index 4f98b017a86..00000000000 --- a/test/builders/steal-tools/import/app.stache +++ /dev/null @@ -1,7 +0,0 @@ - - -{{#eq page "list"}} - - thing - -{{/if}} diff --git a/test/builders/steal-tools/import/other.js b/test/builders/steal-tools/import/other.js deleted file mode 100644 index e7134e7006d..00000000000 --- a/test/builders/steal-tools/import/other.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = "foo"; diff --git a/test/builders/steal-tools/import/thing.js b/test/builders/steal-tools/import/thing.js deleted file mode 100644 index f89639b5d5c..00000000000 --- a/test/builders/steal-tools/import/thing.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = "thing"; diff --git a/test/builders/steal-tools/prod-bundled.html b/test/builders/steal-tools/prod-bundled.html deleted file mode 100644 index 9cb0c34064a..00000000000 --- a/test/builders/steal-tools/prod-bundled.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/test/builders/steal-tools/prod.html b/test/builders/steal-tools/prod.html deleted file mode 100644 index 37de4abc775..00000000000 --- a/test/builders/steal-tools/prod.html +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/test/builders/steal-tools/test.js b/test/builders/steal-tools/test.js deleted file mode 100644 index 40b47a33cbc..00000000000 --- a/test/builders/steal-tools/test.js +++ /dev/null @@ -1,151 +0,0 @@ -var assert = require("assert"), - connect = require("connect"), - stealTools = require("steal-tools"), - path = require("path"), - rmdir = require("rimraf"), - Browser = require("zombie"), - fs = require("fs"); - -var exists = function(dest){ - try { - return !!fs.readFileSync(dest, "utf8"); - } catch(err){ - return false; - } -}; - -// Helpers -var find = function(browser, property, callback, done){ - var start = new Date(); - var check = function(){ - if(browser.window && browser.window[property]) { - callback(browser.window[property]); - } else if(new Date() - start < 2000){ - setTimeout(check, 20); - } else { - done("failed to find "+property+" in "+browser.window.location.href); - } - }; - check(); -}; - -var open = function(url, callback, done){ - var server = connect().use(connect.static(path.join(__dirname,"../../.."))).listen(8081); - var browser = new Browser(); - browser.visit("http://localhost:8081/"+url) - .then(function(e){ - callback(browser, function(err){ - server.close(); - done(err); - }) - }).catch(function(e){ - server.close(); - done(e) - }); - return browser; -}; - -describe("Building steal projects", function(){ - this.timeout(20000); - - it("works with stache", function(done){ - rmdir(__dirname+"/bundles", function(error){ - if(error){ - done(error) - } - // build the project that - // uses a plugin - stealTools.build({ - config: __dirname+"/config.js", - main: "app" - }, { - minify: false, - quiet: true - }).then(function(){ - open("test/builders/steal-tools/prod.html", function(browser, close) { - find(browser, "MODULE", function(m){ - var div = browser.document.createElement('div'); - div.appendChild(m.stache); - - assert.equal(div.getElementsByTagName('h1')[0].textContent, "Stache HI", "Stache generated correctly"); - close(); - }, close); - }, done); - }).catch(done); - }); - }); - - it("works with bundles", function(done){ - rmdir(__dirname + "bundle/dist", function(error){ - if(error) return done(error); - - stealTools.build({ - config: __dirname + "/bundle/config.js", - main: "main" - }, { - quiet: true, - minify: false - }).then(function(){ - - open("test/builders/steal-tools/bundle/prod.html", function(browser, close){ - find(browser, "MODULE", function(m){ - assert(typeof m, "object", "Correctly returned the module"); - assert.equal(m.html, "Main", "Rendered the div correctly"); - close(); - }, close); - }, done); - }).catch(done); - }); - }); - - it("works bundled with autorender", function(done){ - rmdir(__dirname + "bundle/dist", function(error){ - if(error) return done(error); - - stealTools.build({ - config: __dirname + "/config.js", - main: "app" - }, { - quiet: true, - minify: false, - bundleSteal: true - }).then(function(){ - - var browser = open("test/builders/steal-tools/prod-bundled.html", function(browser, close){ - // If we got here it worked. - close(); - }, done); - browser.on("response", function(req, resp){ - if(resp.statusCode === 404) { - done(new Error("Tried to load " + resp.url)); - } - }); - }).catch(done); - }); - }); - - it("adds dynamically imported modules to the bundle", function(done){ - rmdir(__dirname + "/import/dist", function(error){ - if(error) return done(error); - - stealTools.build({ - config: __dirname + "/config.js", - main: "import/app", - bundlesPath: __dirname + "/import/dist/bundles" - }, { - quiet: true, - minify: false, - bundleSteal: true - }).then(function(buildResult){ - var loader = buildResult.loader; - assert.equal(loader.bundle[0], "import/thing", "import/thing added a bundle"); - - assert(exists(__dirname + "/import/dist/bundles/import/thing.js"), "thing.js bundle written out"); - - done(); - }); - }); - }); - - -}); diff --git a/test/compatibility/jquery.html b/test/compatibility/jquery.html deleted file mode 100644 index e1c14bf0540..00000000000 --- a/test/compatibility/jquery.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - jQuery Compatibility Test Suite - - - - - - -

                      jQuery Compatibility Test Suite

                      - -

                      - -
                      -

                      -
                        -
                        -
                        - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/demos_and_tests.html b/test/demos_and_tests.html deleted file mode 100644 index 5758219c6a4..00000000000 --- a/test/demos_and_tests.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - -

                        Construct

                        - - -

                        Control

                        - - -

                        route

                        - - - - -

                        Model

                        - - - - - - - -

                        Attributes

                        - - - -

                        Compute

                        - - - -

                        Delegate

                        - -

                        Elements (old)

                        - - - -

                        Setter

                        - - - -

                        Sort

                        - - -

                        Validations

                        - - - - -

                        Route

                        - - - - - - -

                        pushstate

                        - - - -

                        view

                        - - - - - diff --git a/test/dev/jquery.html b/test/dev/jquery.html deleted file mode 100644 index 36cc2bbee06..00000000000 --- a/test/dev/jquery.html +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - jQuery Test Suite - - - - - - -

                        jQuery Test Suite

                        - -

                        - -
                        -

                        -
                          -
                          -
                          - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/index.html b/test/index.html new file mode 100644 index 00000000000..08146dcec57 --- /dev/null +++ b/test/index.html @@ -0,0 +1,3 @@ +CanJS tests + +
                          diff --git a/test/jquery.html b/test/jquery.html deleted file mode 100644 index ada6eda541e..00000000000 --- a/test/jquery.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - CanJS jQuery tests - - - - - -
                          - - - \ No newline at end of file diff --git a/test/performance-loading.html b/test/performance-loading.html deleted file mode 100644 index feb359e2dc5..00000000000 --- a/test/performance-loading.html +++ /dev/null @@ -1,788 +0,0 @@ - - - - - - - - - -
                          - - - - - diff --git a/test/server/test.js b/test/server/test.js deleted file mode 100644 index 63219d2f2de..00000000000 --- a/test/server/test.js +++ /dev/null @@ -1,28 +0,0 @@ -var assert = require("assert"); -var Steal = require("steal"); - -describe("Running on the server", function(){ - this.timeout(20000); - - describe("With Steal", function(){ - before(function(){ - var steal = this.steal = Steal.clone(); - - var System = global.System = this.System = steal.System; - - System.config({ - config: __dirname + "/../../package.json!npm", - main: "@empty", - env: "server-development" - }); - }); - - it("Works with no config", function(done){ - var steal = this.steal; - - steal.import("can/util/").then(function(){ - assert(true, "it worked"); - }).then(done); - }); - }); -}); diff --git a/test/templates/__configuration__-amd.html.ejs b/test/templates/__configuration__-amd.html.ejs deleted file mode 100644 index 4ad48583bc9..00000000000 --- a/test/templates/__configuration__-amd.html.ejs +++ /dev/null @@ -1,59 +0,0 @@ - - - - <%= configuration.description %> Test Suite - - - -

                          <%= configuration.description %> Test Suite

                          - -

                          - -
                          -

                          -
                            -
                            -
                            - - - - - diff --git a/test/templates/__configuration__-compat.html.ejs b/test/templates/__configuration__-compat.html.ejs deleted file mode 100644 index 7ab7cd2ff94..00000000000 --- a/test/templates/__configuration__-compat.html.ejs +++ /dev/null @@ -1,51 +0,0 @@ - - - - <%= configuration.description %> Compatibility Test Suite - - - - - -

                            <%= configuration.description %> Compatibility Test Suite

                            - -

                            - -
                            -

                            -
                              -
                              -
                              - - - - - - -<% modules.forEach(function(module) { %> -<% if(!module.isDefault) { %><% } %><% }); %> -<% pluginified.forEach(function(version) { %> - -<% }); %> - - - - - diff --git a/test/templates/__configuration__-dev.html.ejs b/test/templates/__configuration__-dev.html.ejs deleted file mode 100644 index d986addde42..00000000000 --- a/test/templates/__configuration__-dev.html.ejs +++ /dev/null @@ -1,55 +0,0 @@ - - - - <%= configuration.description %> Test Suite - - - - - -

                              <%= configuration.description %> Test Suite

                              - -

                              - -
                              -

                              -
                                -
                                -
                                - - - - - - - - -<% modules.forEach(function(module) { %> -<% if(!module.isDefault) { %><% } %><% }); %> - - - - - - diff --git a/test/templates/__configuration__-dist.html.ejs b/test/templates/__configuration__-dist.html.ejs deleted file mode 100644 index b118db8c237..00000000000 --- a/test/templates/__configuration__-dist.html.ejs +++ /dev/null @@ -1,56 +0,0 @@ - - - - <%= configuration.description %> Test Suite - - - - - -

                                <%= configuration.description %> Test Suite

                                - -

                                - -
                                -

                                -
                                  -
                                  -
                                  - - - - - - - - - - -<% modules.forEach(function(module) { %><% if(!module.isDefault) { %><% } %><% }); %> - - - - - - diff --git a/test/templates/__configuration__.html.ejs b/test/templates/__configuration__.html.ejs deleted file mode 100644 index 4acf5912a3a..00000000000 --- a/test/templates/__configuration__.html.ejs +++ /dev/null @@ -1,20 +0,0 @@ - - - - CanJS <%= configuration.description %> tests - - - - -
                                  - - diff --git a/test/templates/test.html.ejs b/test/templates/test.html.ejs deleted file mode 100644 index 4d0cfb18e73..00000000000 --- a/test/templates/test.html.ejs +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - -

                                  <%= module.definition.name %> Test Suite

                                  - -

                                  - -
                                  -

                                  -
                                    -
                                    - - - - - - \ No newline at end of file diff --git a/test/test.js b/test/test.js index 6374b958b01..a899026d568 100644 --- a/test/test.js +++ b/test/test.js @@ -1,41 +1,8 @@ -steal('can/util', function() { - var viewCheck = /(\.stache|extensionless)$/; - - can.test = { - fixture: function (path) { - if (typeof steal !== 'undefined') { - if(steal.idToUri) { - return steal.config('root').toString() + '/' + path; - } else { - return steal.joinURIs(System.baseURL, path); - } - } - - if (window.require && require.toUrl && !viewCheck.test(path)) { - return require.toUrl(path); - } - return path; - }, - path: function (path, absolute) { - //absolute prevents require.toURL from returning a relative path - if (typeof steal !== 'undefined') { - if(steal.idToUri) { - return ""+steal.idToUri(steal.id("can/"+path).toString()); - } else { - return steal.joinURIs(System.baseURL, path); - } - } - - if (!absolute && window.require && require.toUrl && !viewCheck.test(path)) { - return require.toUrl(path); - } - - var pathIndex = window.location.href.indexOf('/test/') - if(pathIndex){ - return window.location.href.substring(0, pathIndex + 1) + path; - } - - return path; - } - }; -}); +require('../construct/construct_test'); +require('../component/component_test'); +require('../compute/compute_test'); +require('../control/control_test'); +require('../event/event_test'); +require('../list/list_test'); +require('../map/map_test'); +// require('../model/model_test'); diff --git a/util/can.js b/util/can.js index 5494fa15075..a17b6ec3053 100644 --- a/util/can.js +++ b/util/can.js @@ -6,261 +6,11 @@ var glbl = typeof window !== "undefined" ? window : (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) ? self : global; var can = {}; + if (typeof GLOBALCAN === 'undefined' || GLOBALCAN !== false) { glbl.can = can; } -can.global = glbl; - -// An empty function useful for where you need a dummy callback. -can.k = function(){}; - -can.isDeferred = can.isPromise = function (obj) { - // Returns `true` if something looks like a deferred. - return obj && typeof obj.then === "function" && typeof obj.pipe === "function"; -}; -can.isMapLike = function(obj){ - return can.Map && (obj instanceof can.Map || obj && obj.___get); -}; - -var cid = 0; -can.cid = function (object, name) { - if (!object._cid) { - cid++; - object._cid = (name || '') + cid; - } - return object._cid; -}; -can.VERSION = '@EDGE'; - -can.simpleExtend = function (d, s) { - for (var prop in s) { - d[prop] = s[prop]; - } - return d; -}; - -can.last = function(arr){ - return arr && arr[arr.length - 1]; -}; - - -can.isDOM = function(el) { - return (el.ownerDocument || el) === can.global.document; -}; - -can.childNodes = function(node) { - var childNodes = node.childNodes; - if("length" in childNodes) { - return childNodes; - } else { - var cur = node.firstChild; - var nodes = []; - while(cur) { - nodes.push(cur); - cur = cur.nextSibling; - } - return nodes; - } -}; - -var protoBind = Function.prototype.bind; -if(protoBind) { - can.proxy = function(fn, context){ - return protoBind.call(fn, context); - }; -} else { - can.proxy = function (fn, context) { - return function () { - return fn.apply(context, arguments); - }; - }; -} - -can.frag = function(item, doc){ - var document = doc || can.document || can.global.document; - var frag; - if(!item || typeof item === "string"){ - frag = can.buildFragment(item == null ? "" : ""+item, document); - // If we have an empty frag... - if (!frag.childNodes.length) { - frag.appendChild(document.createTextNode('')); - } - return frag; - } else if(item.nodeType === 11) { - return item; - } else if(typeof item.nodeType === "number") { - frag = document.createDocumentFragment(); - frag.appendChild(item); - return frag; - } else if(typeof item.length === "number") { - frag = document.createDocumentFragment(); - can.each(item, function(item){ - frag.appendChild( can.frag(item) ); - }); - if (!can.childNodes(frag).length) { - frag.appendChild(document.createTextNode('')); - } - return frag; - } else { - frag = can.buildFragment( ""+item, document); - // If we have an empty frag... - if (!can.childNodes(frag).length) { - frag.appendChild(document.createTextNode('')); - } - return frag; - } -}; - -// Define the `can.scope` function that can be used to retrieve the `scope` from the element -can.scope = can.viewModel = function (el, attr, val) { - el = can.$(el); - var scope = can.data(el, "scope") || can.data(el, "viewModel"); - if(!scope) { - scope = new can.Map(); - can.data(el, "scope", scope); - can.data(el, "viewModel", scope); - } - switch (arguments.length) { - case 0: - case 1: - return scope; - case 2: - return scope.attr(attr); - default: - scope.attr(attr, val); - return el; - } -}; - -var parseURI = function(url){ - var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/); - // authority = '//' + user + ':' + pass '@' + hostname + ':' port - return (m ? { - href : m[0] || '', - protocol : m[1] || '', - authority: m[2] || '', - host : m[3] || '', - hostname : m[4] || '', - port : m[5] || '', - pathname : m[6] || '', - search : m[7] || '', - hash : m[8] || '' - } : null); - }; - -can.joinURIs = function(base, href) { - function removeDotSegments(input) { - var output = []; - input.replace(/^(\.\.?(\/|$))+/, '') - .replace(/\/(\.(\/|$))+/g, '/') - .replace(/\/\.\.$/, '/../') - .replace(/\/?[^\/]*/g, function (p) { - if (p === '/..') { - output.pop(); - } else { - output.push(p); - } - }); - return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : ''); - } - - href = parseURI(href || ''); - base = parseURI(base || ''); - - return !href || !base ? null : (href.protocol || base.protocol) + - (href.protocol || href.authority ? href.authority : base.authority) + - removeDotSegments(href.protocol || href.authority || href.pathname.charAt(0) === '/' ? href.pathname : (href.pathname ? ((base.authority && !base.pathname ? '/' : '') + base.pathname.slice(0, base.pathname.lastIndexOf('/') + 1) + href.pathname) : base.pathname)) + - (href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) + - href.hash; -}; - -can["import"] = function(moduleName, parentName) { - var deferred = new can.Deferred(); - - if(typeof window.System === "object" && can.isFunction(window.System["import"])) { - window.System["import"](moduleName, { - name: parentName - }).then(can.proxy(deferred.resolve, deferred), - can.proxy(deferred.reject, deferred)); - } else if(window.define && window.define.amd){ - - window.require([moduleName], function(value){ - deferred.resolve(value); - }); - - } else if(window.require){ - deferred.resolve(window.require(moduleName)); - } else { - // ideally this will use can.getObject - deferred.resolve(); - } - - return deferred.promise(); -}; - -// this is here in case can.compute hasn't loaded -can.__observe = function () {}; - -can.isNode = typeof process === "object" && - {}.toString.call(process) === "[object process]"; - -can.isBrowserWindow = typeof window !== "undefined" && - typeof document !== "undefined" && typeof SimpleDOM === "undefined"; -can.isWebWorker = typeof WorkerGlobalScope !== "undefined" && - (self instanceof WorkerGlobalScope); - - -//!steal-remove-start -can.dev = { - warnTimeout: 5000, - logLevel: 0, - /** - * Adds a warning message to the console. - * ``` - * can.dev.warn("something evil"); - * ``` - * @param {String} out the message - */ - warn: function (out) { - var ll = this.logLevel; - if (ll < 2) { - Array.prototype.unshift.call(arguments, 'WARN:'); - if (typeof window !== undefined && window.console && console.warn) { - this._logger("warn", Array.prototype.slice.call(arguments)); - } else if (window.console && console.log) { - this._logger("log", Array.prototype.slice.call(arguments)); - } else if (window.opera && window.opera.postError) { - window.opera.postError("steal.js WARNING: " + out); - } - } - }, - /** - * Adds a message to the console. - * ``` - * can.dev.log("hi"); - * ``` - * @param {String} out the message - */ - log: function (out) { - var ll = this.logLevel; - if (ll < 1) { - if (window.console && console.log) { - Array.prototype.unshift.call(arguments, 'Info:'); - this._logger("log", Array.prototype.slice.call(arguments)); - } else if (window.opera && window.opera.postError) { - window.opera.postError("steal.js INFO: " + out); - } - } - }, - _logger: function (type, arr) { - try { - console[type].apply(console, arr); - } catch(e) { - console[type](arr); - } - } -}; -//!steal-remove-end +can.global = glbl; module.exports = exports = can; diff --git a/util/util.js b/util/util.js index c89bafa88a1..05aae488c8f 100644 --- a/util/util.js +++ b/util/util.js @@ -1,3 +1,5 @@ -// comments are in func.js for documentation purposes. -var can = require('can/util/jquery/jquery'); -module.exports = can; +var js = require('can-util/js/js'); +var dom = require('can-util/dom/dom'); +var can = require('../util/can'); + +js.assign(can, js, dom); From da2cab0651ec919f09cb679538d812d92d4f9dc4 Mon Sep 17 00:00:00 2001 From: David Luecke Date: Tue, 3 May 2016 15:17:39 -0700 Subject: [PATCH 02/10] Update Travis settings --- .travis.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9f5147bb2dc..f3db51b112f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,4 @@ before_install: - "npm install -g phantomjs-prebuilt" - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" -env: - - TEST_SUITE=loaders - - TEST_SUITE=dists - - TEST_SUITE=individuals + \ No newline at end of file From b7ceda87add049eaef3b692392975d644b38b199 Mon Sep 17 00:00:00 2001 From: David Luecke Date: Tue, 3 May 2016 15:23:27 -0700 Subject: [PATCH 03/10] Add testee --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 5c2270d649e..88de34c02a5 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "devDependencies": { "steal": "^0.16.4", "steal-qunit": "^0.1.1", - "steal-tools": "^0.16.2" + "steal-tools": "^0.16.2", + "testee": "^0.2.5" }, "scripts": { "test": "testee test/index.html --browsers firefox", From 93340e862a9edbb8b33432bab7697a38eaf2ce04 Mon Sep 17 00:00:00 2001 From: David Luecke Date: Mon, 9 May 2016 14:53:40 -0700 Subject: [PATCH 04/10] Build and test the build --- build.js | 8 +- can.js | 5 +- event/event.js | 2 +- package.json | 8 +- route/pushstate/binding.md | 30 -- route/pushstate/demo.html | 249 --------- route/pushstate/empty.html | 1 - route/pushstate/pushstate.html | 56 -- route/pushstate/pushstate.md | 201 ------- route/pushstate/test.html | 3 - route/pushstate/testing.html | 46 -- route/route.js | 691 +----------------------- route/route_test.js | 959 +-------------------------------- test/dist.html | 8 + test/test.js | 13 +- 15 files changed, 32 insertions(+), 2248 deletions(-) delete mode 100644 route/pushstate/binding.md delete mode 100644 route/pushstate/demo.html delete mode 100644 route/pushstate/empty.html delete mode 100644 route/pushstate/pushstate.html delete mode 100644 route/pushstate/pushstate.md delete mode 100644 route/pushstate/test.html delete mode 100644 route/pushstate/testing.html create mode 100644 test/dist.html diff --git a/build.js b/build.js index 2869d37cbf9..1dacf366897 100644 --- a/build.js +++ b/build.js @@ -4,12 +4,12 @@ stealTools.export({ system: { config: __dirname + "/package.json!npm" }, + options: { + useNormalizedDependencies: false + }, outputs: { - "+amd": {}, "+global-js": { - ignore: function(name, load){ - return false; - } + ignore: false } } }).catch(function(e){ diff --git a/can.js b/can.js index b548af03d2a..4ebf8658560 100644 --- a/can.js +++ b/can.js @@ -1,5 +1,8 @@ var can = require('./util/can'); -require('can/component/component'); +require('./construct/construct'); +// require('./compute/compute'); +// require('./control/control'); +// require('./component/component'); module.exports = can; diff --git a/event/event.js b/event/event.js index 199f7bc89dd..d774919a9e7 100644 --- a/event/event.js +++ b/event/event.js @@ -1,6 +1,6 @@ var can = require('../util/can'); can.assign(can, require('can-event')); -can.assign(can, require('can-event/batch/batch')); +can.batch = require('can-event/batch/batch'); module.exports = can; diff --git a/package.json b/package.json index 88de34c02a5..1ab089378c4 100644 --- a/package.json +++ b/package.json @@ -19,16 +19,18 @@ "github": "https://github.com/canjs/canjs" }, "dependencies": { - "can-component": "^3.0.0-pre.1", + "can-component": "^3.0.0-pre.3", "can-compute": "^3.0.0-pre.2", "can-construct": "^3.0.0-pre.3", "can-control": "^3.0.0-pre.1", + "can-define": "^0.7.4", "can-event": "^3.0.0-pre.3", "can-list": "^3.0.0-pre.1", "can-map": "^3.0.0-pre.2", "can-model": "^2.3.11", - "can-route": "github:canjs/can-route", - "can-simple-dom": "^0.3.0" + "can-route": "^3.0.0-pre.3", + "can-simple-dom": "^0.3.0", + "can-stache": "^3.0.0-pre.1" }, "devDependencies": { "steal": "^0.16.4", diff --git a/route/pushstate/binding.md b/route/pushstate/binding.md deleted file mode 100644 index 4a1caecf996..00000000000 --- a/route/pushstate/binding.md +++ /dev/null @@ -1,30 +0,0 @@ -@typedef {{}} can.route.binding - -@option {String} root The starting point of -the url to match. For `hashchange`, the value -is "#!". For `pushstate`, the value is `/`. This can -be overwritten before [can.route.ready] is called like: - - can.route.bindings.pushstate.root = "/site/" - -@option {String} querySeparator Specifies the seperator -between the path part of the url and the query (also known as search) -part of the url. For `hashchange`, the value -is `"&"`. For `pushstate`, the value is `"?"`. - -@option {RegExp} paramsMatcher A regular expression that is used -to identify the `key=value` pairs in the query part of the url. - -@option {function():String} matchingPartOfURL Reads the url and returns the -part that is used for matching routes. - -@option {function(path):String} setURL Called with the -serialized can.route data after a route has changed. -Returns what the url has been updated to. - -@option {function} bind Attaches listeners to the document to know -when the url has changed. Typically `bind` is called when [can.route.ready] is called. - -@option {function} unbind Tears down the bindings to the document. - - diff --git a/route/pushstate/demo.html b/route/pushstate/demo.html deleted file mode 100644 index 98ddc8bd978..00000000000 --- a/route/pushstate/demo.html +++ /dev/null @@ -1,249 +0,0 @@ - - - - can.route.pushstate demo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

                                    window.location

                                    -
                                    - " -
                                    -
                                    Change the demo's path (try it) or push the back or - forward - button. Notice how can.route's attributes change.

                                    can.route.attr(data)

                                    -
                                    -
                                    
                                    -                
                                    -
                                    Add or change the Route Data's properties (except for the - special route property). Notice how the path changes.

                                    can.route(route, default)

                                      can.route uses these routes to match and create urls. Edit the - default values. Show Me

                                      can.link(name, data)

                                      Returns a link that - updates the path with a url determined by the routes. Specifically: - . - Try it: -

                                      can.url(data)

                                      Returns a path url determined by the routes. Specifically: - . - Go there!
                                      -
                                      - -
                                      -
                                      - - - - - - -

                                      Events

                                      -
                                      can.route.bind("change", function(ev, attr, how, newVal, oldVal){
                                      -  log(ev.batchNum, attr, how, newVal, oldVal)
                                      -})
                                      - - - - - - - - -
                                      batchNumattrhownewValoldVal
                                      -
                                      - - - - -
                                      -
                                      - - - diff --git a/route/pushstate/empty.html b/route/pushstate/empty.html deleted file mode 100644 index 6c70bcfe4d4..00000000000 --- a/route/pushstate/empty.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/route/pushstate/pushstate.html b/route/pushstate/pushstate.html deleted file mode 100644 index 8b273a7e033..00000000000 --- a/route/pushstate/pushstate.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - diff --git a/route/pushstate/pushstate.md b/route/pushstate/pushstate.md deleted file mode 100644 index 2808617855d..00000000000 --- a/route/pushstate/pushstate.md +++ /dev/null @@ -1,201 +0,0 @@ -@property {Object} can.route.pushstate -@download can/route/pushstate -@test can/route/pushstate/test.html -@parent can.route.plugins -@link ../docco/route/pushstate/pushstate.html docco - -@description Changes [can.route] to use -[pushstate](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history) -to change the window's [pathname](https://developer.mozilla.org/en-US/docs/Web/API/URLUtils.pathname) instead -of the [hash](https://developer.mozilla.org/en-US/docs/Web/API/URLUtils.hash). - - -@option {Object} The pushstate object comprises several properties that configure the behavior of [can.route] to work with `history.pushstate`. - -@body - -## Use - -The pushstate plugin uses the same API as [can.route]. To start using pushstate plugin all you need is to load `can/route/pushstate`, it will set itself as default binding on [can.route]. - -You can check current binding by inspecting `can.route.currentBinding`, the default value is `"hashchange"`. - -### Creating and changing routes - -To [create](can.route.html#section_CreatingaRoute) route use `can.route(url, defaults)` like: - - can.route(":page", {page: 'homepage'}); - can.route("contacts/:username"); - can.route("books/:genre/:author"); - can.route.ready(); // do not forget to initialize can.route - -Do not forget to [initialize](can.route.ready.html) `can.route` after creating all routes, do it by calling `can.route.ready()`. - -List of defined routes is contained in `can.route.routes`, you can examine current `can.route` state by calling: - - can.route.attr(); //-> {page: "homepage", route: ":page"} - -After creating routes and initializing `can.route` you can update current route by calling `can.route.attr(attr, newVal)` - - can.route.attr('page', 'about'); - can.route.attr(); //-> {page: "about", route: ":page"} - - // without cleaning current can.route state - can.route.attr('username', 'veljko'); - can.route.attr(); //-> {page: "about", route: ":page", username: 'veljko'} - - // with cleaning current can.route state - can.route.attr({username: 'veljko'}, true); - can.route.attr(); //-> {username: "veljko", route: "contacts/:username"} - -To update multiple attributes at once pass hash of attributes to `can.route.attr(hashOfAttrs, true)`. Pass `true` as second argument to clean up current state. - - can.route.attr({genre: 'sf', author: 'adams'}, true); - can.route.attr(); //-> {genre: "sf", author: "adams", route: "books/:genre/:author"} - -`window.location` acts as expected: - - window.location.pathname; //-> "/books/sf/adams" - window.location.hash; //-> "", hash remains unchanged - -To generate urls use `can.route.url({attrs})`: - - can.route.url({username: 'justinbmeyer'}) //-> '/contacts/justinbmeyer' - - -### Listening changes on matched route - -As `can.route` is basically a [can.Map] that represents `window.location.pathname`, you can bind on it in the same way you would on any can.Map object. - -To listen on any changes on `can.route` use `can.route.bind('change', callback)`, the following params will be passed to callback function: - - can.route.bind('change', function(ev, attr, how, newVal, oldVal) { - //-> ev: {EventObject} - //-> attr: 'username' - //-> how: 'change' - //-> newVal: 'veljko' - //-> oldVal: undefined - }); - can.route.attr({username: 'veljko'}, true); - -You can also bind to specific attribute on can.route: - - can.route.bind('username', function(ev, newVal, oldVal) { - //-> ev: {EventObject} - //-> newVal: 'nikica' - //-> oldVal: 'veljko' - }); - can.route.attr({username: nikica}, true); - - -### Using different pathname root - -Pushstate plugin has one additional property, `can.route.bindings.pushstate.root`, which specifies the part of that pathname that should not change. For example, if we only want to have pathnames within `http://example.com/contacts/`, we can specify a root like: - - can.route.bindings.pushstate.root = "/contacts/" - can.route(":page"); - can.route.url({page: "list"}) //-> "/contacts/list" - can.route.url({foo: "bar"}) //-> "/contacts/?foo=bar" - -Now, all routes will start with `"/contacts/"`, the default `can.route.bindings.pushstate.root` value is `"/"`. - -### Using `can.route.pushstate.js` with the `can.Control.route` plugin - -The `can.Control.route` plugin is a great way to simplify your code. Not only will it bind the event listeners for routes, but it also automagically prevents clicks on matching links from reloading the entire page. - -Here are some examples of binding to pushstate-style routes in a can.Control using the `can.Control.route` plugin. The first comment in each example is an href for a link that would trigger the route. The second is the contents of can.route.attr(). In each example, the contents of the data variable in the function will be the same as can.route.attr(), but without the route attribute. All of these examples assume that pushstate.root is set to the default of '/': - - // Listen to the root route. See above section on pushstate.root - "route": function(data) { - // Go Home! - // {route:""} - }, - - // A route with a trailing slash - "/files/ route": function(data) { - // Pretend this is a folder full of files. - // {route:"files/"} - }, - - // A route with a file extension - "/files/batman.pdf route": function(data) { - // I'm Batman. - // {route:"files/batman.pdf"} - }, - - // A route without a trailing slash - "/files route": function(data) { - // The files are in the computer? - // {route:"files"} - }, - - // A route with a wildcard parameter - "/files/:file_name route": function(data) { - // Holy roasted metal! - // {route:"files/robin.pdf", file_name:"robin.pdf"} - - // Notice that the :file_name parameter in the route becomes a separate - // attribute of can.route. This "wildcard" enables listening for - // any two-parameter route that begins with 'files'. /files/test.html - // and /files/moose would also route here. - }, - - // A useless route handler that will never be called. - "/files/joker.pdf route": function(data){ - // Smile! - // {route:"files/joker.pdf"} - - // Why is it useless? Because, while the above href could potentially - // trigger this route event, the directly preceding route with the - // :file_name parameter will respond because it comes first in the code. - }, - - // Three parameters, one a wildcard. - "/admin/contestants/:id route": function(data){ - // The price is wrong, Bob. - // {route:"admin/contestants/:id", id:1} - }, - - // Three parameters, two wildcards. - "/admin/:page/:anchor route": function(data){ - // Steve's Testimonial - // {route:"admin/:page/:anchor", page:"testimonials", anchor:"steve"} - - // Technically, if the route just before this one was taken out of the code, - // this route would respond to links like /admin/users/1. In that case the - // route attributes would look like this: - - // The price is wrong, Bob. - // {route:"admin/:page/:anchor", page:"contestants", anchor:"1"} - - // But, because the preceding route exists, this route will respond to all - // three-parameter routes that have the first parameter of admin except for ones - // where the second parameter is users (ones that start with '/admin/users/'), - // which are picked up by the preceding route. - }, - -## Watch for errors inside route handlers. - -Any error inside of a route handler will cause a page refresh before the error is shown. For example: - - "/admin/users route": function(data){ - - // This will cause problems. - console.log(nonExistentVariable); - - // This will cause problems - var users = someFunctionWithAnErrorInside(); - - // This one won't cause problems. - console.log(data); - } - -In an error-free route handler, all links matching the route would not result in a page refresh, but instead would result in a pushstate change. The errors in the example will cause the page to refresh. The code will run after the refresh and show the error message. - -## Planning route structure - -Complications can arise if your route structure mimics the folder structure inside your app's public directory. For example, if you have a folder structure like the one in this url for your admin app... - -`/admin/users/list.js` - -... using a route of /admin/users on the same page that uses the list.js file will require the use of a trailing slash on all routes and links. The browser already learned that '/admin/users' is folder. Because folders were originally denoted by a trailing slash in a url, the browser will correct the url to be '/admin/users/'. While it is possible to add the trailing slash in routes and listen for them, any link to the page that omits the trailing slash will not trigger the route handler. diff --git a/route/pushstate/test.html b/route/pushstate/test.html deleted file mode 100644 index 77a7cb55b3e..00000000000 --- a/route/pushstate/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/route/pushstate - -
                                      diff --git a/route/pushstate/testing.html b/route/pushstate/testing.html deleted file mode 100644 index 0604af50d8d..00000000000 --- a/route/pushstate/testing.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - can.route test page - - -

                                      This is a dummy page to use
                                      for testing route goodness

                                      - - - - diff --git a/route/route.js b/route/route.js index 9dc3e6f4efd..ded5b1c9e0e 100644 --- a/route/route.js +++ b/route/route.js @@ -1,690 +1,3 @@ -var can = require('can/util/util'); -require('can/map/map'); -require('can/list/list'); -require('can/util/string/deparam/deparam'); +var can = require('../util/can'); -// ## route.js -// `can.route` -// _Helps manage browser history (and client state) by synchronizing the -// `window.location.hash` with a `can.Map`._ -// -// Helper methods used for matching routes. -var -// `RegExp` used to match route variables of the type ':name'. -// Any word character or a period is matched. -matcher = /\:([\w\.]+)/g, - // Regular expression for identifying &key=value lists. - paramsMatcher = /^(?:&[^=]+=[^&]*)+/, - // Converts a JS Object into a list of parameters that can be - // inserted into an html element tag. - makeProps = function (props) { - var tags = []; - can.each(props, function (val, name) { - tags.push((name === 'className' ? 'class' : name) + '="' + - (name === "href" ? val : can.esc(val)) + '"'); - }); - return tags.join(" "); - }, - // Checks if a route matches the data provided. If any route variable - // is not present in the data, the route does not match. If all route - // variables are present in the data, the number of matches is returned - // to allow discerning between general and more specific routes. - matchesData = function (route, data) { - var count = 0, - i = 0, - defaults = {}; - // look at default values, if they match ... - for (var name in route.defaults) { - if (route.defaults[name] === data[name]) { - // mark as matched - defaults[name] = 1; - count++; - } - } - for (; i < route.names.length; i++) { - if (!data.hasOwnProperty(route.names[i])) { - return -1; - } - if (!defaults[route.names[i]]) { - count++; - } - - } - - return count; - }, - location = typeof window !== 'undefined' ? window.location : {}, // jshint ignore:line - wrapQuote = function (str) { - return (str + '') - .replace(/([.?*+\^$\[\]\\(){}|\-])/g, "\\$1"); - }, - each = can.each, - extend = can.extend, - // Helper for convert any object (or value) to stringified object (or value) - stringify = function (obj) { - // Object is array, plain object, Map or List - if (obj && typeof obj === "object") { - if (obj instanceof can.Map) { - obj = obj; - } else { - // Get array from array-like or shallow-copy object - obj = can.isFunction(obj.slice) ? obj.slice() : can.extend({}, obj); - } - // Convert each object property or array item into stringified new - can.each(obj, function (val, prop) { - obj[prop] = stringify(val); - }); - // Object supports toString function - } else if (obj !== undefined && obj !== null && can.isFunction(obj.toString)) { - obj = obj.toString(); - } - - return obj; - }, - removeBackslash = function (str) { - return str.replace(/\\/g, ""); - }, - // A ~~throttled~~ debounced function called multiple times will only fire once the - // timer runs down. Each call resets the timer. - timer, - // Intermediate storage for `can.route.data`. - curParams, - // The last hash caused by a data change - lastHash, - // Are data changes pending that haven't yet updated the hash - changingData, - // List of attributes that have changed since last update - changedAttrs = [], - // If the `can.route.data` changes, update the hash. - // Using `.serialize()` retrieves the raw data contained in the `observable`. - // This function is ~~throttled~~ debounced so it only updates once even if multiple values changed. - // This might be able to use batchNum and avoid this. - onRouteDataChange = function (ev, attr, how, newval) { - // indicate that data is changing - changingData = 1; - // collect attributes that are changing - changedAttrs.push(attr); - clearTimeout(timer); - timer = setTimeout(function () { - // indicate that the hash is set to look like the data - changingData = 0; - var serialized = can.route.data.serialize(), - path = can.route.param(serialized, true); - can.route._call("setURL", path, changedAttrs); - // trigger a url change so its possible to live-bind on url-based changes - can.batch.trigger(eventsObject,"__url",[path, lastHash]); - lastHash = path; - changedAttrs = []; - }, 10); - }, - // A dummy events object used to dispatch url change events on. - eventsObject = can.extend({}, can.event), - // everything in the backing Map is a string - // add type coercion during Map setter to coerce all values to strings - stringCoercingMapDecorator = function(map) { - var attrSuper = map.attr; - - map.attr = function(prop, val) { - var serializable = this.define === undefined || this.define[prop] === undefined || !!this.define[prop].serialize, - args; - - if (serializable) { // if setting non-str non-num attr - args = stringify(Array.apply(null, arguments)); - } else { - args = arguments; - } - - return attrSuper.apply(this, args); - }; - - return map; - }; - -can.route = function (url, defaults) { - // if route ends with a / and url starts with a /, remove the leading / of the url - var root = can.route._call("root"); - - if (root.lastIndexOf("/") === root.length - 1 && - url.indexOf("/") === 0) { - url = url.substr(1); - } - - defaults = defaults || {}; - // Extract the variable names and replace with `RegExp` that will match - // an atual URL with values. - var names = [], - res, - test = "", - lastIndex = matcher.lastIndex = 0, - next, - querySeparator = can.route._call("querySeparator"), - matchSlashes = can.route._call("matchSlashes"); - - // res will be something like [":foo","foo"] - while (res = matcher.exec(url)) { - names.push(res[1]); - test += removeBackslash(url.substring(lastIndex, matcher.lastIndex - res[0].length)); - // if matchSlashes is false (the default) don't greedily match any slash in the string, assume its part of the URL - next = "\\" + (removeBackslash(url.substr(matcher.lastIndex, 1)) || querySeparator+(matchSlashes? "": "|/")); - // a name without a default value HAS to have a value - // a name that has a default value can be empty - // The `\\` is for string-escaping giving single `\` for `RegExp` escaping. - test += "([^" + next + "]" + (defaults[res[1]] ? "*" : "+") + ")"; - lastIndex = matcher.lastIndex; - } - test += url.substr(lastIndex) - .replace("\\", ""); - // Add route in a form that can be easily figured out. - can.route.routes[url] = { - // A regular expression that will match the route when variable values - // are present; i.e. for `:page/:type` the `RegExp` is `/([\w\.]*)/([\w\.]*)/` which - // will match for any value of `:page` and `:type` (word chars or period). - test: new RegExp("^" + test + "($|" + wrapQuote(querySeparator) + ")"), - // The original URL, same as the index for this entry in routes. - route: url, - // An `array` of all the variable names in this route. - names: names, - // Default values provided for the variables. - defaults: defaults, - // The number of parts in the URL separated by `/`. - length: url.split('/') - .length - }; - return can.route; -}; - -/** - * @static - */ -extend(can.route, { - - /** - * @function can.route.param param - * @parent can.route.static - * @description Get a route path from given data. - * @signature `can.route.param( data )` - * @param {data} object The data to populate the route with. - * @return {String} The route, with the data populated in it. - * - * @body - * Parameterizes the raw JS object representation provided in data. - * - * can.route.param( { type: "video", id: 5 } ) - * // -> "type=video&id=5" - * - * If a route matching the provided data is found, that URL is built - * from the data. Any remaining data is added at the end of the - * URL as & separated key/value parameters. - * - * can.route(":type/:id") - * - * can.route.param( { type: "video", id: 5 } ) // -> "video/5" - * can.route.param( { type: "video", id: 5, isNew: false } ) - * // -> "video/5&isNew=false" - */ - param: function (data, _setRoute) { - // Check if the provided data keys match the names in any routes; - // Get the one with the most matches. - var route, - // Need to have at least 1 match. - matches = 0, - matchCount, - routeName = data.route, - propCount = 0; - - delete data.route; - - each(data, function () { - propCount++; - }); - // Otherwise find route. - each(can.route.routes, function (temp, name) { - // best route is the first with all defaults matching - - matchCount = matchesData(temp, data); - if (matchCount > matches) { - route = temp; - matches = matchCount; - } - if (matchCount >= propCount) { - return false; - } - }); - // If we have a route name in our `can.route` data, and it's - // just as good as what currently matches, use that - if (can.route.routes[routeName] && matchesData(can.route.routes[routeName], data) === matches) { - route = can.route.routes[routeName]; - } - // If this is match... - if (route) { - var cpy = extend({}, data), - // Create the url by replacing the var names with the provided data. - // If the default value is found an empty string is inserted. - res = route.route.replace(matcher, function (whole, name) { - delete cpy[name]; - return data[name] === route.defaults[name] ? "" : encodeURIComponent(data[name]); - }) - .replace("\\", ""), - after; - // Remove matching default values - each(route.defaults, function (val, name) { - if (cpy[name] === val) { - delete cpy[name]; - } - }); - - // The remaining elements of data are added as - // `&` separated parameters to the url. - after = can.param(cpy); - // if we are paraming for setting the hash - // we also want to make sure the route value is updated - if (_setRoute) { - can.route.attr('route', route.route); - } - return res + (after ? can.route._call("querySeparator") + after : ""); - } - // If no route was found, there is no hash URL, only paramters. - return can.isEmptyObject(data) ? "" : can.route._call("querySeparator") + can.param(data); - }, - /** - * @function can.route.deparam deparam - * @parent can.route.static - * @description Extract data from a route path. - * @signature `can.route.deparam( url )` - * @param {String} url A route fragment to extract data from. - * @return {Object} An object containing the extracted data. - * - * @body - * Creates a data object based on the query string passed into it. This is - * useful to create an object based on the `location.hash`. - * - * can.route.deparam("id=5&type=videos") - * // -> { id: 5, type: "videos" } - * - * - * It's important to make sure the hash or exclamantion point is not passed - * to `can.route.deparam` otherwise it will be included in the first property's - * name. - * - * can.route.attr("id", 5) // location.hash -> #!id=5 - * can.route.attr("type", "videos") - * // location.hash -> #!id=5&type=videos - * can.route.deparam(location.hash) - * // -> { #!id: 5, type: "videos" } - * - * `can.route.deparam` will try and find a matching route and, if it does, - * will deconstruct the URL and parse our the key/value parameters into the data object. - * - * can.route(":type/:id") - * - * can.route.deparam("videos/5"); - * // -> { id: 5, route: ":type/:id", type: "videos" } - */ - deparam: function (url) { - - // remove the url - var root = can.route._call("root"); - if (root.lastIndexOf("/") === root.length - 1 && - url.indexOf("/") === 0) { - url = url.substr(1); - } - - // See if the url matches any routes by testing it against the `route.test` `RegExp`. - // By comparing the URL length the most specialized route that matches is used. - var route = { - length: -1 - }, - querySeparator = can.route._call("querySeparator"), - paramsMatcher = can.route._call("paramsMatcher"); - - each(can.route.routes, function (temp, name) { - if (temp.test.test(url) && temp.length > route.length) { - route = temp; - } - }); - // If a route was matched. - if (route.length > -1) { - - var // Since `RegExp` backreferences are used in `route.test` (parens) - // the parts will contain the full matched string and each variable (back-referenced) value. - parts = url.match(route.test), - // Start will contain the full matched string; parts contain the variable values. - start = parts.shift(), - // The remainder will be the `&key=value` list at the end of the URL. - remainder = url.substr(start.length - (parts[parts.length - 1] === querySeparator ? 1 : 0)), - // If there is a remainder and it contains a `&key=value` list deparam it. - obj = (remainder && paramsMatcher.test(remainder)) ? can.deparam(remainder.slice(1)) : {}; - - // Add the default values for this route. - obj = extend(true, {}, route.defaults, obj); - // Overwrite each of the default values in `obj` with those in - // parts if that part is not empty. - each(parts, function (part, i) { - if (part && part !== querySeparator) { - obj[route.names[i]] = decodeURIComponent(part); - } - }); - obj.route = route.route; - return obj; - } - // If no route was matched, it is parsed as a `&key=value` list. - if (url.charAt(0) !== querySeparator) { - url = querySeparator + url; - } - return paramsMatcher.test(url) ? can.deparam(url.slice(1)) : {}; - }, - /** - * @hide - * A can.Map that represents the state of the history. - */ - data: stringCoercingMapDecorator(new can.Map({})), - map: function(data){ - var appState; - // appState is a can.Map constructor function - if(data.prototype instanceof can.Map){ - appState = new data(); - } - // appState is an instance of can.Map - else { - appState = data; - } - - can.route.data = stringCoercingMapDecorator(appState); - }, - /** - * @property {Object} routes - * @hide - * - * A list of routes recognized by the router indixed by the url used to add it. - * Each route is an object with these members: - * - * - test - A regular expression that will match the route when variable values - * are present; i.e. for :page/:type the `RegExp` is /([\w\.]*)/([\w\.]*)/ which - * will match for any value of :page and :type (word chars or period). - * - * - route - The original URL, same as the index for this entry in routes. - * - * - names - An array of all the variable names in this route - * - * - defaults - Default values provided for the variables or an empty object. - * - * - length - The number of parts in the URL separated by '/'. - */ - routes: {}, - /** - * @function can.route.ready ready - * @parent can.route.static - * - * Initialize can.route. - * - * @signature `can.route.ready()` - * - * Sets up the two-way binding between the hash and the can.route observable map and - * sets the can.route map to its initial values. - * - * @return {can.route} The `can.route` object. - * - * @body - * - * ## Use - * - * After setting all your routes, call can.route.ready(). - * - * can.route("overview/:dateStart-:dateEnd"); - * can.route(":type/:id") - * can.route.ready() - */ - ready: function (val) { - if (val !== true) { - can.route._setup(); - if(can.isBrowserWindow || can.isWebWorker) { - can.route.setState(); - } - } - return can.route; - }, - /** - * @function can.route.url url - * @parent can.route.static - * @signature `can.route.url( data [, merge] )` - * - * Make a URL fragment that when set to window.location.hash will update can.route's properties - * to match those in `data`. - * - * @param {Object} data The data to populate the route with. - * @param {Boolean} [merge] Whether the given options should be merged into the current state of the route. - * @return {String} The route URL and query string. - * - * @body - * Similar to [can.route.link], but instead of creating an anchor tag, `can.route.url` creates - * only the URL based on the route options passed into it. - * - * can.route.url( { type: "videos", id: 5 } ) - * // -> "#!type=videos&id=5" - * - * If a route matching the provided data is found the URL is built from the data. Any remaining - * data is added at the end of the URL as & separated key/value parameters. - * - * can.route(":type/:id") - * - * can.route.url( { type: "videos", id: 5 } ) // -> "#!videos/5" - * can.route.url( { type: "video", id: 5, isNew: false } ) - * // -> "#!video/5&isNew=false" - */ - url: function (options, merge) { - - if (merge) { - can.__observe(eventsObject,"__url"); - options = can.extend({}, can.route.deparam(can.route._call("matchingPartOfURL")), options); - } - return can.route._call("root") + can.route.param(options); - }, - /** - * @function can.route.link link - * @parent can.route.static - * @signature `can.route.link( innerText, data, props [, merge] )` - * - * Make an anchor tag (``) that when clicked on will update can.route's properties - * to match those in `data`. - * - * @param {Object} innerText The text inside the link. - * @param {Object} data The data to populate the route with. - * @param {Object} props Properties for the anchor other than `href`. - * @param {Boolean} [merge] Whether the given options should be merged into the current state of the route. - * @return {String} A string with an anchor tag that points to the populated route. - * - * @body - * Creates and returns an anchor tag with an href of the route - * attributes passed into it, as well as any properties desired - * for the tag. - * - * can.route.link( "My videos", { type: "videos" }, {}, false ) - * // -> My videos - * - * Other attributes besides href can be added to the anchor tag - * by passing in a data object with the attributes desired. - * - * can.route.link( "My videos", { type: "videos" }, - * { className: "new" }, false ) - * // -> My Videos - * - * It is possible to utilize the current route options when making anchor - * tags in order to make your code more reusable. If merge is set to true, - * the route options passed into `can.route.link` will be passed into the - * current ones. - * - * location.hash = "#!type=videos" - * can.route.link( "The zoo", { id: 5 }, true ) - * // -> The zoo - * - * location.hash = "#!type=pictures" - * can.route.link( "The zoo", { id: 5 }, true ) - * // -> The zoo - * - * - */ - link: function (name, options, props, merge) { - return "" + name + ""; - }, - /** - * @function can.route.current current - * @parent can.route.static - * @signature `can.route.current( data )` - * - * Check if data represents the current route. - * - * @param {Object} data Data to check agains the current route. - * @return {Boolean} Whether the data matches the current URL. - * - * @body - * Checks the page's current URL to see if the route represents the options passed - * into the function. - * - * Returns true if the options respresent the current URL. - * - * can.route.attr('id', 5) // location.hash -> "#!id=5" - * can.route.current({ id: 5 }) // -> true - * can.route.current({ id: 5, type: 'videos' }) // -> false - * - * can.route.attr('type', 'videos') - * // location.hash -> #!id=5&type=videos - * can.route.current({ id: 5, type: 'videos' }) // -> true - */ - current: function (options) { - // "reads" the url so the url is live-bindable. - can.__observe(eventsObject,"__url"); - return this._call("matchingPartOfURL") === can.route.param(options); - }, - bindings: { - hashchange: { - paramsMatcher: paramsMatcher, - querySeparator: "&", - // don't greedily match slashes in routing rules - matchSlashes: false, - bind: function () { - can.bind.call(window, 'hashchange', setState); - }, - unbind: function () { - can.unbind.call(window, 'hashchange', setState); - }, - // Gets the part of the url we are determinging the route from. - // For hashbased routing, it's everything after the #, for - // pushState it's configurable - matchingPartOfURL: function () { - var loc = can.route.location || location; - return loc.href.split(/#!?/)[1] || ""; - }, - // gets called with the serialized can.route data after a route has changed - // returns what the url has been updated to (for matching purposes) - setURL: function (path) { - if(location.hash !== "#" + path) { - location.hash = "!" + path; - } - return path; - }, - root: "#!" - } - }, - defaultBinding: "hashchange", - currentBinding: null, - // ready calls setup - // setup binds and listens to data changes - // bind listens to whatever you should be listening to - // data changes tries to set the path - - // we need to be able to - // easily kick off calling setState - // teardown whatever is there - // turn on a particular binding - - // called when the route is ready - _setup: function () { - if (!can.route.currentBinding) { - can.route._call("bind"); - can.route.bind("change", onRouteDataChange); - can.route.currentBinding = can.route.defaultBinding; - } - }, - _teardown: function () { - if (can.route.currentBinding) { - can.route._call("unbind"); - can.route.unbind("change", onRouteDataChange); - can.route.currentBinding = null; - } - clearTimeout(timer); - changingData = 0; - }, - // a helper to get stuff from the current or default bindings - _call: function () { - var args = can.makeArray(arguments), - prop = args.shift(), - binding = can.route.bindings[can.route.currentBinding || can.route.defaultBinding], - method = binding[prop]; - if (method.apply) { - return method.apply(binding, args); - } else { - return method; - } - } -}); - -// The functions in the following list applied to `can.route` (e.g. `can.route.attr('...')`) will -// instead act on the `can.route.data` observe. -each(['bind', 'unbind', 'on', 'off', 'delegate', 'undelegate', 'removeAttr', 'compute', '_get', '___get','each'], function (name) { - can.route[name] = function () { - // `delegate` and `undelegate` require - // the `can/map/delegate` plugin - if (!can.route.data[name]) { - return; - } - - return can.route.data[name].apply(can.route.data, arguments); - }; -}); - -can.route.attr = function () { - return can.route.data.attr.apply(can.route.data, arguments); -}; - -//Allow for overriding of route batching by can.transaction -can.route.batch = can.batch; - -var // Deparameterizes the portion of the hash of interest and assign the -// values to the `can.route.data` removing existing values no longer in the hash. -// setState is called typically by hashchange which fires asynchronously -// So it's possible that someone started changing the data before the -// hashchange event fired. For this reason, it will not set the route data -// if the data is changing or the hash already matches the hash that was set. -setState = can.route.setState = function () { - var hash = can.route._call("matchingPartOfURL"); - var oldParams = curParams; - curParams = can.route.deparam(hash); - - // if the hash data is currently changing, or - // the hash is what we set it to anyway, do NOT change the hash - if (!changingData || hash !== lastHash) { - can.route.batch.start(); - recursiveClean(oldParams, curParams, can.route.data); - - can.route.attr(curParams); - // trigger a url change so its possible to live-bind on url-based changes - can.route.batch.trigger(eventsObject,"__url",[hash, lastHash]); - can.route.batch.stop(); - } -}; - -var recursiveClean = function(old, cur, data){ - for(var attr in old){ - if(cur[attr] === undefined){ - data.removeAttr(attr); - } - else if(Object.prototype.toString.call(old[attr]) === "[object Object]") { - recursiveClean( old[attr], cur[attr], data.attr(attr) ); - } - } -}; - -module.exports = exports = can.route; +module.exports = can.route = require('can-route'); diff --git a/route/route_test.js b/route/route_test.js index f428b0367bf..ca181b8f55b 100644 --- a/route/route_test.js +++ b/route/route_test.js @@ -1,958 +1 @@ -/* jshint asi:true*/ -require('can/route/route'); -require('can/map/define/define'); -require('can/test/test'); -require('steal-qunit'); - -QUnit.module("can/route", { - setup: function () { - can.route._teardown(); - can.route.defaultBinding = "hashchange"; - } -}) - -if (("onhashchange" in window)) { - -test("deparam", function () { - can.route.routes = {}; - can.route(":page", { - page: "index" - }); - - var obj = can.route.deparam("can.Control"); - deepEqual(obj, { - page: "can.Control", - route: ":page" - }); - - obj = can.route.deparam(""); - deepEqual(obj, { - page: "index", - route: ":page" - }); - - obj = can.route.deparam("can.Control&where=there"); - deepEqual(obj, { - page: "can.Control", - where: "there", - route: ":page" - }); - - can.route.routes = {}; - can.route(":page/:index", { - page: "index", - index: "foo" - }); - - obj = can.route.deparam("can.Control/&where=there"); - deepEqual(obj, { - page: "can.Control", - index: "foo", - where: "there", - route: ":page/:index" - }, "default value and queryparams"); -}); - -test("deparam of invalid url", function () { - var obj; - can.route.routes = {}; - can.route("pages/:var1/:var2/:var3", { - var1: 'default1', - var2: 'default2', - var3: 'default3' - }); - - // This path does not match the above route, and since the hash is not - // a &key=value list there should not be data. - obj = can.route.deparam("pages//"); - deepEqual(obj, {}); - - // A valid path with invalid parameters should return the path data but - // ignore the parameters. - obj = can.route.deparam("pages/val1/val2/val3&invalid-parameters"); - deepEqual(obj, { - var1: 'val1', - var2: 'val2', - var3: 'val3', - route: "pages/:var1/:var2/:var3" - }); -}); - -test("deparam of url with non-generated hash (manual override)", function () { - var obj; - can.route.routes = {}; - - // This won't be set like this by route, but it could easily happen via a - // user manually changing the URL or when porting a prior URL structure. - obj = can.route.deparam("page=foo&bar=baz&where=there"); - deepEqual(obj, { - page: 'foo', - bar: 'baz', - where: 'there' - }); -}); - -test("param", function () { - can.route.routes = {}; - can.route("pages/:page", { - page: "index" - }) - - var res = can.route.param({ - page: "foo" - }); - equal(res, "pages/foo") - - res = can.route.param({ - page: "foo", - index: "bar" - }); - equal(res, "pages/foo&index=bar") - - can.route("pages/:page/:foo", { - page: "index", - foo: "bar" - }) - - res = can.route.param({ - page: "foo", - foo: "bar", - where: "there" - }); - equal(res, "pages/foo/&where=there") - - // There is no matching route so the hash should be empty. - res = can.route.param({}); - equal(res, "") - - can.route.routes = {}; - - res = can.route.param({ - page: "foo", - bar: "baz", - where: "there" - }); - equal(res, "&page=foo&bar=baz&where=there") - - res = can.route.param({}); - equal(res, "") -}); - -test("symmetry", function () { - can.route.routes = {}; - - var obj = { - page: "=&[]", - nestedArray: ["a"], - nested: { - a: "b" - } - } - - var res = can.route.param(obj) - - var o2 = can.route.deparam(res) - deepEqual(o2, obj) -}); - -test("light param", function () { - can.route.routes = {}; - can.route(":page", { - page: "index" - }) - - var res = can.route.param({ - page: "index" - }); - equal(res, "") - - can.route("pages/:p1/:p2/:p3", { - p1: "index", - p2: "foo", - p3: "bar" - }) - - res = can.route.param({ - p1: "index", - p2: "foo", - p3: "bar" - }); - equal(res, "pages///") - - res = can.route.param({ - p1: "index", - p2: "baz", - p3: "bar" - }); - equal(res, "pages//baz/") -}); - -test('param doesnt add defaults to params', function () { - can.route.routes = {}; - - can.route("pages/:p1", { - p2: "foo" - }) - var res = can.route.param({ - p1: "index", - p2: "foo" - }); - equal(res, "pages/index") -}) - -test("param-deparam", function () { - - can.route(":page/:type", { - page: "index", - type: "foo" - }) - - var data = { - page: "can.Control", - type: "document", - bar: "baz", - where: "there" - }; - var res = can.route.param(data); - var obj = can.route.deparam(res); - delete obj.route - deepEqual(obj, data) - data = { - page: "can.Control", - type: "foo", - bar: "baz", - where: "there" - }; - res = can.route.param(data); - obj = can.route.deparam(res); - delete obj.route; - deepEqual(data, obj) - - data = { - page: " a ", - type: " / " - }; - res = can.route.param(data); - obj = can.route.deparam(res); - delete obj.route; - deepEqual(obj, data, "slashes and spaces") - - data = { - page: "index", - type: "foo", - bar: "baz", - where: "there" - }; - res = can.route.param(data); - obj = can.route.deparam(res); - delete obj.route; - deepEqual(data, obj) - - can.route.routes = {}; - - data = { - page: "foo", - bar: "baz", - where: "there" - }; - res = can.route.param(data); - obj = can.route.deparam(res); - deepEqual(data, obj) -}) - -test("deparam-param", function () { - can.route.routes = {}; - can.route(":foo/:bar", { - foo: 1, - bar: 2 - }); - var res = can.route.param({ - foo: 1, - bar: 2 - }); - equal(res, "/", "empty slash") - - var deparamed = can.route.deparam("/") - deepEqual(deparamed, { - foo: 1, - bar: 2, - route: ":foo/:bar" - }) -}) - -test("precident", function () { - can.route.routes = {}; - can.route(":who", { - who: "index" - }); - can.route("search/:search"); - - var obj = can.route.deparam("can.Control"); - deepEqual(obj, { - who: "can.Control", - route: ":who" - }); - - obj = can.route.deparam("search/can.Control"); - deepEqual(obj, { - search: "can.Control", - route: "search/:search" - }, "bad deparam"); - - equal(can.route.param({ - search: "can.Control" - }), - "search/can.Control", "bad param"); - - equal(can.route.param({ - who: "can.Control" - }), - "can.Control"); -}); - -test("better matching precident", function () { - can.route.routes = {}; - can.route(":type", { - who: "index" - }); - can.route(":type/:id"); - - equal(can.route.param({ - type: "foo", - id: "bar" - }), - "foo/bar"); -}) - -test("linkTo", function () { - can.route.routes = {}; - can.route(":foo"); - var res = can.route.link("Hello", { - foo: "bar", - baz: 'foo' - }); - equal(res, 'Hello'); -}); - -test("param with route defined", function () { - can.route.routes = {}; - can.route("holler") - can.route("foo"); - - var res = can.route.param({ - foo: "abc", - route: "foo" - }); - - equal(res, "foo&foo=abc") -}); - -test("route endings", function () { - can.route.routes = {}; - can.route("foo", { - foo: true - }); - can.route("food", { - food: true - }); - - var res = can.route.deparam("food") - ok(res.food, "we get food back") - -}); - -test("strange characters", function () { - can.route.routes = {}; - can.route(":type/:id"); - var res = can.route.deparam("foo/" + encodeURIComponent("\/")) - equal(res.id, "\/") - res = can.route.param({ - type: "bar", - id: "\/" - }); - equal(res, "bar/" + encodeURIComponent("\/")) -}); - -test("empty default is matched even if last", function () { - - can.route.routes = {}; - can.route(":who"); - can.route("", { - foo: "bar" - }) - - var obj = can.route.deparam(""); - deepEqual(obj, { - foo: "bar", - route: "" - }); -}); - -test("order matched", function () { - can.route.routes = {}; - can.route(":foo"); - can.route(":bar") - - var obj = can.route.deparam("abc"); - deepEqual(obj, { - foo: "abc", - route: ":foo" - }); -}); - -test("param order matching", function () { - can.route.routes = {}; - can.route("", { - bar: "foo" - }); - can.route("something/:bar"); - var res = can.route.param({ - bar: "foo" - }); - equal(res, "", "picks the shortest, best match"); - - // picks the first that matches everything ... - can.route.routes = {}; - - can.route(":recipe", { - recipe: "recipe1", - task: "task3" - }); - - can.route(":recipe/:task", { - recipe: "recipe1", - task: "task3" - }); - - res = can.route.param({ - recipe: "recipe1", - task: "task3" - }); - - equal(res, "", "picks the first match of everything"); - - res = can.route.param({ - recipe: "recipe1", - task: "task2" - }); - equal(res, "/task2") -}); - -test("dashes in routes", function () { - can.route.routes = {}; - can.route(":foo-:bar"); - - var obj = can.route.deparam("abc-def"); - deepEqual(obj, { - foo: "abc", - bar: "def", - route: ":foo-:bar" - }); - - window.location.hash = "qunit-fixture"; - window.location.hash = ""; -}); -var teardownRouteTest; -var setupRouteTest = function(callback){ - - var testarea = document.getElementById('qunit-fixture'); - var iframe = document.createElement('iframe'); - stop(); - window.routeTestReady = function(){ - var args = can.makeArray(arguments) - args.unshift(iframe); - callback.apply(null, args); - }; - iframe.src = can.test.path("route/testing.html?"+Math.random()); - testarea.appendChild(iframe); - teardownRouteTest = function(){ - setTimeout(function(){ - can.remove(can.$(iframe)); - setTimeout(function(){ - start(); - },10); - },1); - }; -}; - - -if (typeof steal !== 'undefined') { - test("listening to hashchange (#216, #124)", function () { - - setupRouteTest(function (iframe, iCanRoute) { - - ok(!iCanRoute.attr('bla'), 'Value not set yet'); - - iCanRoute.bind('change', function () { - - equal(iCanRoute.attr('bla'), 'blu', 'Got route change event and value is as expected'); - teardownRouteTest(); - }); - - iCanRoute.ready(); - - setTimeout(function () { - - iframe.src = iframe.src + '#!bla=blu'; - }, 10); - }); - - }); - - test("initial route fires twice", function () { - stop(); - expect(1); - window.routeTestReady = function (iCanRoute, loc) { - iCanRoute("", {}); - iCanRoute.bind('change', function(){ - ok(true, 'change triggered once') - start(); - }); - iCanRoute.ready(); - } - var iframe = document.createElement('iframe'); - iframe.src = can.test.path("route/testing.html?5"); - can.$("#qunit-fixture")[0].appendChild(iframe); - }); - - test("removing things from the hash", function () { - - setupRouteTest(function (iframe, iCanRoute, loc) { - - iCanRoute.bind('change', function () { - - equal(iCanRoute.attr('foo'), 'bar', 'expected value'); - iCanRoute.unbind('change'); - iCanRoute.bind('change', function(){ - - equal(iCanRoute.attr('personId'), '3', 'personId'); - equal(iCanRoute.attr('foo'), undefined, 'unexpected value'); - iCanRoute.unbind('change'); - - teardownRouteTest(); - }); - setTimeout(function () { - iframe.contentWindow.location.hash = '#!personId=3'; - }, 100); - - }); - iCanRoute.ready(); - setTimeout(function () { - - iframe.contentWindow.location.hash = '#!foo=bar'; - }, 100); - }); - }); - - test("can.route.map: conflicting route values, hash should win", function(){ - setupRouteTest(function (iframe, iCanRoute, loc) { - - iCanRoute(":type/:id"); - var AppState = can.Map.extend(); - var appState = new AppState({type: "dog", id: '4'}); - - iCanRoute.map(appState); - - loc.hash = "#!cat/5"; - iCanRoute.ready(); - - setTimeout(function () { - - var after = loc.href.substr(loc.href.indexOf("#")); - equal(after, "#!cat/5", "same URL"); - equal(appState.attr("type"), "cat", "conflicts should be won by the URL"); - equal(appState.attr("id"), "5", "conflicts should be won by the URL"); - teardownRouteTest(); - - }, 30); - - }); - }); - - test("can.route.map: route is initialized from URL first, then URL params are added from can.route.data", function(){ - setupRouteTest(function (iframe, iCanRoute, loc) { - - iCanRoute(":type/:id"); - var AppState = can.Map.extend(); - var appState = new AppState({section: 'home'}); - - iCanRoute.map(appState); - loc.hash = "#!cat/5"; - iCanRoute.ready(); - - setTimeout(function () { - - var after = loc.href.substr(loc.href.indexOf("#")); - equal(after, "#!cat/5§ion=home", "same URL"); - equal(appState.attr("type"), "cat", "hash populates the appState"); - equal(appState.attr("id"), "5", "hash populates the appState"); - equal(appState.attr("section"), "home", "appState keeps its properties"); - ok(iCanRoute.data === appState, "can.route.data is the same as appState"); - - - teardownRouteTest(); - - }, 30); - - }); - }); - - test("updating the hash", function () { - setupRouteTest(function (iframe, iCanRoute, loc) { - - iCanRoute.ready(); - iCanRoute(":type/:id"); - iCanRoute.attr({ - type: "bar", - id: "\/" - }); - - setTimeout(function () { - - var after = loc.href.substr(loc.href.indexOf("#")); - equal(after, "#!bar/" + encodeURIComponent("\/")); - - teardownRouteTest(); - - }, 30); - }); - }); - - test("sticky enough routes", function () { - - setupRouteTest(function (iframe, iCanRoute, loc) { - - iCanRoute.ready() - iCanRoute("active"); - iCanRoute(""); - - loc.hash = "#!active"; - - setTimeout(function () { - - var after = loc.href.substr(loc.href.indexOf("#")); - equal(after, "#!active"); - - teardownRouteTest(); - - }, 30); - }); - }); - - test("unsticky routes", function () { - setupRouteTest(function (iframe, iCanRoute, loc) { - iCanRoute.ready(); - iCanRoute(":type"); - iCanRoute(":type/:id"); - iCanRoute.attr({ - type: "bar" - }); - - setTimeout(function () { - var after = loc.href.substr(loc.href.indexOf("#")); - equal(after, "#!bar"); - iCanRoute.attr({ - type: "bar", - id: "\/" - }); - - // check for 1 second - var time = new Date() - setTimeout(function innerTimer() { - var after = loc.href.substr(loc.href.indexOf("#")); - if (after === "#!bar/" + encodeURIComponent("\/")) { - equal(after, "#!bar/" + encodeURIComponent("\/"), "should go to type/id"); - - teardownRouteTest(); - } else if (new Date() - time > 2000) { - ok(false, "hash is " + after); - can.remove(can.$(iframe)) - } else { - setTimeout(innerTimer, 30) - } - - }, 100); - - }, 100); - - }); - }); - - test("can.route.current is live-bindable (#1156)", function () { - setupRouteTest(function (iframe, iCanRoute, loc, win) { - iCanRoute.ready(); - var isOnTestPage = win.can.compute(function(){ - return iCanRoute.current({page: "test"}); - }); - - isOnTestPage.bind("change", function(ev,newVal){ - teardownRouteTest(); - }); - - equal(isOnTestPage(), false, "initially not on test page") - setTimeout(function(){ - iCanRoute.attr("page","test"); - },20); - }); - }); - - test("can.compute.read should not call can.route (#1154)", function () { - setupRouteTest(function (iframe, iCanRoute, loc, win) { - iCanRoute.attr("page","test"); - iCanRoute.ready(); - - var val = win.can.compute.read({route: iCanRoute},win.can.compute.read.reads("route")).value; - - setTimeout(function(){ - equal(val,iCanRoute,"read correctly"); - teardownRouteTest(); - },1); - }); - }); - - test("routes should deep clean", function() { - expect(2); - setupRouteTest(function (iframe, iCanRoute, loc) { - iCanRoute.ready(); - var hash1 = can.route.url({ - panelA: { - name: "fruit", - id: 15, - show: true - } - }); - var hash2 = can.route.url({ - panelA: { - name: "fruit", - id: 20, - read: false - } - }); - - - loc.hash = hash1; - - loc.hash = hash2; - - setTimeout(function() { - equal(iCanRoute.attr("panelA.id"), 20, "id should change"); - equal(iCanRoute.attr("panelA.show"), undefined, "show should be removed"); - - teardownRouteTest(); - }, 30); - - }); - }); - - test("updating bound can.Map causes single update with a coerced string value", function() { - expect(1); - - setupRouteTest(function (iframe, route) { - var appVM = new can.Map(); - - route.map(appVM); - route.ready(); - - appVM.bind('action', function(ev, newVal) { - strictEqual(newVal, '10'); - }); - - appVM.attr('action', 10); - - // check after 30ms to see that we only have a single call - setTimeout(function() { - teardownRouteTest(); - }, 5); - }); - }); - - test("updating unserialized prop on bound can.Map causes single update without a coerced string value", function() { - expect(1); - - setupRouteTest(function (iframe, route) { - var appVM = new (can.Map.extend({define: { - action: {serialize: false} - }}))(); - - route.map(appVM); - route.ready(); - - appVM.bind('action', function(ev, newVal) { - equal(typeof newVal, 'function'); - }); - - appVM.attr('action', function() {}); - - // check after 30ms to see that we only have a single call - setTimeout(function() { - teardownRouteTest(); - }, 5); - }); - }); - - test("hash doesn't update to itself with a !", function() { - stop(); - window.routeTestReady = function (iCanRoute, loc) { - - iCanRoute.ready(); - iCanRoute(":path"); - - iCanRoute.attr('path', 'foo'); - setTimeout(function() { - var counter = 0; - try { - equal(loc.hash, '#!foo'); - } catch(e) { - start(); - throw e; - } - - iCanRoute.bind("change", function() { - counter++; - }); - - loc.hash = "bar"; - setTimeout(function() { - try { - equal(loc.hash, '#bar'); - equal(counter, 1); //sanity check -- bindings only ran once before this change. - } finally { - start(); - } - }, 100); - }, 100); - }; - var iframe = document.createElement('iframe'); - iframe.src = can.test.path("route/testing.html?1"); - can.$("#qunit-fixture")[0].appendChild(iframe); - }); - - -} - -test("escaping periods", function () { - - can.route.routes = {}; - can.route(":page\\.html", { - page: "index" - }); - - var obj = can.route.deparam("can.Control.html"); - deepEqual(obj, { - page: "can.Control", - route: ":page\\.html" - }); - - equal(can.route.param({ - page: "can.Control" - }), "can.Control.html"); - -}); - -if (typeof require === 'undefined') { - - test("correct stringing", function () { - setupRouteTest(function(iframe, route) { - route.routes = {}; - - route.attr('number', 1); - propEqual(route.attr(), { - 'number': "1" - }); - - route.attr({ - bool: true - }, true); - - propEqual(route.attr(), { - 'bool': "true" - }); - - route.attr({ - string: "hello" - }, true); - propEqual(route.attr(), { - 'string': "hello" - }); - - route.attr({ - array: [1, true, "hello"] - }, true); - propEqual(route.attr(), { - 'array': ["1", "true", "hello"] - }); - - route.attr({ - number: 1, - bool: true, - string: "hello", - array: [2, false, "world"], - obj: { - number: 3, - array: [4, true] - } - }, true); - - propEqual(route.attr(), { - number: "1", - bool: "true", - string: "hello", - array: ["2", "false", "world"], - obj: { - number: "3", - array: ["4", "true"] - } - }); - - route.routes = {}; - route(":type/:id"); - - route.attr({ - type: 'page', - id: 10, - sort_by_name: true - }, true); - - propEqual(route.attr(), { - type: "page", - id: "10", - sort_by_name: "true" - }); - - teardownRouteTest(); - }); - }); - -} - -test("on/off binding", function () { - can.route.routes = {}; - expect(1) - - can.route.on('foo', function () { - ok(true, "foo called"); - - can.route.off('foo'); - - can.route.attr('foo', 'baz'); - }); - - can.route.attr('foo', 'bar'); -}); - -test("two way binding can.route.map with can.Map instance", function(){ - expect(1); - var AppState = can.Map.extend(); - var appState = new AppState(); - - can.route.map(appState); - - can.route.on('change', function(){ - equal(can.route.attr('name'), 'Brian', 'appState is bound to can.route'); - can.route.off('change'); - appState.removeAttr('name'); - }); - appState.attr('name', 'Brian'); -}); - -} +require('can-route/test/route-test'); diff --git a/test/dist.html b/test/dist.html new file mode 100644 index 00000000000..ed64bc39777 --- /dev/null +++ b/test/dist.html @@ -0,0 +1,8 @@ +CanJS distributable tests + + + +
                                      diff --git a/test/test.js b/test/test.js index a899026d568..7224af57a12 100644 --- a/test/test.js +++ b/test/test.js @@ -1,8 +1,9 @@ require('../construct/construct_test'); -require('../component/component_test'); -require('../compute/compute_test'); -require('../control/control_test'); -require('../event/event_test'); -require('../list/list_test'); -require('../map/map_test'); +// require('../component/component_test'); +// require('../compute/compute_test'); +// require('../control/control_test'); +// require('../event/event_test'); +// require('../list/list_test'); +// require('../map/map_test'); // require('../model/model_test'); +// require('../route/route_test'); From a4f3f2ed19dfa0b971feefc3b12ad7f260fabfc1 Mon Sep 17 00:00:00 2001 From: David Luecke Date: Tue, 10 May 2016 09:58:43 -0700 Subject: [PATCH 05/10] Cleaning up can-util --- model/model.js | 3 - model/model_test.js | 1 - package.json | 7 +- route/pushstate/pushstate.js | 239 +--- route/pushstate/pushstate_test.js | 967 +--------------- test/dist.html | 17 +- test/test.js | 16 +- util/all.js | 10 - util/array/array_test.js | 2 - util/array/diff.js | 64 -- util/array/diff_test.js | 74 -- util/array/each.js | 51 - util/array/each_test.js | 30 - util/array/isArrayLike.js | 18 - util/array/makeArray.js | 11 - util/array/test.html | 3 - util/attr/attr.js | 274 ----- util/attr/attr_test.js | 212 ---- util/attr/test.html | 10 - util/batch/batch.js | 295 ----- util/batch/batch.md | 158 --- util/bind/bind.js | 53 - util/deferred.js | 175 --- util/demos/demos.css | 153 --- util/demos/observer.js | 354 ------ util/destroyed.js | 42 - util/doc/contentArray.md | 30 - util/doc/frag.md | 24 - util/doc/import.md | 34 - util/domless/domless.js | 140 --- util/domless/domless_test.js | 10 - util/event.js | 6 - util/events/attributes.md | 76 -- util/events/events.md | 11 - util/events/inserted.md | 51 - util/events/removed.md | 54 - util/fixture/doc/create.md | 15 - util/fixture/doc/delay.md | 11 - util/fixture/doc/destroy.md | 17 - util/fixture/doc/find.md | 15 - util/fixture/doc/findAll.md | 44 - util/fixture/doc/findOne.md | 17 - util/fixture/doc/fixture.md | 287 ----- util/fixture/doc/on.md | 9 - util/fixture/doc/rand.md | 38 - util/fixture/doc/requestHandler.md | 99 -- util/fixture/doc/reset.md | 18 - util/fixture/doc/responseHandler.md | 40 - util/fixture/doc/rootUrl.md | 6 - util/fixture/doc/store.md | 120 -- util/fixture/doc/storeTypes.md | 35 - util/fixture/doc/update.md | 16 - util/fixture/doc/xhr.md | 30 - util/fixture/fixture.js | 765 +------------ util/fixture/fixture_test.js | 652 +---------- util/fixture/fixtures/foo.json | 1 - util/fixture/fixtures/foobar.json | 3 - util/fixture/fixtures/messages.html | 31 - util/fixture/fixtures/remove.json | 3 - util/fixture/fixtures/stuff.3.json | 3 - util/fixture/fixtures/test.json | 3 - util/fixture/test.html | 3 - util/fragment.js | 89 -- util/func.js | 1019 ----------------- util/function/function.js | 37 - util/function/function_test.js | 41 - util/hashchange.js | 16 - util/inserted/inserted.js | 72 -- util/inserted/inserted_test.js | 27 - util/jquery/jquery.js | 296 ----- util/jquery/jquery_test.js | 27 - util/jquery/test.html | 3 - util/library.js | 3 - util/mvc.js | 2 - util/object/isplain/isplain.js | 30 - util/object/isplain/isplain_test.js | 59 - util/object/object.js | 185 --- util/object/object_test.js | 142 --- util/string/deparam/deparam.js | 44 - util/string/deparam/deparam_test.js | 55 - util/string/rsplit/rsplit.js | 27 - util/string/string.js | 132 --- util/string/string_test.js | 394 ------- util/tests/index.html | 3 - util/tests/tests.js | 4 - util/tests/tests_test.js | 4 - util/util.md | 46 - util/util_test.js | 1 + util/vdom/build_fragment/build_fragment.js | 18 - .../build_fragment/build_fragment_test.js | 1 - util/vdom/build_fragment/make_parser.js | 42 - util/vdom/document/document.js | 11 - util/vdom/document/document_test.js | 10 - util/vdom/document/test.html | 11 - util/vdom/test/dom.js | 67 -- util/vdom/test/hello-world.js | 8 - util/vdom/test/main.html | 6 - util/vdom/test/main.stache | 2 - util/vdom/test/node_test.js | 12 - util/vdom/vdom.js | 32 - util/view_model/view_model.js | 42 - view/autorender/autorender.html | 5 - view/autorender/autorender.md | 214 ---- view/autorender/can-autorender.md | 29 - view/autorender/test.html | 3 - view/autorender/tests/basics.html | 32 - view/autorender/tests/basics.js | 15 - view/autorender/tests/requirejs-basics.html | 39 - view/autorender/tests/requirejs-basics.js | 15 - view/autorender/tests/standalone-basics.html | 35 - view/autorender/tests/standalone-basics.js | 15 - view/autorender/tests/steal-viewmodel.html | 37 - 112 files changed, 35 insertions(+), 9380 deletions(-) delete mode 100644 model/model.js delete mode 100644 model/model_test.js delete mode 100644 util/all.js delete mode 100644 util/array/array_test.js delete mode 100644 util/array/diff.js delete mode 100644 util/array/diff_test.js delete mode 100644 util/array/each.js delete mode 100644 util/array/each_test.js delete mode 100644 util/array/isArrayLike.js delete mode 100644 util/array/makeArray.js delete mode 100644 util/array/test.html delete mode 100644 util/attr/attr.js delete mode 100644 util/attr/attr_test.js delete mode 100644 util/attr/test.html delete mode 100644 util/batch/batch.js delete mode 100644 util/batch/batch.md delete mode 100644 util/bind/bind.js delete mode 100644 util/deferred.js delete mode 100644 util/demos/demos.css delete mode 100644 util/demos/observer.js delete mode 100644 util/destroyed.js delete mode 100644 util/doc/contentArray.md delete mode 100644 util/doc/frag.md delete mode 100644 util/doc/import.md delete mode 100644 util/domless/domless.js delete mode 100644 util/domless/domless_test.js delete mode 100644 util/event.js delete mode 100644 util/events/attributes.md delete mode 100644 util/events/events.md delete mode 100644 util/events/inserted.md delete mode 100644 util/events/removed.md delete mode 100644 util/fixture/doc/create.md delete mode 100644 util/fixture/doc/delay.md delete mode 100644 util/fixture/doc/destroy.md delete mode 100644 util/fixture/doc/find.md delete mode 100644 util/fixture/doc/findAll.md delete mode 100644 util/fixture/doc/findOne.md delete mode 100644 util/fixture/doc/fixture.md delete mode 100644 util/fixture/doc/on.md delete mode 100644 util/fixture/doc/rand.md delete mode 100644 util/fixture/doc/requestHandler.md delete mode 100644 util/fixture/doc/reset.md delete mode 100644 util/fixture/doc/responseHandler.md delete mode 100644 util/fixture/doc/rootUrl.md delete mode 100644 util/fixture/doc/store.md delete mode 100644 util/fixture/doc/storeTypes.md delete mode 100644 util/fixture/doc/update.md delete mode 100644 util/fixture/doc/xhr.md delete mode 100644 util/fixture/fixtures/foo.json delete mode 100644 util/fixture/fixtures/foobar.json delete mode 100644 util/fixture/fixtures/messages.html delete mode 100644 util/fixture/fixtures/remove.json delete mode 100644 util/fixture/fixtures/stuff.3.json delete mode 100644 util/fixture/fixtures/test.json delete mode 100644 util/fixture/test.html delete mode 100644 util/fragment.js delete mode 100644 util/func.js delete mode 100644 util/function/function.js delete mode 100644 util/function/function_test.js delete mode 100644 util/hashchange.js delete mode 100644 util/inserted/inserted.js delete mode 100644 util/inserted/inserted_test.js delete mode 100644 util/jquery/jquery.js delete mode 100644 util/jquery/jquery_test.js delete mode 100644 util/jquery/test.html delete mode 100644 util/library.js delete mode 100644 util/mvc.js delete mode 100644 util/object/isplain/isplain.js delete mode 100644 util/object/isplain/isplain_test.js delete mode 100644 util/object/object.js delete mode 100644 util/object/object_test.js delete mode 100644 util/string/deparam/deparam.js delete mode 100644 util/string/deparam/deparam_test.js delete mode 100644 util/string/rsplit/rsplit.js delete mode 100644 util/string/string.js delete mode 100644 util/string/string_test.js delete mode 100644 util/tests/index.html delete mode 100644 util/tests/tests.js delete mode 100644 util/tests/tests_test.js delete mode 100644 util/util.md create mode 100644 util/util_test.js delete mode 100644 util/vdom/build_fragment/build_fragment.js delete mode 100644 util/vdom/build_fragment/build_fragment_test.js delete mode 100644 util/vdom/build_fragment/make_parser.js delete mode 100644 util/vdom/document/document.js delete mode 100644 util/vdom/document/document_test.js delete mode 100644 util/vdom/document/test.html delete mode 100644 util/vdom/test/dom.js delete mode 100644 util/vdom/test/hello-world.js delete mode 100644 util/vdom/test/main.html delete mode 100644 util/vdom/test/main.stache delete mode 100644 util/vdom/test/node_test.js delete mode 100644 util/vdom/vdom.js delete mode 100644 util/view_model/view_model.js delete mode 100644 view/autorender/autorender.html delete mode 100644 view/autorender/autorender.md delete mode 100644 view/autorender/can-autorender.md delete mode 100644 view/autorender/test.html delete mode 100644 view/autorender/tests/basics.html delete mode 100644 view/autorender/tests/basics.js delete mode 100644 view/autorender/tests/requirejs-basics.html delete mode 100644 view/autorender/tests/requirejs-basics.js delete mode 100644 view/autorender/tests/standalone-basics.html delete mode 100644 view/autorender/tests/standalone-basics.js delete mode 100644 view/autorender/tests/steal-viewmodel.html diff --git a/model/model.js b/model/model.js deleted file mode 100644 index 97ffd520f31..00000000000 --- a/model/model.js +++ /dev/null @@ -1,3 +0,0 @@ -var can = require('../util/can'); - -module.exports = can.Model = require('can-model'); diff --git a/model/model_test.js b/model/model_test.js deleted file mode 100644 index 6991f2a58e9..00000000000 --- a/model/model_test.js +++ /dev/null @@ -1 +0,0 @@ -require('can-model/model_test'); diff --git a/package.json b/package.json index 1ab089378c4..ed8954bd489 100644 --- a/package.json +++ b/package.json @@ -25,17 +25,20 @@ "can-control": "^3.0.0-pre.1", "can-define": "^0.7.4", "can-event": "^3.0.0-pre.3", + "can-fixture": "^0.4.0-pre.2", "can-list": "^3.0.0-pre.1", "can-map": "^3.0.0-pre.2", "can-model": "^2.3.11", "can-route": "^3.0.0-pre.3", "can-simple-dom": "^0.3.0", - "can-stache": "^3.0.0-pre.1" + "can-stache": "^3.0.0-pre.1", + "can-util": "^3.0.0-pre.15" }, "devDependencies": { + "jquery": "^2.2.3", "steal": "^0.16.4", "steal-qunit": "^0.1.1", - "steal-tools": "^0.16.2", + "steal-tools": "pre", "testee": "^0.2.5" }, "scripts": { diff --git a/route/pushstate/pushstate.js b/route/pushstate/pushstate.js index ab22f30e840..3f01ed4c8a8 100644 --- a/route/pushstate/pushstate.js +++ b/route/pushstate/pushstate.js @@ -1,238 +1,3 @@ -// # can/route/pushstate/pushstate.js -// -// Plugin for `can.route` which uses browser `history.pushState` support -// to update window's pathname instead of the `hash`. -// -// It registers itself as binding on `can.route`, intercepts `click` events -// on `` elements across document and accordingly updates `can.route` state -// and window's pathname. +var can = require('../util/can'); -/*jshint maxdepth:6, scripturl:true*/ -"use strict"; -var can = require('can/util/util'); -require('can/route/route'); - -var hasPushstate = window.history && window.history.pushState; -var isFileProtocol = window.location && window.location.protocol === 'file:'; - -// Initialize plugin only if browser supports pushstate. -if (!isFileProtocol && hasPushstate) { - - // Registers itself within `can.route.bindings`. - can.route.bindings.pushstate = { - /** - * @property {String} can.route.pushstate.root - * @parent can.route.pushstate - * - * @description Configure the base url that will not be modified. - * - * @option {String} Represents the base url that pushstate will prepend to all - * routes. `root` defaults to: `"/"`. - * - * @body - * - * ## Use - * - * By default, a route like: - * - * can.route(":type/:id") - * - * Matches urls like: - * - * http://domain.com/contact/5 - * - * But sometimes, you only want to match pages within a certain directory. For - * example, an application that is a filemanager. You might want to - * specify root and routes like: - * - * can.route.pushstate.root = "/filemanager/" - * can.route("file-:fileId"); - * can.route("folder-:fileId") - * - * Which matches urls like: - * - * http://domain.com/filemanager/file-34234 - * - */ - - // Start of `location.pathname` is the root. - // (Can be configured via `can.route.bindings.pushstate.root`) - root: "/", - // don't greedily match slashes in routing rules - matchSlashes: false, - paramsMatcher: /^\?(?:[^=]+=[^&]*&)*[^=]+=[^&]*/, - querySeparator: '?', - - // ## bind - - // Intercepts clicks on `` elements and rewrites original `history` methods. - bind: function () { - if(can.isNode) { - return; - } - - // Intercept routable links. - can.delegate.call(can.$(document.documentElement), 'a', 'click', anchorClickHandler); - - // Rewrites original `pushState`/`replaceState` methods on `history` and keeps pointer to original methods - can.each(methodsToOverwrite, function (method) { - originalMethods[method] = window.history[method]; - window.history[method] = function (state, title, url) { - // Avoid doubled history states (with pushState). - var absolute = url.indexOf("http") === 0; - var searchHash = window.location.search + window.location.hash; - // If url differs from current call original histoy method and update `can.route` state. - if ((!absolute && url !== window.location.pathname + searchHash) || (absolute && url !== window.location.href + searchHash)) { - originalMethods[method].apply(window.history, arguments); - can.route.setState(); - } - }; - }); - - // Bind to `popstate` event, fires on back/forward. - can.bind.call(window, 'popstate', can.route.setState); - }, - - // ## unbind - - // Unbinds and restores original `history` methods - unbind: function () { - can.undelegate.call(can.$(document.documentElement), 'click', 'a', anchorClickHandler); - - can.each(methodsToOverwrite, function (method) { - window.history[method] = originalMethods[method]; - }); - can.unbind.call(window, 'popstate', can.route.setState); - }, - - // ## matchingPartOfURL - - // Returns matching part of url without root. - matchingPartOfURL: function () { - var root = cleanRoot(), - loc = (location.pathname + location.search), - index = loc.indexOf(root); - - return loc.substr(index + root.length); - }, - - // ## setURL - - // Updates URL by calling `pushState`. - setURL: function (path, changed) { - var method = "pushState"; - // Keeps hash if not in path. - if (includeHash && path.indexOf("#") === -1 && window.location.hash) { - path += window.location.hash; - } - if(replaceStateAttrs.length > 0) { - var toRemove = []; - for(var i = 0, l = changed.length; i < l; i++) { - if(can.inArray(changed[i], replaceStateAttrs) !== -1) { - method = "replaceState"; - } - if(can.inArray(changed[i], replaceStateAttrs.once) !== -1) { - toRemove.push(changed[i]); - } - } - if(toRemove.length > 0) { - removeAttrs(replaceStateAttrs, toRemove); - removeAttrs(replaceStateAttrs.once, toRemove); - } - } - window.history[method](null, null, can.route._call("root") + path); - } - }; - - // ## anchorClickHandler - - // Handler function for `click` events. - var anchorClickHandler = function (e) { - if (!(e.isDefaultPrevented ? e.isDefaultPrevented() : e.defaultPrevented === true)) { - // YUI calls back events triggered with this as a wrapped object. - var node = this._node || this; - // Fix for IE showing blank host, but blank host means current host. - var linksHost = node.host || window.location.host; - - if(node.href === "javascript://") { - return; - } - - // If link is within the same domain and descendant of `root` - if (window.location.host === linksHost) { - var root = cleanRoot(); - if (node.pathname.indexOf(root) === 0) { - - // Removes root from url. - var url = (node.pathname + node.search).substr(root.length); - // If a route matches update the data. - var curParams = can.route.deparam(url); - if (curParams.hasOwnProperty('route')) { - // Makes it possible to have a link with a hash. - includeHash = true; - window.history.pushState(null, null, node.href); - - // Test if you can preventDefault - // our tests can't call .click() b/c this - // freezes phantom. - if (e.preventDefault) { - e.preventDefault(); - } - } - } - } - } - }, - - // ## cleanRoot - - // Always returns clean root, without domain. - cleanRoot = function () { - var domain = location.protocol + "//" + location.host, - root = can.route._call("root"), - index = root.indexOf(domain); - if (index === 0) { - return root.substr(domain.length); - } - return root; - }, - removeAttrs = function(arr, attrs) { - var index; - for(var i = attrs.length - 1; i >= 0; i--) { - if( (index = can.inArray(attrs[i], arr)) !== -1) { - arr.splice(index, 1); - } - } - }, - // Original methods on `history` that will be overwritten - methodsToOverwrite = ['pushState', 'replaceState'], - // A place to store pointers to original `history` methods. - originalMethods = {}, - // Used to tell setURL to include the hash because we clicked on a link. - includeHash = false, - // Attributes that will cause replaceState to be called - replaceStateAttrs = []; - - // Enables plugin, by default `hashchange` binding is used. - can.route.defaultBinding = "pushstate"; - - can.extend(can.route, { - replaceStateOn: function() { - var attrs = can.makeArray(arguments); - Array.prototype.push.apply(replaceStateAttrs, attrs); - }, - replaceStateOnce: function() { - var attrs = can.makeArray(arguments); - replaceStateAttrs.once = can.makeArray(replaceStateAttrs.once); - - Array.prototype.push.apply(replaceStateAttrs.once, attrs); - can.route.replaceStateOn.apply(this, arguments); - }, - replaceStateOff: function() { - var attrs = can.makeArray(arguments); - removeAttrs(replaceStateAttrs, attrs); - } - }); -} - -module.exports = exports = can; +module.exports = can.route = require('can-route-pushstate'); diff --git a/route/pushstate/pushstate_test.js b/route/pushstate/pushstate_test.js index c3a3edea59f..e56b7870ca5 100644 --- a/route/pushstate/pushstate_test.js +++ b/route/pushstate/pushstate_test.js @@ -1,966 +1 @@ -/* jshint asi:true,scripturl:true */ -require('can/route/route'); -require('can/route/pushstate/pushstate'); -require('can/test/test'); -require('steal-qunit'); - -function eventFire(el, etype) { - var doc = el.ownerDocument, - win = doc.defaultView || doc.parentWindow; - win.can.trigger(el, etype, [], true); - /*if (el.fireEvent) { - (el.fireEvent('on' + etype)); - } else { - var evObj = el.ownerDocument.createEvent('MouseEvents'); - evObj.initEvent("click", true, true, el.ownerDocument.defaultView, - 0, 0, 0, 0, 0, false, false, false, false, 0, null); - el.dispatchEvent(evObj); - }*/ -} - -if (window.history && history.pushState) { - - QUnit.module("can/route/pushstate", { - setup: function () { - can.route._teardown(); - can.route.defaultBinding = "pushstate"; - }, - teardown: function () { - - } - }); - - test("deparam", function () { - can.route.routes = {}; - can.route(":page", { - page: "index" - }); - - var obj = can.route.deparam("can.Control"); - deepEqual(obj, { - page: "can.Control", - route: ":page" - }); - - obj = can.route.deparam(""); - deepEqual(obj, { - page: "index", - route: ":page" - }); - - obj = can.route.deparam("can.Control?where=there"); - deepEqual(obj, { - page: "can.Control", - where: "there", - route: ":page" - }); - - can.route.routes = {}; - can.route(":page/:index", { - page: "index", - index: "foo" - }); - - obj = can.route.deparam("can.Control/?where=there"); - deepEqual(obj, { - page: "can.Control", - index: "foo", - where: "there", - route: ":page/:index" - }); - }); - - test("deparam of invalid url", function () { - var obj; - - can.route.routes = {}; - can.route("pages/:var1/:var2/:var3", { - var1: 'default1', - var2: 'default2', - var3: 'default3' - }); - - // This path does not match the above route, and since the hash is not - // a &key=value list there should not be data. - obj = can.route.deparam("pages//"); - deepEqual(obj, {}); - - // A valid path with invalid parameters should return the path data but - // ignore the parameters. - obj = can.route.deparam("pages/val1/val2/val3?invalid-parameters"); - deepEqual(obj, { - var1: 'val1', - var2: 'val2', - var3: 'val3', - route: "pages/:var1/:var2/:var3" - }); - }) - - test("deparam of url with non-generated hash (manual override)", function () { - var obj; - - can.route.routes = {}; - - // This won't be set like this by route, but it could easily happen via a - // user manually changing the URL or when porting a prior URL structure. - obj = can.route.deparam("?page=foo&bar=baz&where=there"); - deepEqual(obj, { - page: 'foo', - bar: 'baz', - where: 'there' - }); - }) - - test("param", function () { - can.route.routes = {}; - can.route("pages/:page", { - page: "index" - }) - - var res = can.route.param({ - page: "foo" - }); - equal(res, "pages/foo") - - res = can.route.param({ - page: "foo", - index: "bar" - }); - equal(res, "pages/foo?index=bar") - - can.route("pages/:page/:foo", { - page: "index", - foo: "bar" - }) - - res = can.route.param({ - page: "foo", - foo: "bar", - where: "there" - }); - equal(res, "pages/foo/?where=there") - - // There is no matching route so the hash should be empty. - res = can.route.param({}); - equal(res, "") - - can.route.routes = {}; - - res = can.route.param({ - page: "foo", - bar: "baz", - where: "there" - }); - equal(res, "?page=foo&bar=baz&where=there") - - res = can.route.param({}); - equal(res, "") - }); - - test("symmetry", function () { - can.route.routes = {}; - - var obj = { - page: "=&[]", - nestedArray: ["a"], - nested: { - a: "b" - } - } - - var res = can.route.param(obj) - - var o2 = can.route.deparam(res) - deepEqual(o2, obj) - }) - - test("light param", function () { - can.route.routes = {}; - can.route(":page", { - page: "index" - }) - - var res = can.route.param({ - page: "index" - }); - equal(res, "") - - can.route("pages/:p1/:p2/:p3", { - p1: "index", - p2: "foo", - p3: "bar" - }) - - res = can.route.param({ - p1: "index", - p2: "foo", - p3: "bar" - }); - equal(res, "pages///") - - res = can.route.param({ - p1: "index", - p2: "baz", - p3: "bar" - }); - equal(res, "pages//baz/") - }); - - test('param doesnt add defaults to params', function () { - can.route.routes = {}; - - can.route("pages/:p1", { - p2: "foo" - }) - var res = can.route.param({ - p1: "index", - p2: "foo" - }); - equal(res, "pages/index") - }) - - test("param-deparam", function () { - - can.route(":page/:type", { - page: "index", - type: "foo" - }) - - var data = { - page: "can.Control", - type: "document", - bar: "baz", - where: "there" - }; - var res = can.route.param(data); - var obj = can.route.deparam(res); - delete obj.route - deepEqual(obj, data) - data = { - page: "can.Control", - type: "foo", - bar: "baz", - where: "there" - }; - res = can.route.param(data); - obj = can.route.deparam(res); - delete obj.route; - deepEqual(data, obj); - - data = { - page: " a ", - type: " / " - }; - res = can.route.param(data); - obj = can.route.deparam(res); - delete obj.route; - deepEqual(obj, data, "slashes and spaces") - - data = { - page: "index", - type: "foo", - bar: "baz", - where: "there" - }; - // adding the / should not be necessary. can.route.deparam removes / if the root starts with / - res = "/" + can.route.param(data); - obj = can.route.deparam(res); - delete obj.route; - deepEqual(data, obj); - - can.route.routes = {}; - - data = { - page: "foo", - bar: "baz", - where: "there" - }; - res = can.route.param(data); - obj = can.route.deparam(res); - deepEqual(data, obj) - }) - - test("deparam-param", function () { - can.route.routes = {}; - can.route(":foo/:bar", { - foo: 1, - bar: 2 - }); - var res = can.route.param({ - foo: 1, - bar: 2 - }); - equal(res, "/", "empty slash") - - // you really should deparam with root .. - var deparamed = can.route.deparam("//") - deepEqual(deparamed, { - foo: 1, - bar: 2, - route: ":foo/:bar" - }) - }) - - test("precident", function () { - can.route.routes = {}; - can.route(":who", { - who: "index" - }); - can.route("search/:search"); - - var obj = can.route.deparam("can.Control"); - deepEqual(obj, { - who: "can.Control", - route: ":who" - }); - - obj = can.route.deparam("search/can.Control"); - deepEqual(obj, { - search: "can.Control", - route: "search/:search" - }, "bad deparam"); - - equal(can.route.param({ - search: "can.Control" - }), - "search/can.Control", "bad param"); - - equal(can.route.param({ - who: "can.Control" - }), - "can.Control"); - }) - - test("better matching precident", function () { - can.route.routes = {}; - can.route(":type", { - who: "index" - }); - can.route(":type/:id"); - - equal(can.route.param({ - type: "foo", - id: "bar" - }), - "foo/bar"); - }) - - test("linkTo", function () { - can.route.routes = {}; - can.route("/:foo"); - var res = can.route.link("Hello", { - foo: "bar", - baz: 'foo' - }); - equal(res, 'Hello'); - }) - - test("param with route defined", function () { - can.route.routes = {}; - can.route("holler") - can.route("foo"); - - var res = can.route.param({ - foo: "abc", - route: "foo" - }); - - equal(res, "foo?foo=abc") - }) - - test("route endings", function () { - can.route.routes = {}; - can.route("foo", { - foo: true - }); - can.route("food", { - food: true - }) - - var res = can.route.deparam("food") - ok(res.food, "we get food back") - - }); - - test("strange characters", function () { - can.route.routes = {}; - can.route(":type/:id"); - var res = can.route.deparam("foo/" + encodeURIComponent("\/")) - equal(res.id, "\/") - res = can.route.param({ - type: "bar", - id: "\/" - }); - equal(res, "bar/" + encodeURIComponent("\/")) - }); - - // Start steal-only - if (typeof steal !== 'undefined') { - - var makeTestingIframe = function (callback) { - - - window.routeTestReady = function (iCanRoute, loc, history, win) { - callback({ - route: iCanRoute, - location: loc, - history: history, - window: win, - iframe: iframe - }, function () { - iframe.onload = null; - can.remove(can.$(iframe)); - delete window.routeTestReady; - }); - }; - - var iframe = document.createElement('iframe'); - iframe.src = can.test.path("route/pushstate/testing.html")+"?" + Math.random(); - can.$("#qunit-fixture")[0].appendChild(iframe); - - - }; - - test("updating the url", function () { - stop(); - makeTestingIframe(function (info, done) { - info.route.ready() - info.route("/:type/:id"); - info.route.attr({ - type: "bar", - id: "5" - }); - - setTimeout(function () { - var after = info.location.pathname; - equal(after, "/bar/5", "path is " + after); - start(); - - done(); - - }, 100); - }); - - }); - - test("sticky enough routes", function () { - stop(); - makeTestingIframe(function (info, done) { - info.route("/active"); - info.route(""); - info.history.pushState(null, null, "/active"); - - setTimeout(function () { - var after = info.location.pathname; - equal(after, "/active"); - start(); - - done(); - }, 30); - }); - }); - - test("unsticky routes", function () { - - stop(); - window.routeTestReady = function (iCanRoute, loc, iframeHistory) { - // check if we can even test this - iframeHistory.pushState(null, null, "/bar/" + encodeURIComponent("\/")); - setTimeout(function timer() { - - if ("/bar/" + encodeURIComponent("\/") === loc.pathname) { - runTest(); - - } else if (loc.pathname.indexOf("/bar/") >= 0) { - // encoding doesn't actually work - ok(true, "can't test!"); - can.remove(can.$(iframe)) - start() - } else { - setTimeout(timer, 30) - } - }, 30); - var runTest = function () { - iCanRoute.ready(); - iCanRoute("/:type"); - iCanRoute("/:type/:id"); - iCanRoute.attr({ - type: "bar" - }); - - setTimeout(function () { - var after = loc.pathname; - equal(after, "/bar", "only type is set"); - iCanRoute.attr({ - type: "bar", - id: "\/" - }); - - // check for 1 second - var time = new Date() - setTimeout(function innerTimer() { - var after = loc.pathname; - - if (after === "/bar/" + encodeURIComponent("\/")) { - equal(after, "/bar/" + encodeURIComponent("\/"), "should go to type/id"); - can.remove(can.$(iframe)) - start(); - } else if (new Date() - time > 2000) { - ok(false, "hash is " + after); - can.remove(can.$(iframe)) - } else { - setTimeout(innerTimer, 30) - } - - }, 30); - - }, 30); - }; - - }; - var iframe = document.createElement('iframe'); - iframe.src = can.test.path("route/pushstate/testing.html?1"); - can.$("#qunit-fixture")[0].appendChild(iframe); - }); - - test("clicked hashes work (#259)", function () { - - stop(); - window.routeTestReady = function (iCanRoute, loc, hist, win) { - - iCanRoute(win.location.pathname, { - page: "index" - }); - - iCanRoute(":type/:id"); - iCanRoute.ready(); - - window.win = win; - var link = win.document.createElement("a"); - link.href = "/articles/17#references"; - link.innerHTML = "Click Me" - - win.document.body.appendChild(link); - - win.can.trigger(win.can.$(link), "click") - - //link.click() - - setTimeout(function () { - - deepEqual(can.extend({}, iCanRoute.attr()), { - type: "articles", - id: "17", - route: ":type/:id" - }, "articles are right") - - equal(win.location.hash, "#references", "includes hash"); - - start(); - - can.remove(can.$(iframe)) - - }, 100); - }; - var iframe = document.createElement('iframe'); - iframe.src = can.test.path("route/pushstate/testing.html"); - can.$("#qunit-fixture")[0].appendChild(iframe); - }); - - test("javascript:// links do not get pushstated", function(){ - stop(); - makeTestingIframe(function (info, done) { - info.route(":type", { type: "yay" }); - info.route.ready(); - - - var window = info.window; - var link = window.document.createElement("a"); - link.href = "javascript://"; - link.innerHTML = "Click Me"; - - window.document.body.appendChild(link); - try { - window.can.trigger(window.can.$(link), "click"); - ok(true, "Clicking javascript:// anchor did not cause a security exception"); - } catch(err) { - ok(false, "Clicking javascript:// anchor caused a security exception"); - } - - start(); - done(); - }); - }); - - if(window.parent === window) { - // we can't call back if running in multiple frames - test("no doubled history states (#656)", function () { - stop(); - - window.routeTestReady = function (iCanRoute, loc, hist, win) { - var root = loc.pathname.substr(0, loc.pathname.lastIndexOf("/") + 1); - var stateTest = -1, - message; - - function nextStateTest() { - stateTest++; - win.can.route.attr("page", "start"); - - setTimeout(function () { - if (stateTest === 0) { - message = "can.route.attr"; - win.can.route.attr("page", "test"); - } else if (stateTest === 1) { - message = "history.pushState"; - win.history.pushState(null, null, root + "test/"); - } else if (stateTest === 2) { - message = "link click"; - var link = win.document.createElement("a"); - link.href = root + "test/"; - link.innerText = "asdf"; - win.document.body.appendChild(link); - win.can.trigger(win.can.$(link), "click"); - } else { - start(); - can.remove(can.$(iframe)); - return; - } - - setTimeout(function () { - win.history.back(); - setTimeout(function () { - var path = win.location.pathname; - // strip root for deparam - if (path.indexOf(root) === 0) { - path = path.substr(root.length); - } - equal(win.can.route.deparam(path) - .page, "start", message + " passed"); - nextStateTest(); - }, 200); - }, 200); - - }, 200); - } - - win.can.route.bindings.pushstate.root = root; - win.can.route(":page/"); - win.can.route.ready(); - nextStateTest(); - }; - - var iframe = document.createElement("iframe"); - iframe.src = can.test.path("route/pushstate/testing.html"); - can.$("#qunit-fixture")[0].appendChild(iframe); - }); - - - test("root can include the domain", function () { - // Allows bindings.pushstate.root to handle the full domain instead of just the pathname - stop(); - makeTestingIframe(function(info, done){ - info.route.bindings.pushstate.root = can.test.path("route/pushstate/testing.html", true).replace("route/pushstate/testing.html", ""); - info.route(":module/:plugin/:page\\.html"); - info.route.ready(); - - setTimeout(function(){ - equal(info.route.attr('module'), 'route', 'works'); - start(); - - done(); - }, 100); - }); - }); - - test("URL's don't greedily match", function () { - stop(); - makeTestingIframe(function(info, done){ - info.route.bindings.pushstate.root = can.test.path("route/pushstate/testing.html", true).replace("route/pushstate/testing.html", ""); - info.route(":module\\.html"); - info.route.ready(); - - setTimeout(function(){ - ok(!info.route.attr('module'), 'there is no route match'); - start(); - - done(); - }, 100); - }); - }); - - } - - test("routed links must descend from pushstate root (#652)", 1, function () { - - - stop(); - - var setupRoutesAndRoot = function (iCanRoute, root) { - iCanRoute(":section/"); - iCanRoute(":section/:sub/"); - iCanRoute.bindings.pushstate.root = root; - iCanRoute.ready(); - }; - - - var createLink = function (win, url) { - var link = win.document.createElement("a"); - link.href = link.innerHTML = url; - win.document.body.appendChild(link); - return link; - }; - - // The following makes sure a link that is not "rooted" will - // behave normally and not call pushState - makeTestingIframe(function (info, done) { - setupRoutesAndRoot(info.route, "/app/"); - var link = createLink(info.window, "/route/pushstate/empty.html"); // a link to somewhere outside app - - var clickKiller = function(ev) { - if(ev.preventDefault) { - ev.preventDefault(); - } - return false; - }; - // kill the click b/c phantom doesn't like it. - can.bind.call(info.window.document,"click",clickKiller); - - info.history.pushState = function () { - ok(false, "pushState should not have been called"); - }; - - // click a link and make sure the iframe url changes - eventFire(link, "click") - - done(); - setTimeout(next, 10); - }); - - var next = function () { - makeTestingIframe(function (info, done) { - - var timer; - info.route.bind("change", function () { - clearTimeout(timer); - timer = setTimeout(function () { - // deepEqual doesn't like to compare objects from different contexts - // so we copy it - var obj = can.simpleExtend({}, info.route.attr()); - - deepEqual(obj, { - section: "something", - sub: "test", - route: ":section/:sub/" - }, "route's data is correct"); - - done(); - start(); - }, 10); - - }); - - setupRoutesAndRoot(info.route, "/app/"); - var link = createLink(info.window, "/app/something/test/"); - - - eventFire(link, "click") - // click a link and make sure the iframe url changes - - }); - - - }; - - }); - - test("replaceStateOn makes changes to an attribute use replaceSate (#1137)", function() { - stop(); - - makeTestingIframe(function(info, done){ - info.history.pushState = function () { - ok(false, "pushState should not have been called"); - }; - - info.history.replaceState = function () { - ok(true, "replaceState called"); - }; - - info.route.replaceStateOn("ignoreme"); - - info.route.ready(); - info.route.attr('ignoreme', 'yes'); - - setTimeout(function(){ - start(); - done(); - }, 30); - }); - }); - - test("replaceStateOn makes changes to multiple attributes use replaceState (#1137)", function() { - stop(); - - makeTestingIframe(function(info, done){ - info.history.pushState = function () { - ok(false, "pushState should not have been called"); - }; - - info.history.replaceState = function () { - ok(true, "replaceState called"); - }; - - info.route.replaceStateOn("ignoreme", "metoo"); - - info.route.ready(); - info.route.attr('ignoreme', 'yes'); - - setTimeout(function(){ - info.route.attr('metoo', 'yes'); - - setTimeout(function(){ - start(); - done(); - }, 30); - - }, 30); - }); - }); - - test("replaceStateOnce makes changes to an attribute use replaceState only once (#1137)", function() { - stop(); - var replaceCalls = 0, - pushCalls = 0; - - makeTestingIframe(function(info, done){ - info.history.pushState = function () { - pushCalls++; - }; - - info.history.replaceState = function () { - replaceCalls++; - }; - - info.route.replaceStateOnce("ignoreme", "metoo"); - - info.route.ready(); - info.route.attr('ignoreme', 'yes'); - - setTimeout(function(){ - info.route.attr('ignoreme', 'no'); - - setTimeout(function(){ - equal(replaceCalls, 1); - equal(pushCalls, 1); - start(); - done(); - }, 30); - - }, 30); - }); - }); - - test("replaceStateOff makes changes to an attribute use pushState again (#1137)", function(){ - stop(); - - makeTestingIframe(function(info, done){ - info.history.pushState = function () { - ok(true, "pushState called"); - }; - - info.history.replaceState = function () { - ok(false, "replaceState should not be called called"); - }; - - info.route.replaceStateOn("ignoreme"); - info.route.replaceStateOff("ignoreme"); - - info.route.ready(); - info.route.attr('ignoreme', 'yes'); - - setTimeout(function(){ - start(); - done(); - }, 30); - }); - }); - - } // end steal-only - - test("empty default is matched even if last", function () { - - can.route.routes = {}; - can.route(":who"); - can.route("", { - foo: "bar" - }); - - var obj = can.route.deparam(""); - deepEqual(obj, { - foo: "bar", - route: "" - }); - }); - - test("order matched", function () { - can.route.routes = {}; - can.route(":foo"); - can.route(":bar") - - var obj = can.route.deparam("abc"); - deepEqual(obj, { - foo: "abc", - route: ":foo" - }); - }); - - test("param order matching", function () { - can.route.routes = {}; - can.route("", { - bar: "foo" - }); - can.route("something/:bar"); - var res = can.route.param({ - bar: "foo" - }); - equal(res, "", "picks the shortest, best match"); - - // picks the first that matches everything ... - can.route.routes = {}; - - can.route(":recipe", { - recipe: "recipe1", - task: "task3" - }); - - can.route(":recipe/:task", { - recipe: "recipe1", - task: "task3" - }); - - res = can.route.param({ - recipe: "recipe1", - task: "task3" - }); - - equal(res, "", "picks the first match of everything"); - - res = can.route.param({ - recipe: "recipe1", - task: "task2" - }); - equal(res, "/task2") - }); - - test("dashes in routes", function () { - can.route.routes = {}; - can.route(":foo-:bar"); - - var obj = can.route.deparam("abc-def"); - deepEqual(obj, { - foo: "abc", - bar: "def", - route: ":foo-:bar" - }); - }); - -} +require('can-route-pushstate/can-route-pushstate_test'); diff --git a/test/dist.html b/test/dist.html index ed64bc39777..24142df93d6 100644 --- a/test/dist.html +++ b/test/dist.html @@ -1,8 +1,19 @@ CanJS distributable tests - +
                                      diff --git a/test/test.js b/test/test.js index 7224af57a12..47ca1197a15 100644 --- a/test/test.js +++ b/test/test.js @@ -1,9 +1,11 @@ require('../construct/construct_test'); -// require('../component/component_test'); -// require('../compute/compute_test'); -// require('../control/control_test'); -// require('../event/event_test'); -// require('../list/list_test'); -// require('../map/map_test'); -// require('../model/model_test'); +require('../component/component_test'); +require('../compute/compute_test'); +require('../control/control_test'); +require('../event/event_test'); +require('../list/list_test'); +require('../map/map_test'); // require('../route/route_test'); +// require('../route/pushstate/pushstate_test'); +// require('../util/fixture/fixture_test'); +require('util/util_test'); diff --git a/util/all.js b/util/all.js deleted file mode 100644 index d8364cec753..00000000000 --- a/util/all.js +++ /dev/null @@ -1,10 +0,0 @@ -require('can/component'); -require('can/construct'); -require('can/control'); -require('can/list'); -require('can/list/promise'); -require('can/model'); -require('can/route'); -require('can/view/stache'); -require('can/util/fixture'); -require('can/util/func.js'); diff --git a/util/array/array_test.js b/util/array/array_test.js deleted file mode 100644 index 15d6c22451c..00000000000 --- a/util/array/array_test.js +++ /dev/null @@ -1,2 +0,0 @@ -require("can/util/array/each_test.js"); -require("can/util/array/diff_test.js"); diff --git a/util/array/diff.js b/util/array/diff.js deleted file mode 100644 index aff30af7e7f..00000000000 --- a/util/array/diff.js +++ /dev/null @@ -1,64 +0,0 @@ -var slice = [].slice; -// a b c -// a b c d -// [[2,0, d]] -module.exports = exports = function(oldList, newList){ - var oldIndex = 0, - newIndex = 0, - oldLength = oldList.length, - newLength = newList.length, - patches = []; - - while(oldIndex < oldLength && newIndex < newLength) { - var oldItem = oldList[oldIndex], - newItem = newList[newIndex]; - - if( oldItem === newItem ) { - oldIndex++; - newIndex++; - continue; - } - // look for single insert, does the next newList item equal the current oldList. - // 1 2 3 - // 1 2 4 3 - if( newIndex+1 < newLength && newList[newIndex+1] === oldItem) { - patches.push({index: newIndex, deleteCount: 0, insert: [ newList[newIndex] ]}); - oldIndex++; - newIndex += 2; - continue; - } - // look for single removal, does the next item in the oldList equal the current newList item. - // 1 2 3 - // 1 3 - else if( oldIndex+1 < oldLength && oldList[oldIndex+1] === newItem ) { - patches.push({index: newIndex, deleteCount: 1, insert: []}); - oldIndex += 2; - newIndex++; - continue; - } - // just clean up the rest and exit - // 1 2 3 - // 1 2 5 6 7 - else { - patches.push( - {index: newIndex, - deleteCount: oldLength-oldIndex, - insert: slice.call(newList, newIndex) } ); - return patches; - } - } - if( (newIndex === newLength) && (oldIndex === oldLength) ) { - return patches; - } - // a b - // a b c d e - patches.push( - {index: newIndex, - deleteCount: oldLength-oldIndex, - insert: slice.call(newList, newIndex) } ); - - return patches; -}; - -// a b c -// a d e b c diff --git a/util/array/diff_test.js b/util/array/diff_test.js deleted file mode 100644 index c9fb033c683..00000000000 --- a/util/array/diff_test.js +++ /dev/null @@ -1,74 +0,0 @@ -var diff = require('can/util/array/diff'); -require('can/test/test'); -require('steal-qunit'); - -QUnit.test("diff", function(){ - - var patches = diff([], [1,2,3]); - deepEqual(patches, [{ - index: 0, - deleteCount: 0, - insert: [1,2,3] - }], "insert many at end"); - - patches = diff([1,2,3], [1,2,3]); - deepEqual(patches,[],"no changes"); - - patches = diff([1,2,3],[1,2,3,4]); - deepEqual(patches, [{ - index: 3, - deleteCount: 0, - insert: [4] - }],"add one at the end"); - - patches = diff([1,2,3,4], [1,2,4]); - - deepEqual(patches, [{ - index: 2, - deleteCount: 1, - insert: [] - }],"remove one in the middle"); - - patches = diff(["a","b","z","f","x"],["a","b","f","w","z"]); - deepEqual(patches, [{ - index: 2, - insert: [], - deleteCount: 1 - },{ - index: 3, - deleteCount: 1, - insert: ["w","z"] - }]); - - patches = diff(["a","b","b"],["c","a","b"]); - deepEqual(patches, [{ - index: 0, - insert: ["c"], - deleteCount: 0 - },{ - index: 3, - deleteCount: 1, - insert: [] - }]); - - // a, b, c, d, e, f, g - // a, c, d, e, f, g - // a, c, e, f, g - // a, c, e, g - patches = diff(["a","b","c","d","e","f","g"],["a","c","e","g"]); - deepEqual(patches, [{ - index: 1, - insert: [], - deleteCount: 1 - },{ - index: 2, - deleteCount: 1, - insert: [] - }, - { - index: 3, - deleteCount: 1, - insert: [] - }]); - -}); diff --git a/util/array/each.js b/util/array/each.js deleted file mode 100644 index f7290daf756..00000000000 --- a/util/array/each.js +++ /dev/null @@ -1,51 +0,0 @@ -/* jshint maxdepth:7*/ -var can = require('can/util/can'); -require('can/util/array/isArrayLike'); - -can.each = function (elements, callback, context) { - var i = 0, - key, - len, - item; - if (elements) { - if ( can.isArrayLike(elements) ) { - if(can.List && elements instanceof can.List ) { - for (len = elements.attr("length"); i < len; i++) { - item = elements.attr(i); - if (callback.call(context || item, item, i, elements) === false) { - break; - } - } - } else { - for (len = elements.length; i < len; i++) { - item = elements[i]; - if (callback.call(context || item, item, i, elements) === false) { - break; - } - } - } - - } else if (typeof elements === "object") { - - if (can.Map && elements instanceof can.Map || elements === can.route) { - var keys = can.Map.keys(elements); - for(i =0, len = keys.length; i < len; i++) { - key = keys[i]; - item = elements.attr(key); - if (callback.call(context || item, item, key, elements) === false) { - break; - } - } - } else { - for (key in elements) { - if (Object.prototype.hasOwnProperty.call(elements, key) && callback.call(context || elements[key], elements[key], key, elements) === false) { - break; - } - } - } - - } - } - return elements; -}; -module.exports = exports = can; diff --git a/util/array/each_test.js b/util/array/each_test.js deleted file mode 100644 index 766f8a50c97..00000000000 --- a/util/array/each_test.js +++ /dev/null @@ -1,30 +0,0 @@ -require('can/test/test'); -require('steal-qunit'); - -QUnit.module('can/util/array/each'); - -// The following test is from jQuery’s solution to this bug: -// https://github.com/jquery/jquery/pull/2185 -test('iOS 8 64-bit JIT object length bug', function () { - expect(4); - - var i; - for (i = 0; i < 1000; i++) { - can.each([]); - } - - i = 0; - can.each({1: '1', 2: '2', 3: '3'}, function (index) { - equal(++i, index, 'Iterate over object'); - }); - equal(i, 3, 'Last index should be the length of the array'); -}); - -test('#1989 - isArrayLike needs to check for object type', function() { - try { - can.each(true, function(index) { }); - ok(true, 'can.each on true worked'); - } catch(e) { - ok(false, 'Should not fail'); - } -}); diff --git a/util/array/isArrayLike.js b/util/array/isArrayLike.js deleted file mode 100644 index 361f2130947..00000000000 --- a/util/array/isArrayLike.js +++ /dev/null @@ -1,18 +0,0 @@ -var can = require('can/util/can'); - -// The following is from jQuery -can.isArrayLike = function(obj){ - // The `in` check is from jQuery’s fix for an iOS 8 64-bit JIT object length bug: - // https://github.com/jquery/jquery/pull/2185 - // When passing a non-object (e.g. boolean) can.each fails where it previously did nothing. - // https://github.com/canjs/canjs/issues/1989 - var length = obj && typeof obj !== 'boolean' && - typeof obj !== 'number' && - "length" in obj && obj.length; - - // var length = "length" in obj && obj.length; - return typeof arr !== "function" && - ( length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj ); -}; - -module.exports = can.isArrayLike; diff --git a/util/array/makeArray.js b/util/array/makeArray.js deleted file mode 100644 index 6c3c91112b1..00000000000 --- a/util/array/makeArray.js +++ /dev/null @@ -1,11 +0,0 @@ -require('./each'); - -can.makeArray = function (arr) { - var ret = []; - can.each(arr, function (a, i) { - ret[i] = a; - }); - return ret; -}; - -module.exports = can; diff --git a/util/array/test.html b/util/array/test.html deleted file mode 100644 index 43bbafc06ad..00000000000 --- a/util/array/test.html +++ /dev/null @@ -1,3 +0,0 @@ -can/util/array - -
                                      diff --git a/util/attr/attr.js b/util/attr/attr.js deleted file mode 100644 index 6559edf9186..00000000000 --- a/util/attr/attr.js +++ /dev/null @@ -1,274 +0,0 @@ -// # can/util/attr.js -// Central location for attribute changing to occur, used to trigger an -// `attributes` event on elements. This enables the user to do (jQuery example): `$(el).bind("attributes", function(ev) { ... })` where `ev` contains `attributeName` and `oldValue`. -var can = require('can/util/can'); - -// Acts as a polyfill for setImmediate which only works in IE 10+. Needed to make -// the triggering of `attributes` event async. -var setImmediate = can.global.setImmediate || function (cb) { - return setTimeout(cb, 0); - }, - // this is a hack to deal with a problem with can-simple-dom - formElements = {"input": true, "textarea": true, "select": true}, - hasProperty = function(el,attrName){ - return (attrName in el) || (can.document && formElements[el.nodeName.toLowerCase()]); - }, - attr = { - // This property lets us know if the browser supports mutation observers. - // If they are supported then that will be setup in can/util/jquery and those native events will be used to inform observers of attribute changes. - // Otherwise this module handles triggering an `attributes` event on the element. - MutationObserver: can.global.MutationObserver || can.global.WebKitMutationObserver || can.global.MozMutationObserver, - - /** - * @property {Object.} can.view.attr.map - * @parent can.view.elements - * @hide - * - * - * A mapping of - * special attributes to their JS property. For example: - * - * "class" : "className" - * - * means get or set `element.className`. And: - * - * "checked" : true - * - * means set `element.checked = true`. - * - * - * If the attribute name is not found, it's assumed to use - * `element.getAttribute` and `element.setAttribute`. - */ - map: { - "class": function(el, val) { - val = val || ''; - - if(el.namespaceURI === 'http://www.w3.org/2000/svg') { - el.setAttribute('class', val); - } - else { - el.className = val; - } - - return val; - }, - "value": "value", - "innertext": "innerText", - "innerhtml": "innerHTML", - "textcontent": "textContent", - "for": "htmlFor", - "checked": true, - "disabled": true, - "readonly": function (el, val) { - el.readOnly = true; - return val; - }, - "required": true, - // For the `src` attribute we are using a setter function to prevent values such as an empty string or null from being set. - // An `img` tag attempts to fetch the `src` when it is set, so we need to prevent that from happening by removing the attribute instead. - src: function (el, val) { - if (val == null || val === "") { - el.removeAttribute("src"); - return null; - } else { - el.setAttribute("src", val); - return val; - } - }, - style: (function () { - var el = can.global.document && document.createElement('div'); - if ( el && el.style && ("cssText" in el.style) ) { - return function (el, val) { - return el.style.cssText = (val || ""); - }; - } else { - return function (el, val) { - return el.setAttribute("style", val); - }; - } - })() - }, - // These are elements whos default value we should set. - defaultValue: ["input", "textarea"], - setAttrOrProp: function(el, attrName, val){ - attrName = attrName.toLowerCase(); - var prop = attr.map[attrName]; - if(prop === true && !val) { - this.remove(el, attrName); - } else { - this.set(el, attrName, val); - } - }, - setSelectValue: function(el, val) { - // jshint eqeqeq: false - if(val != null) { - var options = el.getElementsByTagName('option'); - for(var i = 0; i < options.length; i++) { - if(val == options[i].value) { - options[i].selected = true; - return; - } - } - } - - el.selectedIndex = -1; - }, - // ## attr.set - // Set the value an attribute on an element. - set: function (el, attrName, val) { - var usingMutationObserver = can.isDOM(el) && attr.MutationObserver; - attrName = attrName.toLowerCase(); - var oldValue; - // In order to later trigger an event we need to compare the new value to the old value, - // so here we go ahead and retrieve the old value for browsers that don't have native MutationObservers. - if (!usingMutationObserver) { - oldValue = attr.get(el, attrName); - } - - var prop = attr.map[attrName], - newValue; - - // Using the property of `attr.map`, go through and check if the property is a function, and if so call it. - // Then check if the property is `true`, and if so set the value to `true`, also making sure - // to set `defaultChecked` to `true` for elements of `attr.defaultValue`. We always set the value to true - // because for these boolean properties, setting them to false would be the same as removing the attribute. - // - // For all other attributes use `setAttribute` to set the new value. - if (typeof prop === "function") { - newValue = prop(el, val); - } else if (prop === true && hasProperty(el, attrName)) { - newValue = el[attrName] = true; - - if (attrName === "checked" && el.type === "radio") { - if (can.inArray((el.nodeName+"").toLowerCase(), attr.defaultValue) >= 0) { - el.defaultChecked = true; - } - } - - } else if (typeof prop === "string" && hasProperty(el, prop)) { - newValue = val; - // https://github.com/canjs/canjs/issues/356 - // But still needs to be set for