From b6a452efcba49ce18900f9956a31b85b525e520e Mon Sep 17 00:00:00 2001 From: Michael Richards Date: Mon, 3 Aug 2015 11:11:18 -0700 Subject: [PATCH 01/43] Initial effort to rewrite in ES6 using webpack. --- gulpfile.js | 73 -------- package.json | 17 +- spec/runner.html | 3 +- src/adapter.coffee | 104 ----------- src/adapter.js | 201 ++++++++++++++++++++ src/binders.coffee | 249 ------------------------- src/binders.js | 353 +++++++++++++++++++++++++++++++++++ src/bindings.coffee | 279 ---------------------------- src/bindings.js | 444 ++++++++++++++++++++++++++++++++++++++++++++ src/constants.js | 14 ++ src/export.coffee | 22 --- src/export.js | 40 ++++ src/parsers.coffee | 79 -------- src/parsers.js | 88 +++++++++ src/rivets.coffee | 75 -------- src/rivets.js | 49 +++++ src/util.coffee | 30 --- src/util.js | 45 +++++ src/view.coffee | 138 -------------- src/view.js | 236 +++++++++++++++++++++++ webpack.config.js | 27 +++ 21 files changed, 1504 insertions(+), 1062 deletions(-) delete mode 100644 gulpfile.js delete mode 100644 src/adapter.coffee create mode 100644 src/adapter.js delete mode 100644 src/binders.coffee create mode 100644 src/binders.js delete mode 100644 src/bindings.coffee create mode 100644 src/bindings.js create mode 100644 src/constants.js delete mode 100644 src/export.coffee create mode 100644 src/export.js delete mode 100644 src/parsers.coffee create mode 100644 src/parsers.js delete mode 100644 src/rivets.coffee create mode 100644 src/rivets.js delete mode 100644 src/util.coffee create mode 100644 src/util.js delete mode 100644 src/view.coffee create mode 100644 src/view.js create mode 100644 webpack.config.js diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index b84ecf51c..000000000 --- a/gulpfile.js +++ /dev/null @@ -1,73 +0,0 @@ -pkg = require('./package.json') -argv = require('minimist')(process.argv.slice(2)) - -gulp = require('gulp') -util = require('gulp-util') -coffee = require('gulp-coffee') -header = require('gulp-header') -concat = require('gulp-concat') -uglify = require('gulp-uglify') -bump = require('gulp-bump') -mocha = require('gulp-mocha-phantomjs') - -source = [ - 'src/rivets.coffee', - 'src/util.coffee', - 'src/parsers.coffee', - 'src/observer.coffee', - 'src/view.coffee', - 'src/bindings.coffee', - 'src/binders.coffee', - 'src/adapter.coffee', - 'src/export.coffee' -] - -manifests = [ - './package.json', - './bower.json', - './component.json' -] - -banner = function(bundled) { - return [ - '// Rivets.js' + (bundled ? ' + Sightglass.js' : ''), - '// version: ' + pkg.version, - '// author: ' + pkg.author, - '// license: ' + pkg.licenses[0].type - ].join('\n') + '\n' -} - -gulp.task('build', function() { - rivets = gulp.src(source) - .pipe(concat('rivets.js')) - .pipe(coffee().on('error', util.log)) - .pipe(header(banner())) - .pipe(gulp.dest('dist')) - - rivetsMin = rivets.pipe(concat('rivets.min.js')) - .pipe(uglify()) - .pipe(header(banner())) - .pipe(gulp.dest('dist')) - - rivets.on('end', function() { - sightglass = 'node_modules/sightglass/index.js' - rivets = 'dist/rivets.js' - - gulp.src([sightglass, rivets]) - .pipe(uglify()) - .pipe(concat('rivets.bundled.min.js')) - .pipe(header(banner(true))) - .pipe(gulp.dest('dist')) - }) -}) - -gulp.task('spec', function() { - gulp.src('spec/runner.html') - .pipe(mocha({reporter: 'dot'})) -}) - -gulp.task('bump', function() { - gulp.src(manifests) - .pipe(bump({type: argv.t, version: argv.v})) - .pipe(gulp.dest('./')) -}) diff --git a/package.json b/package.json index 3d4a4fa3d..da93431ef 100644 --- a/package.json +++ b/package.json @@ -19,21 +19,16 @@ "sightglass": "~0.2.4" }, "scripts": { - "test": "gulp spec", - "build": "gulp build" + "test": "mocha-phantomjs spec/runner.html", + "build": "webpack" }, "devDependencies": { + "babel-core": "^4.7.16", + "babel-loader": "^4.2.0", + "minimist": "~1.1.0", "mocha": "~1.20.1", "should": "~4.0.4", "sinon": "~1.10.2", - "minimist": "~1.1.0", - "gulp": "~3.8.7", - "gulp-util": "~2.2.14", - "gulp-coffee": "~1.4.3", - "gulp-header": "~1.0.2", - "gulp-concat": "~2.2.0", - "gulp-uglify": "~0.3.0", - "gulp-bump": "~0.1.11", - "gulp-mocha-phantomjs": "~0.2.0" + "webpack": "^1.7.3" } } diff --git a/spec/runner.html b/spec/runner.html index e5192dfd1..793d27ee2 100644 --- a/spec/runner.html +++ b/spec/runner.html @@ -14,8 +14,7 @@ - - + diff --git a/src/adapter.coffee b/src/adapter.coffee deleted file mode 100644 index b1dab0144..000000000 --- a/src/adapter.coffee +++ /dev/null @@ -1,104 +0,0 @@ -# The default `.` adapter thats comes with Rivets.js. Allows subscribing to -# properties on plain objects, implemented in ES5 natives using -# `Object.defineProperty`. -Rivets.public.adapters['.'] = - id: '_rv' - counter: 0 - weakmap: {} - - weakReference: (obj) -> - unless obj.hasOwnProperty @id - id = @counter++ - Object.defineProperty obj, @id, value: id - - @weakmap[obj[@id]] or= callbacks: {} - - cleanupWeakReference: (ref, id) -> - unless Object.keys(ref.callbacks).length - unless ref.pointers and Object.keys(ref.pointers).length - delete @weakmap[id] - - stubFunction: (obj, fn) -> - original = obj[fn] - map = @weakReference obj - weakmap = @weakmap - - obj[fn] = -> - response = original.apply obj, arguments - - for r, k of map.pointers - callback() for callback in weakmap[r]?.callbacks[k] ? [] - - response - - observeMutations: (obj, ref, keypath) -> - if Array.isArray obj - map = @weakReference obj - - unless map.pointers? - map.pointers = {} - functions = ['push', 'pop', 'shift', 'unshift', 'sort', 'reverse', 'splice'] - @stubFunction obj, fn for fn in functions - - map.pointers[ref] ?= [] - - unless keypath in map.pointers[ref] - map.pointers[ref].push keypath - - unobserveMutations: (obj, ref, keypath) -> - if Array.isArray(obj) and obj[@id]? - if map = @weakmap[obj[@id]] - if pointers = map.pointers[ref] - if (idx = pointers.indexOf(keypath)) >= 0 - pointers.splice idx, 1 - - delete map.pointers[ref] unless pointers.length - @cleanupWeakReference map, obj[@id] - - observe: (obj, keypath, callback) -> - callbacks = @weakReference(obj).callbacks - - unless callbacks[keypath]? - callbacks[keypath] = [] - desc = Object.getOwnPropertyDescriptor obj, keypath - - unless desc?.get or desc?.set - value = obj[keypath] - - Object.defineProperty obj, keypath, - enumerable: true - get: -> value - set: (newValue) => - if newValue isnt value - @unobserveMutations value, obj[@id], keypath - value = newValue - - if map = @weakmap[obj[@id]] - callbacks = map.callbacks - - if callbacks[keypath] - callback() for callback in callbacks[keypath].slice() when callback in callbacks[keypath] - @observeMutations newValue, obj[@id], keypath - - unless callback in callbacks[keypath] - callbacks[keypath].push callback - - @observeMutations obj[keypath], obj[@id], keypath - - unobserve: (obj, keypath, callback) -> - if map = @weakmap[obj[@id]] - if callbacks = map.callbacks[keypath] - if (idx = callbacks.indexOf(callback)) >= 0 - callbacks.splice idx, 1 - - unless callbacks.length - delete map.callbacks[keypath] - - @unobserveMutations obj[keypath], obj[@id], keypath - @cleanupWeakReference map, obj[@id] - - get: (obj, keypath) -> - obj[keypath] - - set: (obj, keypath, value) -> - obj[keypath] = value diff --git a/src/adapter.js b/src/adapter.js new file mode 100644 index 000000000..c41466492 --- /dev/null +++ b/src/adapter.js @@ -0,0 +1,201 @@ +// The default `.` adapter thats comes with Rivets.js. Allows subscribing to +// properties on plain objects, implemented in ES5 natives using +// `Object.defineProperty`. + +const defined = (value) => { + return value !== undefined && value !== null +} + +const ARRAY_METHODS = [ + 'push', + 'pop', + 'shift', + 'unshift', + 'sort', + 'reverse', + 'splice' +] + +const adapter = { + id: '_rv', + counter: 0, + weakmap: {}, + + weakReference: function(obj) { + if (!obj.hasOwnProperty(this.id)) { + let id = this.counter++ + + Object.defineProperty(obj, this.id, { + value: id + }) + } + + if (!this.weakmap[obj[this.id]]) { + this.weakmap[obj[this.id]] = { + callbacks: {} + } + } + + return this.weakmap[obj[this.id]] + }, + + cleanupWeakReference: function(ref, id) { + if (!Object.keys(ref.callbacks).length) { + if (!(ref.pointers && Object.keys(ref.pointers).length)) { + delete this.weakmap[id] + } + } + }, + + stubFunction: function(obj, fn) { + let original = obj[fn] + let map = this.weakReference(obj) + let weakmap = this.weakmap + + obj[fn] = () => { + let response = original.apply(obj, arguments) + + Object.keys(map.pointers).forEach(r => { + let k = map.pointers[r] + + if (defined(weakmap[r])) { + if (weakmap[r].callbacks[k] instanceof Array) { + weakmap[r].callbacks[k].forEach(callback => { + callback() + }) + } + } + }) + + return response + } + }, + + observeMutations: function(obj, ref, keypath) { + if (obj instanceof Array) { + let map = this.weakReference(obj) + + if (!defined(map.pointers)) { + map.pointers = {} + + ARRAY_METHODS.forEach(fn => { + this.stubFunction(obj, fn) + }) + } + + if (!defined(map.pointers[ref])) { + map.pointers[ref] = [] + } + + if (map.pointers[ref].indexOf(keypath) === -1) { + map.pointers[ref].push(keypath) + } + } + }, + + unobserveMutations: function(obj, ref, keypath) { + if ((obj instanceof Array) && defined(obj[this.id])) { + let map = this.weakmap[obj[this.id]] + + if (map) { + let pointers = map.pointers[ref] + + if (pointers) { + let idx = pointers.indexOf(keypath) + + if (idx > -1) { + pointers.splice(idx, 1) + } + + if (!pointers.length) { + delete map.pointers[ref] + } + + this.cleanupWeakReference(map, obj[this.id]) + } + } + } + }, + + observe: function(obj, keypath, callback) { + let callbacks = this.weakReference(obj).callbacks + + if (!defined(callbacks[keypath])) { + callbacks[keypath] = [] + let desc = Object.getOwnPropertyDescriptor(obj, keypath) + + if (!(desc && (desc.get || desc.set))) { + let value = obj[keypath] + + Object.defineProperty(obj, keypath, { + enumerable: true, + + get: () => { + return value + }, + + set: newValue => { + if (newValue !== value) { + this.unobserveMutations(value, obj[this.id], keypath) + value = newValue + let map = this.weakmap[obj[this.id]] + + if (map) { + let callbacks = map.callbacks + + if (callbacks[keypath]) { + callbacks[keypath].slice().forEach(callback => { + if (callbacks[keypath].indexOf(callback) > -1) { + callback() + } + }) + } + + this.observeMutations(newValue, obj[this.id], keypath) + } + } + } + }) + } + } + + if (callbacks[keypath].indexOf(callback) === -1) { + callbacks[keypath].push(callback) + } + + this.observeMutations(obj[keypath], obj[this.id], keypath) + }, + + unobserve: function(obj, keypath, callback) { + let map = this.weakmap[obj[this.id]] + + if (map) { + let callbacks = map.callbacks[keypath] + + if (callbacks) { + let idx = callbacks.indexOf(callback) + + if (idx > -1) { + callbacks.splice(idx, 1) + + if (!callbacks.length) { + delete map.callbacks[keypath] + } + } + + this.unobserveMutations(obj[keypath], obj[this.id], keypath) + this.cleanupWeakReference(map, obj[this.id]) + } + } + }, + + get: function(obj, keypath) { + return obj[keypath] + }, + + set: (obj, keypath, value) => { + obj[keypath] = value + } +} + +export default adapter diff --git a/src/binders.coffee b/src/binders.coffee deleted file mode 100644 index df0344994..000000000 --- a/src/binders.coffee +++ /dev/null @@ -1,249 +0,0 @@ -# Basic set of core binders that are included with Rivets.js. - -# Sets the element's text value. -Rivets.public.binders.text = (el, value) -> - if el.textContent? - el.textContent = if value? then value else '' - else - el.innerText = if value? then value else '' - -# Sets the element's HTML content. -Rivets.public.binders.html = (el, value) -> - el.innerHTML = if value? then value else '' - -# Shows the element when value is true. -Rivets.public.binders.show = (el, value) -> - el.style.display = if value then '' else 'none' - -# Hides the element when value is true (negated version of `show` binder). -Rivets.public.binders.hide = (el, value) -> - el.style.display = if value then 'none' else '' - -# Enables the element when value is true. -Rivets.public.binders.enabled = (el, value) -> - el.disabled = !value - -# Disables the element when value is true (negated version of `enabled` binder). -Rivets.public.binders.disabled = (el, value) -> - el.disabled = !!value - -# Checks a checkbox or radio input when the value is true. Also sets the model -# property when the input is checked or unchecked (two-way binder). -Rivets.public.binders.checked = - publishes: true - priority: 2000 - - bind: (el) -> - Rivets.Util.bindEvent el, 'change', @publish - - unbind: (el) -> - Rivets.Util.unbindEvent el, 'change', @publish - - routine: (el, value) -> - if el.type is 'radio' - el.checked = el.value?.toString() is value?.toString() - else - el.checked = !!value - -# Unchecks a checkbox or radio input when the value is true (negated version of -# `checked` binder). Also sets the model property when the input is checked or -# unchecked (two-way binder). -Rivets.public.binders.unchecked = - publishes: true - priority: 2000 - - bind: (el) -> - Rivets.Util.bindEvent el, 'change', @publish - - unbind: (el) -> - Rivets.Util.unbindEvent el, 'change', @publish - - routine: (el, value) -> - if el.type is 'radio' - el.checked = el.value?.toString() isnt value?.toString() - else - el.checked = !value - -# Sets the element's value. Also sets the model property when the input changes -# (two-way binder). -Rivets.public.binders.value = - publishes: true - priority: 3000 - - bind: (el) -> - unless el.tagName is 'INPUT' and el.type is 'radio' - @event = if el.tagName is 'SELECT' then 'change' else 'input' - Rivets.Util.bindEvent el, @event, @publish - - unbind: (el) -> - unless el.tagName is 'INPUT' and el.type is 'radio' - Rivets.Util.unbindEvent el, @event, @publish - - routine: (el, value) -> - if el.tagName is 'INPUT' and el.type is 'radio' - el.setAttribute 'value', value - else if window.jQuery? - el = jQuery el - - if value?.toString() isnt el.val()?.toString() - el.val if value? then value else '' - else - if el.type is 'select-multiple' - o.selected = o.value in value for o in el if value? - else if value?.toString() isnt el.value?.toString() - el.value = if value? then value else '' - -# Inserts and binds the element and it's child nodes into the DOM when true. -Rivets.public.binders.if = - block: true - priority: 4000 - - bind: (el) -> - unless @marker? - attr = [@view.prefix, @type].join('-').replace '--', '-' - declaration = el.getAttribute attr - - @marker = document.createComment " rivets: #{@type} #{declaration} " - @bound = false - - el.removeAttribute attr - el.parentNode.insertBefore @marker, el - el.parentNode.removeChild el - - unbind: -> - @nested?.unbind() - - routine: (el, value) -> - if !!value is not @bound - if value - models = {} - models[key] = model for key, model of @view.models - - (@nested or= new Rivets.View(el, models, @view.options())).bind() - @marker.parentNode.insertBefore el, @marker.nextSibling - @bound = true - else - el.parentNode.removeChild el - @nested.unbind() - @bound = false - - update: (models) -> - @nested?.update models - -# Removes and unbinds the element and it's child nodes into the DOM when true -# (negated version of `if` binder). -Rivets.public.binders.unless = - block: true - priority: 4000 - - bind: (el) -> - Rivets.public.binders.if.bind.call @, el - - unbind: -> - Rivets.public.binders.if.unbind.call @ - - routine: (el, value) -> - Rivets.public.binders.if.routine.call @, el, not value - - update: (models) -> - Rivets.public.binders.if.update.call @, models - -# Binds an event handler on the element. -Rivets.public.binders['on-*'] = - function: true - priority: 1000 - - unbind: (el) -> - Rivets.Util.unbindEvent el, @args[0], @handler if @handler - - routine: (el, value) -> - Rivets.Util.unbindEvent el, @args[0], @handler if @handler - Rivets.Util.bindEvent el, @args[0], @handler = @eventHandler value - -# Appends bound instances of the element in place for each item in the array. -Rivets.public.binders['each-*'] = - block: true - priority: 4000 - - bind: (el) -> - unless @marker? - attr = [@view.prefix, @type].join('-').replace '--', '-' - @marker = document.createComment " rivets: #{@type} " - @iterated = [] - - el.removeAttribute attr - el.parentNode.insertBefore @marker, el - el.parentNode.removeChild el - else - for view in @iterated - view.bind() - return; - - unbind: (el) -> - view.unbind() for view in @iterated if @iterated? - - routine: (el, collection) -> - modelName = @args[0] - collection = collection or [] - - if @iterated.length > collection.length - for i in Array @iterated.length - collection.length - view = @iterated.pop() - view.unbind() - @marker.parentNode.removeChild view.els[0] - - for model, index in collection - data = {index} - data[modelName] = model - - if not @iterated[index]? - for key, model of @view.models - data[key] ?= model - - previous = if @iterated.length - @iterated[@iterated.length - 1].els[0] - else - @marker - - options = @view.options() - options.preloadData = true - - template = el.cloneNode true - view = new Rivets.View(template, data, options) - view.bind() - @iterated.push view - - @marker.parentNode.insertBefore template, previous.nextSibling - else if @iterated[index].models[modelName] isnt model - @iterated[index].update data - - if el.nodeName is 'OPTION' - for binding in @view.bindings - if binding.el is @marker.parentNode and binding.type is 'value' - binding.sync() - - update: (models) -> - data = {} - - for key, model of models - data[key] = model unless key is @args[0] - - view.update data for view in @iterated - -# Adds or removes the class from the element when value is true or false. -Rivets.public.binders['class-*'] = (el, value) -> - elClass = " #{el.className} " - - if !value is (elClass.indexOf(" #{@args[0]} ") isnt -1) - el.className = if value - "#{el.className} #{@args[0]}" - else - elClass.replace(" #{@args[0]} ", ' ').trim() - -# Sets the attribute on the element. If no binder above is matched it will fall -# back to using this binder. -Rivets.public.binders['*'] = (el, value) -> - if value? - el.setAttribute @type, value - else - el.removeAttribute @type diff --git a/src/binders.js b/src/binders.js new file mode 100644 index 000000000..306011e9a --- /dev/null +++ b/src/binders.js @@ -0,0 +1,353 @@ +import rivets from './rivets' +import {bindEvent, unbindEvent} from './util' + +const CHANGE_EVENT = 'change' + +const defined = (value) => { + return value !== undefined && value !== null +} + +const getString = (value) => { + defined(value) ? value.toString() : undefined +} + +const binders = { + // Sets the element's text value. + text: (el, value) => { + el.textContent = defined(value) ? value : '' + }, + + // Sets the element's HTML content. + html: (el, value) => { + el.innerHTML = defined(value) ? value : '' + }, + + // Shows the element when value is true. + show: (el, value) => { + el.style.display = value ? '' : 'none' + }, + + // Hides the element when value is true (negated version of `show` binder). + hide: (el, value) => { + el.style.display = value ? 'none' : '' + }, + + // Enables the element when value is true. + enabled: (el, value) => { + el.disabled = !value + }, + + // Disables the element when value is true (negated version of `enabled` binder). + disabled: (el, value) => { + el.disabled = !!value + }, + + // Checks a checkbox or radio input when the value is true. Also sets the model + // property when the input is checked or unchecked (two-way binder). + checked: { + publishes: true, + priority: 2000, + + bind: function(el) { + bindEvent(el, CHANGE_EVENT, this.publish) + }, + + unbind: function(el) { + unbindEvent(el, CHANGE_EVENT, this.publish) + }, + + routine: function(el, value) { + if (el.type === 'radio') { + el.checked = getString(el.value) === getString(value) + } else { + el.checked = !!value + } + } + }, + + // Unchecks a checkbox or radio input when the value is true (negated version of + // `checked` binder). Also sets the model property when the input is checked or + // unchecked (two-way binder). + unchecked: { + publishes: true, + priority: 2000, + + bind: function(el) { + bindEvent(el, CHANGE_EVENT, this.publish) + }, + + unbind: function(el) { + unbindEvent(el, CHANGE_EVENT, this.publish) + }, + + routine: function(el, value) { + if (el.type === 'radio') { + el.checked = getString(el.value) !== getString(value) + } else { + el.checked = !value + } + } + }, + + // Sets the element's value. Also sets the model property when the input changes + // (two-way binder). + value: { + publishes: true, + priority: 3000, + + bind: function(el) { + if (!(el.tagName === 'INPUT' && el.type === 'radio')) { + this.event = el.tagName === 'SELECT' ? 'change' : 'input' + bindEvent(el, this.event, this.publish) + } + }, + + unbind: function(el) { + if (!(el.tagName === 'INPUT' && el.type === 'radio')) { + unbindEvent(el, this.event, this.publish) + } + }, + + routine: function(el, value) { + if (el.tagName === 'INPUT' && el.type === 'radio') { + el.setAttribute('value', value) + } else if (window.jQuery) { + el = jQuery(el) + + if (getString(value) !== getString(el.val())) { + el.val(defined(value) ? value : '') + } + } else { + if (el.type === 'select-multiple') { + if (value instanceof Array) { + el.options.forEach(option => { + option.selected = value.indexOf(option.value) > -1 + }) + } + } else if (getString(value) !== getString(el.value)) { + el.value = defined(value) ? value : '' + } + } + } + }, + + // Inserts and binds the element and it's child nodes into the DOM when true. + if: { + block: true, + priority: 4000, + + bind: function(el) { + if (!defined(this.marker)) { + let attr = [this.view.prefix, this.type].join('-').replace('--', '-') + let declaration = el.getAttribute(attr) + + this.marker = document.createComment ` rivets: ${this.type} ${declaration} ` + this.bound = false + + el.removeAttribute(attr) + el.parentNode.insertBefore(this.marker, el) + el.parentNode.removeChild(el) + } + }, + + unbind: function() { + if (defined(this.nested)) { + this.nested.unbind() + } + }, + + routine: function(el, value) { + if (!!value === !this.bound) { + if (value) { + let models = {} + + Object.keys(this.view.models).forEach(key => { + models[key] = this.view.models[key] + }) + + if (defined(this.nested)) { + this.nested.bind() + } else { + this.nested = rivets.bind(el, models, this.view.options()) + } + + this.marker.parentNode.insertBefore(el, this.marker.nextSibling) + this.bound = true + } else { + el.parentNode.removeChild(el) + this.nested.unbind() + this.bound = false + } + } + }, + + update: function(models) { + if (defined(this.nested)) { + this.nested.update(models) + } + } + }, + + // Removes and unbinds the element and it's child nodes into the DOM when true + // (negated version of `if` binder). + unless: { + block: true, + priority: 4000, + + bind: function(el) { + rivets.binders.if.bind.call(this, el) + }, + + unbind: function() { + rivets.binders.if.unbind.call(this) + }, + + routine: function(el, value) { + rivets.binders.if.routine.call(this, el, !value) + }, + + update: function(models) { + rivets.binders.if.update.call(this, models) + } + }, + + // Binds an event handler on the element. + 'on-*': { + function: true, + priority: 1000, + + unbind: function(el) { + if (defined(this.handler)) { + unbindEvent(el, this.args[0], this.handler) + } + }, + + routine: function(el, value) { + if (defined(this.handler)) { + unbindEvent(el, this.args[0], this.handler) + } + + this.handler = this.eventHandler(value) + bindEvent(el, this.args[0], this.handler) + } + }, + + // Appends bound instances of the element in place for each item in the array. + 'each-*': { + block: true, + priority: 4000, + + bind: function(el) { + if (!defined(this.marker)) { + let attr = [this.view.prefix, this.type].join('-').replace('--', '-') + this.marker = document.createComment ` rivets: ${this.type} ` + this.iterated = [] + + el.removeAttribute(attr) + el.parentNode.insertBefore(this.marker, el) + el.parentNode.removeChild(el) + } else { + this.iterated.forEach(view => { + view.bind() + }) + } + }, + + unbind: function(el) { + if (defined(this.iterated)) { + this.iterated.forEach(view => { + view.unbind() + }) + } + }, + + routine: function(el, collection) { + let modelName = this.args[0] + let collection = collection || [] + + if (this.iterated.length > collection.length) { + Array(this.iterated.length - collection.length).forEach(() => { + let view = this.iterated.pop() + view.unbind() + this.marker.parentNode.removeChild(view.els[0]) + }) + } + + collection.forEach((model, index) => { + let data = {index: index} + data[modelName] = model + + if (!defined(this.iterated[index])) { + Object.keys(this.view.models).forEach(key => { + if (!defined(data[key])) { + data[key] = this.view.models[key] + } + }) + + let previous = this.marker + + if (this.iterated.length) { + previous = this.iterated[this.iterated.length - 1].els[0] + } + + let options = this.view.options() + options.preloadData = true + + let template = el.cloneNode(true) + let view = rivets.bind(template, data, options) + this.iterated.push(view) + this.marker.parentNode.insertBefore(template, previous.nextSibling) + } else if (this.iterated[index].models[modelName] !== model) { + this.iterated[index].update(data) + } + }) + + if (el.nodeName === 'OPTION') { + this.view.bindings.forEach(binding => { + if (binding.el === this.marker.parentNode && binding.type === 'value') { + binding.sync() + } + }) + } + }, + + update: function(models) { + let data = {} + + Object.keys(models).forEach(key => { + if (key !== this.args[0]) { + data[key] = models[key] + } + }) + + this.iterated.forEach(view => { + view.update(data) + }) + } + }, + + // Adds or removes the class from the element when value is true or false. + 'class-*': function(el, value) { + let elClass = ` ${el.className} ` + + if (!value === (elClass.indexOf(` ${this.args[0]} `) > -1)) { + if (value) { + el.className = `${el.className} ${this.args[0]}` + } else { + el.className = elClass.replace(` ${this.args[0]} `, ' ').trim() + } + } + }, + + // Sets the attribute on the element. If no binder above is matched it will fall + // back to using this binder. + '*': function(el, value) { + if (defined(value)) { + el.setAttribute(this.type, value) + } else { + el.removeAttribute(this.type) + } + } +} + +export default binders diff --git a/src/bindings.coffee b/src/bindings.coffee deleted file mode 100644 index 64a977498..000000000 --- a/src/bindings.coffee +++ /dev/null @@ -1,279 +0,0 @@ -# Rivets.Binding -# -------------- - -# A single binding between a model attribute and a DOM element. -class Rivets.Binding - # All information about the binding is passed into the constructor; the - # containing view, the DOM node, the type of binding, the model object and the - # keypath at which to listen for changes. - constructor: (@view, @el, @type, @keypath, @options = {}) -> - @formatters = @options.formatters or [] - @dependencies = [] - @formatterObservers = {} - @model = undefined - @setBinder() - - # Sets the binder to use when binding and syncing. - setBinder: => - unless @binder = @view.binders[@type] - for identifier, value of @view.binders - if identifier isnt '*' and identifier.indexOf('*') isnt -1 - regexp = new RegExp "^#{identifier.replace(/\*/g, '.+')}$" - if regexp.test @type - @binder = value - @args = new RegExp("^#{identifier.replace(/\*/g, '(.+)')}$").exec @type - @args.shift() - - @binder or= @view.binders['*'] - @binder = {routine: @binder} if @binder instanceof Function - - observe: (obj, keypath, callback) => - Rivets.sightglass obj, keypath, callback, - root: @view.rootInterface - adapters: @view.adapters - - parseTarget: => - token = Rivets.TypeParser.parse @keypath - - if token.type is 0 - @value = token.value - else - @observer = @observe @view.models, @keypath, @sync - @model = @observer.target - - # Applies all the current formatters to the supplied value and returns the - # formatted value. - formattedValue: (value) => - for formatter, fi in @formatters - args = formatter.match /[^\s']+|'([^']|'[^\s])*'|"([^"]|"[^\s])*"/g - id = args.shift() - formatter = @view.formatters[id] - - args = (Rivets.TypeParser.parse(arg) for arg in args) - processedArgs = [] - - for arg, ai in args - processedArgs.push if arg.type is 0 - arg.value - else - @formatterObservers[fi] or= {} - - unless observer = @formatterObservers[fi][ai] - observer = @observe @view.models, arg.value, @sync - @formatterObservers[fi][ai] = observer - - observer.value() - - if formatter?.read instanceof Function - value = formatter.read value, processedArgs... - else if formatter instanceof Function - value = formatter value, processedArgs... - - value - - # Returns an event handler for the binding around the supplied function. - eventHandler: (fn) => - handler = (binding = @).view.handler - (ev) -> handler.call fn, @, ev, binding - - # Sets the value for the binding. This Basically just runs the binding routine - # with the suplied value formatted. - set: (value) => - value = if value instanceof Function and !@binder.function - @formattedValue value.call @model - else - @formattedValue value - - @binder.routine?.call @, @el, value - - # Syncs up the view binding with the model. - sync: => - @set if @observer - if @model isnt @observer.target - observer.unobserve() for observer in @dependencies - @dependencies = [] - - if (@model = @observer.target)? and @options.dependencies?.length - for dependency in @options.dependencies - observer = @observe @model, dependency, @sync - @dependencies.push observer - - @observer.value() - else - @value - - # Publishes the value currently set on the input element back to the model. - publish: => - if @observer - value = @getValue @el - - for formatter in @formatters.slice(0).reverse() - args = formatter.split /\s+/ - id = args.shift() - - if @view.formatters[id]?.publish - value = @view.formatters[id].publish value, args... - - @observer.setValue value - - # Subscribes to the model for changes at the specified keypath. Bi-directional - # routines will also listen for changes on the element to propagate them back - # to the model. - bind: => - @parseTarget() - @binder.bind?.call @, @el - - if @model? and @options.dependencies?.length - for dependency in @options.dependencies - observer = @observe @model, dependency, @sync - @dependencies.push observer - - @sync() if @view.preloadData - - # Unsubscribes from the model and the element. - unbind: => - @binder.unbind?.call @, @el - @observer?.unobserve() - - observer.unobserve() for observer in @dependencies - @dependencies = [] - - for fi, args of @formatterObservers - observer.unobserve() for ai, observer of args - - @formatterObservers = {} - - # Updates the binding's model from what is currently set on the view. Unbinds - # the old model first and then re-binds with the new model. - update: (models = {}) => - @model = @observer?.target - @binder.update?.call @, models - - # Returns elements value - getValue: (el) => - if @binder and @binder.getValue? - @binder.getValue.call @, el - else - Rivets.Util.getInputValue el - -# Rivets.ComponentBinding -# ----------------------- - -# A component view encapsulated as a binding within it's parent view. -class Rivets.ComponentBinding extends Rivets.Binding - # Initializes a component binding for the specified view. The raw component - # element is passed in along with the component type. Attributes and scope - # inflections are determined based on the components defined attributes. - constructor: (@view, @el, @type) -> - @component = @view.components[@type] - @static = {} - @observers = {} - @upstreamObservers = {} - - bindingRegExp = view.bindingRegExp() - - for attribute in @el.attributes or [] - unless bindingRegExp.test attribute.name - propertyName = @camelCase attribute.name - - if propertyName in (@component.static ? []) - @static[propertyName] = attribute.value - else - @observers[propertyName] = attribute.value - - # Intercepts `Rivets.Binding::sync` since component bindings are not bound to - # a particular model to update it's value. - sync: -> - - # Intercepts `Rivets.Binding::update` since component bindings are not bound - # to a particular model to update it's value. - update: -> - - # Intercepts `Rivets.Binding::publish` since component bindings are not bound - # to a particular model to update it's value. - publish: -> - - # Returns an object map using the component's scope inflections. - locals: => - result = {} - - for key, value of @static - result[key] = value - - for key, observer of @observers - result[key] = observer.value() - - result - - # Returns a camel-cased version of the string. Used when translating an - # element's attribute name into a property name for the component's scope. - camelCase: (string) -> - string.replace /-([a-z])/g, (grouped) -> - grouped[1].toUpperCase() - - # Intercepts `Rivets.Binding::bind` to build `@componentView` with a localized - # map of models from the root view. Bind `@componentView` on subsequent calls. - bind: => - unless @bound - for key, keypath of @observers - @observers[key] = @observe @view.models, keypath, ((key) => => - @componentView.models[key] = @observers[key].value() - ).call(@, key) - - @bound = true - - if @componentView? - @componentView.bind() - else - @el.innerHTML = @component.template.call this - scope = @component.initialize.call @, @el, @locals() - @el._bound = true - - options = {} - - for option in Rivets.extensions - options[option] = {} - options[option][k] = v for k, v of @component[option] if @component[option] - options[option][k] ?= v for k, v of @view[option] - - for option in Rivets.options - options[option] = @component[option] ? @view[option] - - @componentView = new Rivets.View(@el, scope, options) - @componentView.bind() - - for key, observer of @observers - @upstreamObservers[key] = @observe @componentView.models, key, ((key, observer) => => - observer.setValue @componentView.models[key] - ).call(@, key, observer) - - # Intercept `Rivets.Binding::unbind` to be called on `@componentView`. - unbind: => - for key, observer of @upstreamObservers - observer.unobserve() - - for key, observer of @observers - observer.unobserve() - - @componentView?.unbind.call @ - -# Rivets.TextBinding -# ----------------------- - -# A text node binding, defined internally to deal with text and element node -# differences while avoiding it being overwritten. -class Rivets.TextBinding extends Rivets.Binding - # Initializes a text binding for the specified view and text node. - constructor: (@view, @el, @type, @keypath, @options = {}) -> - @formatters = @options.formatters or [] - @dependencies = [] - @formatterObservers = {} - - # A standard routine binder used for text node bindings. - binder: - routine: (node, value) -> - node.data = value ? '' - - # Wrap the call to `sync` in fat-arrow to avoid function context issues. - sync: => - super diff --git a/src/bindings.js b/src/bindings.js new file mode 100644 index 000000000..0277b0f5e --- /dev/null +++ b/src/bindings.js @@ -0,0 +1,444 @@ +import rivets from './rivets' +import {parseType} from './parsers' +import {getInputValue} from './util' + +const defined = (value) => { + return value !== undefined && value !== null +} + +// A single binding between a model attribute and a DOM element. +export class Binding { + // All information about the binding is passed into the constructor; the + // containing view, the DOM node, the type of binding, the model object and the + // keypath at which to listen for changes. + constructor(view, el, type, keypath, options = {}) { + this.view = view + this.el = el + this.type = type + this.keypath = keypath + this.options = options + this.formatters = options.formatters || [] + this.dependencies = [] + this.formatterObservers = {} + this.model = undefined + this.setBinder() + + this.bind = this.bind.bind(this) + this.unbind = this.unbind.bind(this) + this.sync = this.sync.bind(this) + } + + // Sets the binder to use when binding and syncing. + setBinder() { + this.binder = this.view.binders[this.type] + + if (!this.binder) { + Object.keys(this.view.binders).forEach(identifier => { + let value = this.view.binders[identifier] + + if (identifier !== '*' && identifier.indexOf('*') > -1) { + let regexp = new RegExp(`^${identifier.replace(/\*/g, '.+')}$`) + + if (regexp.test(this.type)) { + this.binder = value + this.args = new RegExp(`^${identifier.replace(/\*/g, '(.+)')}$`).exec(this.type) + this.args.shift() + } + } + }) + } + + if (!defined(this.binder)) { + this.binder = this.view.binders['*'] + } + + if (this.binder instanceof Function) { + this.binder = {routine: this.binder} + } + } + + // Observes the object keypath to run the provided callback. + observe(obj, keypath, callback) { + return rivets.sightglass(obj, keypath, callback, { + root: this.view.rootInterface, + adapters: this.view.adapters + }) + } + + parseTarget() { + let token = parseType(this.keypath) + + if (token.type === 0) { + this.value = token.value + } else { + this.observer = this.observe(this.view.models, this.keypath, this.sync) + this.model = this.observer.target + } + } + + // Applies all the current formatters to the supplied value and returns the + // formatted value. + formattedValue(value) { + Object.keys(this.formatters).forEach(formatter => { + let fi = this.formatters[formatter] + let args = formatter.match(/[^\s']+|'([^']|'[^\s])*'|"([^"]|"[^\s])*"/g) + let id = args.shift() + let formatter = this.view.formatters[id] + let processedArgs = [] + + args = args.map(parseType) + + Object.keys(args).forEach(arg => { + let ai = args[arg] + + if (arg.type === 0) { + processedArgs.push(arg.value) + } else { + if (!defined(this.formatterObservers[fi])) { + this.formatterObservers[fi] = {} + } + + let observer = this.formatterObservers[fi][ai] + + if (!observer) { + observer = this.observe(this.view.models, arg.value, this.sync) + this.formatterObservers[fi][ai] = observer + } + + processedArgs.push(observer.value()) + } + }) + + + if (formatter && (formatter.read instanceof Function)) { + value = formatter.read(value, ...processedArgs) + } else if (formatter instanceof Function) { + value = formatter(value, ...processedArgs) + } + }) + + return value + } + + // Returns an event handler for the binding around the supplied function. + eventHandler(fn) { + let binding = this + let handler = binding.view.handler + + return function(ev) { + handler.call(fn, this, ev, binding) + } + } + + // Sets the value for the binding. This Basically just runs the binding routine + // with the suplied value formatted. + set(value) { + if ((value instanceof Function) && !this.binder.function) { + value = this.formattedValue(value.call(this.model)) + } else { + value = this.formattedValue(value) + } + + if (this.binder.routine) { + this.binder.routine.call(this, this.el, value) + } + } + + // Syncs up the view binding with the model. + sync() { + if (this.observer) { + if (this.model !== this.observer.target) { + let deps = this.options.dependencies + + this.dependencies.forEach(observer => { + observer.unobserve() + }) + + this.dependencies = [] + this.model = this.observer.target + + if (defined(model) && deps && deps.length) { + deps.forEach(dependency => { + let observer = this.observe(this.model, dependency, this.sync) + this.dependencies.push(observer) + }) + } + } + + this.set(this.observer.value()) + } else { + this.set(this.value) + } + } + + // Publishes the value currently set on the input element back to the model. + publish() { + if (this.observer) { + let value = this.getValue(this.el) + + this.formatters.slice(0).reverse().forEach(formatter => { + let args = formatter.split(/\s+/) + let id = args.shift() + let f = this.views.formatters[id] + + if (defined(f) && f.publish) { + value = f.publish(value, ...args) + } + }) + + this.observer.setValue(value) + } + } + + // Subscribes to the model for changes at the specified keypath. Bi-directional + // routines will also listen for changes on the element to propagate them back + // to the model. + bind() { + this.parseTarget() + + if (defined(this.binder.bind)) { + this.binder.bind.call(this, this.el) + } + + if (defined(this.model) && defined(this.options.dependencies)) { + this.options.dependencies.forEach(dependency => { + let observer = this.observe(this.model, dependency, this.sync) + this.dependencies.push(observer) + }) + } + + if (this.view.preloadData) { + this.sync() + } + } + + // Unsubscribes from the model and the element. + unbind() { + if (defined(this.binder.unbind)) { + this.binder.unbind.call(this, this.el) + } + + if (defined(this.observer)) { + this.observer.unobserve() + } + + + this.dependencies.forEach(observer => { + observer.unobserve() + }) + + this.dependencies = [] + + Object.keys(this.formatterObservers).forEach(fi => { + let args = this.formatterObservers[fi] + + Object.keys(args).forEach(ai => { + args[ai].unobserve() + }) + }) + + this.formatterObservers = {} + } + + // Updates the binding's model from what is currently set on the view. Unbinds + // the old model first and then re-binds with the new model. + update(models = {}) { + if (defined(this.observer)) { + this.model = this.observer.target + } + + if (defined(this.binder.update)) { + this.binder.update.call(this, models) + } + } + + // Returns elements value + getValue(el) { + if (this.binder && defined(this.binder.getValue)) { + return this.binder.getValue.call(this, el) + } else { + return getInputValue(el) + } + } +} + +// component view encapsulated as a binding within it's parent view. +export class ComponentBinding extends Binding { + // Initializes a component binding for the specified view. The raw component + // element is passed in along with the component type. Attributes and scope + // inflections are determined based on the components defined attributes. + constructor(view, el, type) { + this.view = view + this.el = el + this.type = type + this.component = this.view.components[this.type] + this.static = {} + this.observers = {} + this.upstreamObservers = {} + + let bindingRegExp = view.bindingRegExp() + + if (this.el.attributes) { + this.el.attributes.forEach(attribute => { + if (!bindingRegExp.test(attribute.name)) { + let propertyName = this.camelCase(attribute.name) + let stat = this.component.static + + if (stat && stat.indexOf(propertyName) > -1) { + this.static[propertyName] = attribute.value + } else { + this.observers[propertyName] = attribute.value + } + } + }) + } + } + + + // Intercepts `Rivets.Binding::sync` since component bindings are not bound to + // a particular model to update it's value. + sync() {} + + // Intercepts `Rivets.Binding::update` since component bindings are not bound + // to a particular model to update it's value. + update() {} + + // Intercepts `Rivets.Binding::publish` since component bindings are not bound + // to a particular model to update it's value. + publish() {} + + // Returns an object map using the component's scope inflections. + locals() { + let result = {} + + Object.keys(this.static).forEach(key => { + result[key] = this.static[key] + }) + + Object.keys(this.observers).forEach(key => { + result[key] = this.observers[key].value() + }) + + return result + } + + // Returns a camel-cased version of the string. Used when translating an + // element's attribute name into a property name for the component's scope. + camelCase(string) { + return string.replace(/-([a-z])/g, grouped => { + grouped[1].toUpperCase() + }) + } + + // Intercepts `Rivets.Binding::bind` to build `@componentView` with a localized + // map of models from the root view. Bind `@componentView` on subsequent calls. + bind() { + if (!this.bound) { + Object.keys(this.observers).forEach(key => { + let keypath = this.observers[key] + + this.observers[key] = this.observe(this.view.models, keypath, (key => { + return () => { + this.componentView.models[key] = this.observers[key].value() + } + }).call(this, key)) + }) + + this.bound = true + } + + if (defined(this.componentView)) { + this.componentView.bind() + } else { + this.el.innerHTML = this.component.template.call(this) + let scope = this.component.initialize.call(this, this.el, this.locals()) + this.el._bound = true + + let options = {} + + EXTENSIONS.forEach(extensionType => { + options[extensionType] = {} + + if (this.component[extensionType]) { + Object.keys(this.component[extensionType]).forEach(key => { + options[extensionType][key] = this.component[extensionType][key] + }) + } + + Object.keys(this.view[extensionType]).forEach(key => { + if (!defined(options[extensionType][key])) { + options[extensionType][key] = this.view[extensionType][key] + } + }) + }) + + OPTIONS.forEach(option => { + if (defined(this.component[option])) { + options[option] = this.component[option] + } else { + options[option] = this.view[option] + } + }) + + this.componentView = new View(this.el, scope, options) + this.componentView.bind() + + Object.keys(this.observers).forEach(key => { + let observer = this.observers[key] + let models = this.componentView.models + + let upstream = this.observe(models, key, ((key, observer) => { + return () => { + observer.setValue(this.componentView.models[key]) + } + }).call(this, key, observer)) + + this.upstreamObservers[key] = upstream + }) + } + } + + // Intercept `Rivets.Binding::unbind` to be called on `@componentView`. + unbind() { + Object.keys(this.upstreamObservers).forEach(key => { + this.upstreamObservers[key].unobserve() + }) + + Object.keys(this.observers).forEach(key => { + this.observers[key].unobserve() + }) + + if (defined(this.componentView)) { + this.componentView.unbind.call(this) + } + } +} + +// A text node binding, defined internally to deal with text and element node +// differences while avoiding it being overwritten. +export class TextBinding extends Binding { + // Initializes a text binding for the specified view and text node. + constructor(view, el, type, keypath, options = {}) { + this.view = view + this.el = el + this.type = type + this.keypath = keypath + this.options = options + this.formatters = this.options.formatters || [] + this.dependencies = [] + this.formatterObservers = {} + + this.binder = { + routine: (node, value) => { + node.data = defined(value) ? value : '' + } + } + + this.sync = this.sync.bind(this) + } + + // Wrap the call to `sync` to avoid function context issues. + sync() { + super.sync() + } +} diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 000000000..f514dfb3a --- /dev/null +++ b/src/constants.js @@ -0,0 +1,14 @@ +export const OPTIONS = [ + 'prefix', + 'templateDelimiters', + 'rootInterface', + 'preloadData', + 'handler' +] + +export const EXTENSIONS = [ + 'binders', + 'formatters', + 'components', + 'adapters' +] diff --git a/src/export.coffee b/src/export.coffee deleted file mode 100644 index c153d38c2..000000000 --- a/src/export.coffee +++ /dev/null @@ -1,22 +0,0 @@ -# Rivets.factory -# -------------- - -# Rivets.js module factory. -Rivets.factory = (sightglass) -> - # Integrate sightglass. - Rivets.sightglass = sightglass - - # Allow access to private members (for testing). - Rivets.public._ = Rivets - - # Return the public interface. - Rivets.public - -# Exports Rivets.js for CommonJS, AMD and the browser. -if typeof module?.exports is 'object' - module.exports = Rivets.factory require('sightglass') -else if typeof define is 'function' and define.amd - define ['sightglass'], (sightglass) -> - @rivets = Rivets.factory sightglass -else - @rivets = Rivets.factory sightglass diff --git a/src/export.js b/src/export.js new file mode 100644 index 000000000..1f870c7a5 --- /dev/null +++ b/src/export.js @@ -0,0 +1,40 @@ +import sightglass from 'sightglass' +import rivets from './rivets' +import View from './view' +import adapter from './adapter' +import binders from './binders' + +// Module factory. Integrates sightglass and public API methods. Returns the +// public interface. +const factory = sightglass => { + rivets.sightglass = sightglass + rivets.binders = binders + rivets.adapters['.'] = adapter + + // Binds some data to a template / element. Retuddrns a Rivets.View instance. + rivets.bind = (el, models = {}, options = {}) => { + let view = new View(el, models, options) + view.bind() + return view + } + + // Initializes a new instance of a component on the specified element and + // returns a Rivets.View instance. + rivets.init = (component, el, data = {}) => { + if (!el) { + el = document.createElement('div') + } + + let component = rivets.components[component] + el.innerHTML = component.template.call(rivets, el) + let scope = component.initialize.call(rivets, el, data) + + let view = new View(el, scope) + view.bind() + return view + } + + return rivets +} + +export default factory(sightglass) diff --git a/src/parsers.coffee b/src/parsers.coffee deleted file mode 100644 index e7bd3a5b8..000000000 --- a/src/parsers.coffee +++ /dev/null @@ -1,79 +0,0 @@ -# Rivets.TypeParser -# --------------------- - -# Parser and tokenizer for getting the type and value of a primitive or keypath. -class Rivets.TypeParser - @types: - primitive: 0 - keypath: 1 - - @parse: (string) -> - if /^'.*'$|^".*"$/.test string - type: @types.primitive - value: string.slice 1, -1 - else if string is 'true' - type: @types.primitive - value: true - else if string is 'false' - type: @types.primitive - value: false - else if string is 'null' - type: @types.primitive - value: null - else if string is 'undefined' - type: @types.primitive - value: undefined - else if isNaN(Number(string)) is false - type: @types.primitive - value: Number string - else - type: @types.keypath - value: string - -# Rivets.TextTemplateParser -# ------------------------- - -# Rivets.js text template parser and tokenizer for mustache-style text content -# binding declarations. -class Rivets.TextTemplateParser - @types: - text: 0 - binding: 1 - - # Parses the template and returns a set of tokens, separating static portions - # of text from binding declarations. - @parse: (template, delimiters) -> - tokens = [] - length = template.length - index = 0 - lastIndex = 0 - - while lastIndex < length - index = template.indexOf delimiters[0], lastIndex - - if index < 0 - tokens.push type: @types.text, value: template.slice lastIndex - break - else - if index > 0 and lastIndex < index - tokens.push type: @types.text, value: template.slice lastIndex, index - - lastIndex = index + delimiters[0].length - index = template.indexOf delimiters[1], lastIndex - - if index < 0 - substring = template.slice lastIndex - delimiters[1].length - lastToken = tokens[tokens.length - 1] - - if lastToken?.type is @types.text - lastToken.value += substring - else - tokens.push type: @types.text, value: substring - - break - - value = template.slice(lastIndex, index).trim() - tokens.push type: @types.binding, value: value - lastIndex = index + delimiters[1].length - - tokens diff --git a/src/parsers.js b/src/parsers.js new file mode 100644 index 000000000..71ef349ca --- /dev/null +++ b/src/parsers.js @@ -0,0 +1,88 @@ +const PRIMITIVE = 0 +const KEYPATH = 1 +const TEXT = 0 +const BINDING = 1 + +// Parser and tokenizer for getting the type and value from a string. +export function parseType(string) { + let type = PRIMITIVE + let value = string + + if (/^'.*'$|^".*"$/.test(string)) { + value = string.slice(1, -1) + } else if (string === 'true') { + value = true + } else if (string === 'false') { + value = false + } else if (string === 'null') { + value = null + } else if (string === 'undefined') { + value = undefined + } else if (isNaN(Number(string)) === false) { + value = Number(string) + } else { + type = KEYPATH + } + + return {type: type, value: value} +} + +// Template parser and tokenizer for mustache-style text content bindings. +// Parses the template and returns a set of tokens, separating static portions +// of text from binding declarations. +export function parseTemplate(template, delimiters) { + let tokens = [] + let length = template.length + let index = 0 + let lastIndex = 0 + + while (lastIndex < length) { + index = template.indexOf(delimiters[0], lastIndex) + + if (index < 0) { + tokens.push({ + type: TEXT, + value: template.slice(lastIndex) + }) + + break + } else { + if (index > 0 && lastIndex < index) { + tokens.push({ + type: TEXT, + value: template.slice(lastIndex, index) + }) + } + + lastIndex = index + delimiters[0].length + index = template.indexOf(delimiters[1], lastIndex) + + if (index < 0) { + let substring = template.slice(lastIndex - delimiters[1].length) + lastToken = tokens[tokens.length - 1] + + if (lastToken && lastToken.type === TEXT) { + lastToken.value += substring + } else { + tokens.push({ + type: TEXT, + value: substring + }) + } + + break + } + + let value = template.slice(lastIndex, index).trim() + + tokens.push({ + type: BINDING, + value: value + }) + + lastIndex = index + delimiters[1].length + } + } + + return tokens +} diff --git a/src/rivets.coffee b/src/rivets.coffee deleted file mode 100644 index ecb0d27d9..000000000 --- a/src/rivets.coffee +++ /dev/null @@ -1,75 +0,0 @@ -# The Rivets namespace. -Rivets = - options: [ - 'prefix' - 'templateDelimiters' - 'rootInterface' - 'preloadData' - 'handler' - ] - - extensions: [ - 'binders' - 'formatters' - 'components' - 'adapters' - ] - - # The public interface (this is the exported module object). - public: - # Global binders. - binders: {} - - # Global components. - components: {} - - # Global formatters. - formatters: {} - - # Global sightglass adapters. - adapters: {} - - # Default attribute prefix. - prefix: 'rv' - - # Default template delimiters. - templateDelimiters: ['{', '}'] - - # Default sightglass root interface. - rootInterface: '.' - - # Preload data by default. - preloadData: true - - # Default event handler. - handler: (context, ev, binding) -> - @call context, ev, binding.view.models - - # Merges an object literal into the corresponding global options. - configure: (options = {}) -> - for option, value of options - if option in ['binders', 'components', 'formatters', 'adapters'] - for key, descriptor of value - Rivets[option][key] = descriptor - else - Rivets.public[option] = value - - return - - # Binds some data to a template / element. Returns a Rivets.View instance. - bind: (el, models = {}, options = {}) -> - view = new Rivets.View(el, models, options) - view.bind() - view - - # Initializes a new instance of a component on the specified element and - # returns a Rivets.View instance. - init: (component, el, data = {}) -> - el ?= document.createElement 'div' - component = Rivets.public.components[component] - el.innerHTML = component.template.call @, el - scope = component.initialize.call @, el, data - - view = new Rivets.View(el, scope) - view.bind() - view diff --git a/src/rivets.js b/src/rivets.js new file mode 100644 index 000000000..56ff09efa --- /dev/null +++ b/src/rivets.js @@ -0,0 +1,49 @@ +import {OPTIONS, EXTENSIONS} from './constants' + +const rivets = { + // Global binders. + binders: {}, + + // Global components. + components: {}, + + // Global formatters. + formatters: {}, + + // Global sightglass adapters. + adapters: {}, + + // Default attribute prefix. + prefix: 'rv', + + // Default template delimiters. + templateDelimiters: ['{', '}'], + + // Default sightglass root interface. + rootInterface: '.', + + // Preload data by default. + preloadData: true, + + // Default event handler. + handler: function(context, ev, binding) { + this.call(context, ev, binding.view.models) + }, + + // Merges an object literal into the corresponding global options. + configure: (options = {}) => { + Object.keys(options).forEach(option => { + let value = options[option] + + if (EXTENSIONS.indexOf(option) > -1) { + Object.keys(value).forEach(key => { + this[option][key] = value[key] + }) + } else { + this[option] = value + } + }) + } +} + +export default rivets diff --git a/src/util.coffee b/src/util.coffee deleted file mode 100644 index 55fbbdfc4..000000000 --- a/src/util.coffee +++ /dev/null @@ -1,30 +0,0 @@ -# Rivets.Util -# ----------- - -if window['jQuery'] or window['$'] - [bindMethod, unbindMethod] = if 'on' of jQuery.prototype then ['on', 'off'] else ['bind', 'unbind'] - - Rivets.Util = - bindEvent: (el, event, handler) -> jQuery(el)[bindMethod] event, handler - unbindEvent: (el, event, handler) -> jQuery(el)[unbindMethod] event, handler - getInputValue: (el) -> - $el = jQuery el - - if $el.attr('type') is 'checkbox' then $el.is ':checked' - else do $el.val -else - Rivets.Util = - bindEvent: do -> - if 'addEventListener' of window then return (el, event, handler) -> - el.addEventListener event, handler, false - - (el, event, handler) -> el.attachEvent 'on' + event, handler - unbindEvent: do -> - if 'removeEventListener' of window then return (el, event, handler) -> - el.removeEventListener event, handler, false - - (el, event, handler) -> el.detachEvent 'on' + event, handler - getInputValue: (el) -> - if el.type is 'checkbox' then el.checked - else if el.type is 'select-multiple' then o.value for o in el when o.selected - else el.value diff --git a/src/util.js b/src/util.js new file mode 100644 index 000000000..362bd98a4 --- /dev/null +++ b/src/util.js @@ -0,0 +1,45 @@ +const $ = window.jQuery || window.$ + +export function bindEvent(el, event, handler) { + if ($) { + $(el).on(event, handler) + } else { + el.addEventListener(event, handler, false) + } +} + +export function unbindEvent(el, event, handler) { + if ($) { + $(el).off(event, handler) + } else { + el.removeEventListener(event, handler, false) + } +} + +export function getInputValue(el) { + if ($) { + let $el = $(el) + + if ($el.attr('type') === 'checkbox') { + return $el.is(':checked') + } else { + return $el.val() + } + } else { + if (el.type === 'checkbox') { + return el.checked + } else if (el.type === 'select-multiple') { + let results = [] + + el.options.forEach(option => { + if (option.selected) { + results.push(option.value) + } + }) + + return results + } else { + return el.value + } + } +} diff --git a/src/view.coffee b/src/view.coffee deleted file mode 100644 index 628747149..000000000 --- a/src/view.coffee +++ /dev/null @@ -1,138 +0,0 @@ -# Rivets.View -# ----------- - -# A collection of bindings built from a set of parent nodes. -class Rivets.View - # The DOM elements and the model objects for binding are passed into the - # constructor along with any local options that should be used throughout the - # context of the view and it's bindings. - constructor: (@els, @models, options = {}) -> - @els = [@els] unless (@els.jquery or @els instanceof Array) - - for option in Rivets.extensions - @[option] = {} - @[option][k] = v for k, v of options[option] if options[option] - @[option][k] ?= v for k, v of Rivets.public[option] - - for option in Rivets.options - @[option] = options[option] ? Rivets.public[option] - - @build() - - options: => - options = {} - - for option in Rivets.extensions.concat Rivets.options - options[option] = @[option] - - options - - # Regular expression used to match binding attributes. - bindingRegExp: => - new RegExp "^#{@prefix}-" - - buildBinding: (binding, node, type, declaration) => - options = {} - - pipes = (pipe.trim() for pipe in declaration.split '|') - context = (ctx.trim() for ctx in pipes.shift().split '<') - keypath = context.shift() - - options.formatters = pipes - - if dependencies = context.shift() - options.dependencies = dependencies.split /\s+/ - - @bindings.push new Rivets[binding] @, node, type, keypath, options - - # Parses the DOM tree and builds `Rivets.Binding` instances for every matched - # binding declaration. - build: => - @bindings = [] - - parse = (node) => - if node.nodeType is 3 - parser = Rivets.TextTemplateParser - - if delimiters = @templateDelimiters - if (tokens = parser.parse(node.data, delimiters)).length - unless tokens.length is 1 and tokens[0].type is parser.types.text - for token in tokens - text = document.createTextNode token.value - node.parentNode.insertBefore text, node - - if token.type is 1 - @buildBinding 'TextBinding', text, null, token.value - node.parentNode.removeChild node - else if node.nodeType is 1 - block = @traverse node - - unless block - parse childNode for childNode in (n for n in node.childNodes) - - parse el for el in @els - - @bindings.sort (a, b) -> - (b.binder?.priority or 0) - (a.binder?.priority or 0) - - return - - traverse: (node) => - bindingRegExp = @bindingRegExp() - block = node.nodeName is 'SCRIPT' or node.nodeName is 'STYLE' - - for attribute in node.attributes - if bindingRegExp.test attribute.name - type = attribute.name.replace bindingRegExp, '' - - unless binder = @binders[type] - for identifier, value of @binders - if identifier isnt '*' and identifier.indexOf('*') isnt -1 - regexp = new RegExp "^#{identifier.replace(/\*/g, '.+')}$" - if regexp.test type - binder = value - - binder or= @binders['*'] - - if binder.block - block = true - attributes = [attribute] - - for attribute in attributes or node.attributes - if bindingRegExp.test attribute.name - type = attribute.name.replace bindingRegExp, '' - @buildBinding 'Binding', node, type, attribute.value - - unless block - type = node.nodeName.toLowerCase() - - if @components[type] and not node._bound - @bindings.push new Rivets.ComponentBinding @, node, type - block = true - - block - - # Returns an array of bindings where the supplied function evaluates to true. - select: (fn) => - binding for binding in @bindings when fn binding - - # Binds all of the current bindings for this view. - bind: => - binding.bind() for binding in @bindings - - # Unbinds all of the current bindings for this view. - unbind: => - binding.unbind() for binding in @bindings - - # Syncs up the view with the model by running the routines on all bindings. - sync: => - binding.sync?() for binding in @bindings - - # Publishes the input values from the view back to the model (reverse sync). - publish: => - binding.publish() for binding in @select (b) -> b.binder?.publishes - - # Updates the view's models along with any affected bindings. - update: (models = {}) => - @models[key] = model for key, model of models - binding.update? models for binding in @bindings diff --git a/src/view.js b/src/view.js new file mode 100644 index 000000000..3ddd0e2f4 --- /dev/null +++ b/src/view.js @@ -0,0 +1,236 @@ +import rivets from './rivets' +import {OPTIONS, EXTENSIONS} from './constants' +import {Binding, TextBinding, ComponentBinding} from './bindings' +import {parseTemplate} from './parsers' + +const defined = (value) => { + return value !== undefined && value !== null +} + +// A collection of bindings built from a set of parent nodes. +export default class View { + // The DOM elements and the model objects for binding are passed into the + // constructor along with any local options that should be used throughout the + // context of the view and it's bindings. + constructor(els, models, options = {}) { + if (els.jquery || els instanceof Array) { + this.els = els + } else { + this.els = [els] + } + + this.models = models + + EXTENSIONS.forEach(extensionType => { + this[extensionType] = {} + + if (options[extensionType]) { + Object.keys(options[extensionType]).forEach(key => { + this[extensionType][key] = options[extensionType][key] + }) + } + + Object.keys(rivets[extensionType]).forEach(key => { + if (!defined(this[extensionType][key])) { + this[extensionType][key] = rivets[extensionType][key] + } + }) + }) + + OPTIONS.forEach(option => { + this[option] = defined(options[option]) ? options[option] : rivets[option] + }) + + this.build() + } + + options() { + let options = {} + + EXTENSIONS.concat(OPTIONS).forEach(option => { + options[option] = this[option] + }) + + return options + } + + // Regular expression used to match binding attributes. + bindingRegExp() { + return new RegExp(`^${this.prefix}-`) + } + + buildBinding(binding, node, type, declaration) { + let pipes = declaration.split('|').map(pipe => { + return pipe.trim() + }) + + let context = pipes.shift().split('<').map(ctx => { + return ctx.trim() + }) + + let keypath = context.shift() + let dependencies = context.shift() + let options = {formatters: pipes} + + if (dependencies) { + options.dependencies = dependencies.split(/\s+/) + } + + this.bindings.push(new binding(this, node, type, keypath, options)) + } + + // Parses the DOM tree and builds `Binding` instances for every matched + // binding declaration. + build() { + this.bindings = [] + + let parse = node => { + let block = false + + if (node.nodeType === 3) { + let delimiters = this.templateDelimiters + + if (delimiters) { + let tokens = parseTemplate(node.data, delimiters) + + if (tokens.length) { + if (!(tokens.length === 1 && tokens[0].type === 0)) { + tokens.forEach(token => { + let text = document.createTextNode(token.value) + node.parentNode.insertBefore(text, node) + + if (token.type === 1) { + this.buildBinding(TextBinding, text, null, token.value) + } + }) + + node.parentNode.removeChild(node) + } + } + } + } else if (node.nodeType === 1) { + block = this.traverse(node) + } + + if (!block) { + Array.prototype.slice.call(node.childNodes).forEach(parse) + } + } + + this.els.forEach(parse) + + this.bindings.sort((a, b) => { + let aPriority = defined(a.binder) ? a.binder.priority : 0 + let bPriority = defined(b.binder) ? b.binder.priority : 0 + return bPriority - aPriority + }) + } + + traverse(node) { + let bindingRegExp = this.bindingRegExp() + let block = node.nodeName === 'SCRIPT' || node.nodeName === 'STYLE' + let attributes = null + + Array.prototype.slice.call(node.attributes).forEach(attribute => { + if (bindingRegExp.test(attribute.name)) { + let type = attribute.name.replace(bindingRegExp, '') + let binder = this.binders[type] + + if (!binder) { + Object.keys(this.binders).forEach(identifier => { + let value = this.binders[identifier] + + if (identifier !== '*' && identifier.indexOf('*') > -1) { + let regexp = new RegExp(`^${identifier.replace(/\*/g, '.+')}$`) + + if (regexp.test(type)) { + binder = value + } + } + }) + } + + if (!defined(binder)) { + binder = this.binders['*'] + } + + if (binder.block) { + block = true + attributes = [attribute] + } + } + }) + + attributes = attributes || Array.prototype.slice.call(node.attributes) + + attributes.forEach(attribute => { + if (bindingRegExp.test(attribute.name)) { + let type = attribute.name.replace(bindingRegExp, '') + this.buildBinding(Binding, node, type, attribute.value) + } + }) + + if (!block) { + let type = node.nodeName.toLowerCase() + + if (this.components[type] && !node._bound) { + this.bindings.push(new ComponentBinding(this, node, type)) + block = true + } + } + + return block + } + + // Returns an array of bindings where the supplied function evaluates to true. + select(fn) { + return this.bindings.filter(fn) + } + + // Binds all of the current bindings for this view. + bind() { + this.bindings.forEach(binding => { + binding.bind() + }) + } + + // Unbinds all of the current bindings for this view. + unbind() { + this.bindings.forEach(binding => { + binding.unbind() + }) + } + + // Syncs up the view with the model by running the routines on all bindings. + sync() { + this.bindings.forEach(binding => { + binding.sync() + }) + } + + // Publishes the input values from the view back to the model (reverse sync). + publish() { + let publishes = this.select(binding => { + if (defined(binding.binder)) { + return binding.binder.publishes + } + }) + + publishes.forEach(binding => { + binding.publish() + }) + } + + // Updates the view's models along with any affected bindings. + update(models = {}) { + Object.keys(models).forEach(key => { + this.models[key] = models[key] + }) + + this.bindings.forEach(binding => { + if (defined(binding.update)) { + binding.update(models) + } + }) + } +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 000000000..31f77a304 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,27 @@ +var webpack = require('webpack') + +module.exports = { + context: __dirname, + entry: './src/export', + + output: { + path: __dirname + '/dist', + filename: 'rivets.bundled.js', + library: 'rivets', + libraryTarget: 'umd' + }, + + module: { + loaders: [ + { + test: /\.js$/, + exclude: '/node_modules/', + loader: 'babel-loader' + } + ] + }, + + resolve: { + extensions: ['', '.js'] + } +} From 29d5b6abe6ba1f2cc99e3a13d828108d178f822d Mon Sep 17 00:00:00 2001 From: Michael Richards Date: Mon, 3 Aug 2015 12:42:29 -0700 Subject: [PATCH 02/43] Update build command in README.md. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 92154bd73..a8ffa5da0 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,10 @@ $ npm install #### Building -Rivets.js uses [gulp](http://gulpjs.com/) as it's build tool. Run the following task to compile + minify the source into `dist/`. +Rivets.js uses [webpack](http://gulpjs.com/) as it's bundling / build tool. Run the following to compile the source into `dist/`. ``` -$ gulp build +$ npm run build ``` #### Testing From 0ea82d255b96dbd944125259860b12f7aef14a45 Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Mon, 3 Aug 2015 20:49:57 +0100 Subject: [PATCH 03/43] Fixed a couple of test (just a typo!) --- src/bindings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings.js b/src/bindings.js index 0277b0f5e..a4f3bb907 100644 --- a/src/bindings.js +++ b/src/bindings.js @@ -179,7 +179,7 @@ export class Binding { this.formatters.slice(0).reverse().forEach(formatter => { let args = formatter.split(/\s+/) let id = args.shift() - let f = this.views.formatters[id] + let f = this.view.formatters[id] if (defined(f) && f.publish) { value = f.publish(value, ...args) From d44ade081cab84acff7d842689596a36c316dca1 Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Tue, 4 Aug 2015 19:31:20 +0100 Subject: [PATCH 04/43] Fixed another couple of failing specs --- src/binders.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/binders.js b/src/binders.js index 306011e9a..94ce22b1b 100644 --- a/src/binders.js +++ b/src/binders.js @@ -8,7 +8,7 @@ const defined = (value) => { } const getString = (value) => { - defined(value) ? value.toString() : undefined + return defined(value) ? value.toString() : undefined } const binders = { From 57f873ca348581dd1b4c70b2fded41f54c174d17 Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Tue, 4 Aug 2015 20:37:45 +0100 Subject: [PATCH 05/43] Another fix (es6 arrow function dropping `this`) --- src/rivets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rivets.js b/src/rivets.js index 56ff09efa..c55215013 100644 --- a/src/rivets.js +++ b/src/rivets.js @@ -31,7 +31,7 @@ const rivets = { }, // Merges an object literal into the corresponding global options. - configure: (options = {}) => { + configure: function(options = {}) { Object.keys(options).forEach(option => { let value = options[option] From 4ff67e4324f033b9f6d6f94776dfd0671513694a Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Tue, 4 Aug 2015 20:39:46 +0100 Subject: [PATCH 06/43] Fixed Functional spec `sightglass` reference --- spec/rivets/functional.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rivets/functional.js b/spec/rivets/functional.js index 5ef96c520..f6d585096 100644 --- a/spec/rivets/functional.js +++ b/spec/rivets/functional.js @@ -19,7 +19,7 @@ describe('Functional', function() { } } - sightglass.adapters[':'] = adapter + rivets.sightglass.adapters[':'] = adapter rivets.configure({preloadData: true}) data = new Data({ From 34cfb23be0770a41ce1f7e36be2737fc7924cfbe Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Tue, 4 Aug 2015 22:02:24 +0100 Subject: [PATCH 07/43] Fixed a whole load of formatter spec failures --- src/bindings.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/bindings.js b/src/bindings.js index a4f3bb907..3026fa7c6 100644 --- a/src/bindings.js +++ b/src/bindings.js @@ -79,18 +79,15 @@ export class Binding { // Applies all the current formatters to the supplied value and returns the // formatted value. formattedValue(value) { - Object.keys(this.formatters).forEach(formatter => { - let fi = this.formatters[formatter] - let args = formatter.match(/[^\s']+|'([^']|'[^\s])*'|"([^"]|"[^\s])*"/g) + this.formatters.forEach((formatterStr, fi) => { + let args = formatterStr.match(/[^\s']+|'([^']|'[^\s])*'|"([^"]|"[^\s])*"/g) let id = args.shift() let formatter = this.view.formatters[id] let processedArgs = [] args = args.map(parseType) - Object.keys(args).forEach(arg => { - let ai = args[arg] - + args.forEach((arg, ai) => { if (arg.type === 0) { processedArgs.push(arg.value) } else { From 9fb09e18271769be0f750107e014e631cb1e4743 Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Tue, 4 Aug 2015 22:08:47 +0100 Subject: [PATCH 08/43] Fixed undefined binder priority --- src/view.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/view.js b/src/view.js index 3ddd0e2f4..d2a00e939 100644 --- a/src/view.js +++ b/src/view.js @@ -120,8 +120,8 @@ export default class View { this.els.forEach(parse) this.bindings.sort((a, b) => { - let aPriority = defined(a.binder) ? a.binder.priority : 0 - let bPriority = defined(b.binder) ? b.binder.priority : 0 + let aPriority = defined(a.binder) ? (a.binder.priority || 0) : 0 + let bPriority = defined(b.binder) ? (b.binder.priority || 0) : 0 return bPriority - aPriority }) } From 47757c5c83291fb030a8576bd8da0ee72b16819f Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Fri, 14 Aug 2015 14:18:25 +0100 Subject: [PATCH 09/43] Quick and dirty getting parseTemplate module into global scope so we can test it --- package.json | 1 + spec/lib/parsers.js | 110 ++++++++++++++++++++++++++++ spec/rivets/text_template_parser.js | 78 ++++++++++---------- spec/runner.html | 1 + 4 files changed, 149 insertions(+), 41 deletions(-) create mode 100644 spec/lib/parsers.js diff --git a/package.json b/package.json index da93431ef..2dfa4ac4b 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "sightglass": "~0.2.4" }, "scripts": { + "pretest": "babel src/parsers.js --out-file spec/lib/parsers.js --modules umd", "test": "mocha-phantomjs spec/runner.html", "build": "webpack" }, diff --git a/spec/lib/parsers.js b/spec/lib/parsers.js new file mode 100644 index 000000000..9fdcc0b5a --- /dev/null +++ b/spec/lib/parsers.js @@ -0,0 +1,110 @@ +(function (global, factory) { + if (typeof define === 'function' && define.amd) { + define(['exports'], factory); + } else if (typeof exports !== 'undefined') { + factory(exports); + } else { + var mod = { + exports: {} + }; + factory(mod.exports); + global.parsers = mod.exports; + } +})(this, function (exports) { + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + // Parser and tokenizer for getting the type and value from a string. + exports.parseType = parseType; + + // Template parser and tokenizer for mustache-style text content bindings. + // Parses the template and returns a set of tokens, separating static portions + // of text from binding declarations. + exports.parseTemplate = parseTemplate; + var PRIMITIVE = 0; + var KEYPATH = 1; + var TEXT = 0; + var BINDING = 1; + function parseType(string) { + var type = PRIMITIVE; + var value = string; + + if (/^'.*'$|^".*"$/.test(string)) { + value = string.slice(1, -1); + } else if (string === 'true') { + value = true; + } else if (string === 'false') { + value = false; + } else if (string === 'null') { + value = null; + } else if (string === 'undefined') { + value = undefined; + } else if (isNaN(Number(string)) === false) { + value = Number(string); + } else { + type = KEYPATH; + } + + return { type: type, value: value }; + } + + function parseTemplate(template, delimiters) { + var tokens = []; + var length = template.length; + var index = 0; + var lastIndex = 0; + + while (lastIndex < length) { + index = template.indexOf(delimiters[0], lastIndex); + + if (index < 0) { + tokens.push({ + type: TEXT, + value: template.slice(lastIndex) + }); + + break; + } else { + if (index > 0 && lastIndex < index) { + tokens.push({ + type: TEXT, + value: template.slice(lastIndex, index) + }); + } + + lastIndex = index + delimiters[0].length; + index = template.indexOf(delimiters[1], lastIndex); + + if (index < 0) { + var substring = template.slice(lastIndex - delimiters[1].length); + lastToken = tokens[tokens.length - 1]; + + if (lastToken && lastToken.type === TEXT) { + lastToken.value += substring; + } else { + tokens.push({ + type: TEXT, + value: substring + }); + } + + break; + } + + var value = template.slice(lastIndex, index).trim(); + + tokens.push({ + type: BINDING, + value: value + }); + + lastIndex = index + delimiters[1].length; + } + } + + return tokens; + } +}); diff --git a/spec/rivets/text_template_parser.js b/spec/rivets/text_template_parser.js index aa15d7b05..2fb42af94 100644 --- a/spec/rivets/text_template_parser.js +++ b/spec/rivets/text_template_parser.js @@ -1,55 +1,51 @@ -describe("Rivets.TextTemplateParser", function() { - var Rivets = rivets._ - - describe("parse()", function() { - it("tokenizes a text template", function() { - template = "Hello {{ user.name }}, you have {{ user.messages.unread | length }} unread messages." +describe("parseTemplate()", function() { + it("tokenizes a text template", function() { + template = "Hello {{ user.name }}, you have {{ user.messages.unread | length }} unread messages." + + expected = [ + {type: 0, value: "Hello "}, + {type: 1, value: "user.name"}, + {type: 0, value: ", you have "}, + {type: 1, value: "user.messages.unread | length"}, + {type: 0, value: " unread messages."} + ] + + results = parsers.parseTemplate(template, ['{{', '}}']) + results.length.should.equal(5) + + for (i = 0; i < results.length; i++) { + results[i].type.should.equal(expected[i].type) + results[i].value.should.equal(expected[i].value) + } + }) - expected = [ - {type: 0, value: "Hello "}, - {type: 1, value: "user.name"}, - {type: 0, value: ", you have "}, - {type: 1, value: "user.messages.unread | length"}, - {type: 0, value: " unread messages."} - ] + describe("with no binding fragments", function() { + it("should return a single text token", function() { + template = "Hello World!" + expected = [{type: 0, value: "Hello World!"}] - results = Rivets.TextTemplateParser.parse(template, ['{{', '}}']) - results.length.should.equal(5) + results = parsers.parseTemplate(template, ['{{', '}}']) + results.length.should.equal(1) for (i = 0; i < results.length; i++) { results[i].type.should.equal(expected[i].type) results[i].value.should.equal(expected[i].value) } }) + }) - describe("with no binding fragments", function() { - it("should return a single text token", function() { - template = "Hello World!" - expected = [{type: 0, value: "Hello World!"}] - - results = Rivets.TextTemplateParser.parse(template, ['{{', '}}']) - results.length.should.equal(1) - - for (i = 0; i < results.length; i++) { - results[i].type.should.equal(expected[i].type) - results[i].value.should.equal(expected[i].value) - } - }) - }) - - describe("with only a binding fragment", function() { - it("should return a single binding token", function() { - template = "{{ user.name }}" - expected = [{type: 1, value: "user.name"}] + describe("with only a binding fragment", function() { + it("should return a single binding token", function() { + template = "{{ user.name }}" + expected = [{type: 1, value: "user.name"}] - results = Rivets.TextTemplateParser.parse(template, ['{{', '}}']) - results.length.should.equal(1) + results = parsers.parseTemplate(template, ['{{', '}}']) + results.length.should.equal(1) - for (i = 0; i < results.length; i++) { - results[i].type.should.equal(expected[i].type) - results[i].value.should.equal(expected[i].value) - } - }) + for (i = 0; i < results.length; i++) { + results[i].type.should.equal(expected[i].type) + results[i].value.should.equal(expected[i].value) + } }) }) }) diff --git a/spec/runner.html b/spec/runner.html index 793d27ee2..0a9a1ae8a 100644 --- a/spec/runner.html +++ b/spec/runner.html @@ -19,6 +19,7 @@ + From 45cdbb276a727ed5cbf0ad00e6695d11d56ea9eb Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Sat, 15 Aug 2015 09:57:22 +0100 Subject: [PATCH 10/43] arrow function arguments fix --- src/adapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/adapter.js b/src/adapter.js index c41466492..4254a3c86 100644 --- a/src/adapter.js +++ b/src/adapter.js @@ -52,8 +52,8 @@ const adapter = { let map = this.weakReference(obj) let weakmap = this.weakmap - obj[fn] = () => { - let response = original.apply(obj, arguments) + obj[fn] = (...args) => { + let response = original.apply(obj, args) Object.keys(map.pointers).forEach(r => { let k = map.pointers[r] From fbdabb221a4e171696cf966f37393e76d797a5e3 Mon Sep 17 00:00:00 2001 From: Greg Larrenaga Date: Sun, 16 Aug 2015 16:45:07 -0700 Subject: [PATCH 11/43] All tests passing, fixes issue where the value binders prototype recieves the incorrect context, and therfore had no observer when the binding publishes. --- src/binders.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/binders.js b/src/binders.js index 94ce22b1b..d6e962ed3 100644 --- a/src/binders.js +++ b/src/binders.js @@ -98,6 +98,8 @@ const binders = { bind: function(el) { if (!(el.tagName === 'INPUT' && el.type === 'radio')) { this.event = el.tagName === 'SELECT' ? 'change' : 'input' + + this.publish = this.publish.bind(this) bindEvent(el, this.event, this.publish) } }, From 519b4ac0078b6fa5438f2c190eada3aa43220fd5 Mon Sep 17 00:00:00 2001 From: Greg Larrenaga Date: Sun, 16 Aug 2015 16:45:07 -0700 Subject: [PATCH 12/43] All tests passing, fixes issue where the value binders prototype recieves the incorrect context, and therefore had no observer when the binding publishes. --- src/binders.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/binders.js b/src/binders.js index 94ce22b1b..d6e962ed3 100644 --- a/src/binders.js +++ b/src/binders.js @@ -98,6 +98,8 @@ const binders = { bind: function(el) { if (!(el.tagName === 'INPUT' && el.type === 'radio')) { this.event = el.tagName === 'SELECT' ? 'change' : 'input' + + this.publish = this.publish.bind(this) bindEvent(el, this.event, this.publish) } }, From 0e160db01c9fe3ef1f6f32db2ce2bbf4d8e39005 Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Fri, 21 Aug 2015 10:01:35 +0100 Subject: [PATCH 13/43] Added test for rv-each-* popping, and fix to pass test --- spec/rivets/binders.js | 12 ++++++++++++ src/binders.js | 6 +++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/spec/rivets/binders.js b/spec/rivets/binders.js index f853093eb..5c9c80a94 100644 --- a/spec/rivets/binders.js +++ b/spec/rivets/binders.js @@ -72,6 +72,18 @@ describe("Rivets.binders", function() { Should(fragment.childNodes[1].innerText).be.exactly("howdy"); }); + it("lets you pop an item", function() { + var view = rivets.bind(fragment, model); + var originalLength = model.items.length; + + // one child for each element in the model plus 1 for the comment placeholder + Should(fragment.childNodes.length).be.exactly(model.items.length + 1); + + model.items.pop(); + Should(model.items.length).be.exactly(originalLength - 1); + Should(fragment.childNodes.length).be.exactly(model.items.length + 1); + }) + it("lets you push an item", function() { var view = rivets.bind(fragment, model); var originalLength = model.items.length; diff --git a/src/binders.js b/src/binders.js index d6e962ed3..0681852da 100644 --- a/src/binders.js +++ b/src/binders.js @@ -11,6 +11,10 @@ const getString = (value) => { return defined(value) ? value.toString() : undefined } +const times = (n, cb) => { + for (let i = 0; i < n; i++) cb() +} + const binders = { // Sets the element's text value. text: (el, value) => { @@ -268,7 +272,7 @@ const binders = { let collection = collection || [] if (this.iterated.length > collection.length) { - Array(this.iterated.length - collection.length).forEach(() => { + times(this.iterated.length - collection.length, () => { let view = this.iterated.pop() view.unbind() this.marker.parentNode.removeChild(view.els[0]) From a46ccb41c1b1b05245677a57f1bf8235d735ccf6 Mon Sep 17 00:00:00 2001 From: Greg Larrenaga Date: Sat, 12 Sep 2015 17:17:02 -0700 Subject: [PATCH 14/43] Moving the scope binding of a binders publish method upstream to the binding constructor. This will ensure that users do not need to manually ensure that publish methods get the right scope. --- dist/rivets.bundled.js | 2069 ++++++++++++++++++++++++++++++++++++++++ spec/lib/parsers.js | 13 +- src/binders.js | 1 - src/bindings.js | 1 + 4 files changed, 2077 insertions(+), 7 deletions(-) create mode 100644 dist/rivets.bundled.js diff --git a/dist/rivets.bundled.js b/dist/rivets.bundled.js new file mode 100644 index 000000000..5151f76c7 --- /dev/null +++ b/dist/rivets.bundled.js @@ -0,0 +1,2069 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define(factory); + else if(typeof exports === 'object') + exports["rivets"] = factory(); + else + root["rivets"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; + + var sightglass = _interopRequire(__webpack_require__(1)); + + var rivets = _interopRequire(__webpack_require__(2)); + + var View = _interopRequire(__webpack_require__(4)); + + var adapter = _interopRequire(__webpack_require__(8)); + + var binders = _interopRequire(__webpack_require__(9)); + + // Module factory. Integrates sightglass and public API methods. Returns the + // public interface. + var factory = function (sightglass) { + rivets.sightglass = sightglass; + rivets.binders = binders; + rivets.adapters["."] = adapter; + + // Binds some data to a template / element. Retuddrns a Rivets.View instance. + rivets.bind = function (el) { + var models = arguments[1] === undefined ? {} : arguments[1]; + var options = arguments[2] === undefined ? {} : arguments[2]; + + var view = new View(el, models, options); + view.bind(); + return view; + }; + + // Initializes a new instance of a component on the specified element and + // returns a Rivets.View instance. + rivets.init = function (component, el) { + var data = arguments[2] === undefined ? {} : arguments[2]; + + if (!el) { + el = document.createElement("div"); + } + + var component = rivets.components[component]; + el.innerHTML = component.template.call(rivets, el); + var scope = component.initialize.call(rivets, el, data); + + var view = new View(el, scope); + view.bind(); + return view; + }; + + return rivets; + }; + + module.exports = factory(sightglass); + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict"; + + (function () { + // Public sightglass interface. + function sightglass(obj, keypath, callback, options) { + return new Observer(obj, keypath, callback, options); + } + + // Batteries not included. + sightglass.adapters = {}; + + // Constructs a new keypath observer and kicks things off. + function Observer(obj, keypath, callback, options) { + this.options = options || {}; + this.options.adapters = this.options.adapters || {}; + this.obj = obj; + this.keypath = keypath; + this.callback = callback; + this.objectPath = []; + this.parse(); + + if (isObject(this.target = this.realize())) { + this.set(true, this.key, this.target, this.callback); + } + } + + // Tokenizes the provided keypath string into interface + path tokens for the + // observer to work with. + Observer.tokenize = function (keypath, interfaces, root) { + var tokens = []; + var current = { i: root, path: "" }; + var index, chr; + + for (index = 0; index < keypath.length; index++) { + chr = keypath.charAt(index); + + if (!! ~interfaces.indexOf(chr)) { + tokens.push(current); + current = { i: chr, path: "" }; + } else { + current.path += chr; + } + } + + tokens.push(current); + return tokens; + }; + + // Parses the keypath using the interfaces defined on the view. Sets variables + // for the tokenized keypath as well as the end key. + Observer.prototype.parse = function () { + var interfaces = this.interfaces(); + var root, path; + + if (!interfaces.length) { + error("Must define at least one adapter interface."); + } + + if (!! ~interfaces.indexOf(this.keypath[0])) { + root = this.keypath[0]; + path = this.keypath.substr(1); + } else { + if (typeof (root = this.options.root || sightglass.root) === "undefined") { + error("Must define a default root adapter."); + } + + path = this.keypath; + } + + this.tokens = Observer.tokenize(path, interfaces, root); + this.key = this.tokens.pop(); + }; + + // Realizes the full keypath, attaching observers for every key and correcting + // old observers to any changed objects in the keypath. + Observer.prototype.realize = function () { + var current = this.obj; + var unreached = false; + var prev; + + this.tokens.forEach(function (token, index) { + if (isObject(current)) { + if (typeof this.objectPath[index] !== "undefined") { + if (current !== (prev = this.objectPath[index])) { + this.set(false, token, prev, this.update.bind(this)); + this.set(true, token, current, this.update.bind(this)); + this.objectPath[index] = current; + } + } else { + this.set(true, token, current, this.update.bind(this)); + this.objectPath[index] = current; + } + + current = this.get(token, current); + } else { + if (unreached === false) { + unreached = index; + } + + if (prev = this.objectPath[index]) { + this.set(false, token, prev, this.update.bind(this)); + } + } + }, this); + + if (unreached !== false) { + this.objectPath.splice(unreached); + } + + return current; + }; + + // Updates the keypath. This is called when any intermediary key is changed. + Observer.prototype.update = function () { + var next, oldValue; + + if ((next = this.realize()) !== this.target) { + if (isObject(this.target)) { + this.set(false, this.key, this.target, this.callback); + } + + if (isObject(next)) { + this.set(true, this.key, next, this.callback); + } + + oldValue = this.value(); + this.target = next; + + if (this.value() !== oldValue) this.callback(); + } + }; + + // Reads the current end value of the observed keypath. Returns undefined if + // the full keypath is unreachable. + Observer.prototype.value = function () { + if (isObject(this.target)) { + return this.get(this.key, this.target); + } + }; + + // Sets the current end value of the observed keypath. Calling setValue when + // the full keypath is unreachable is a no-op. + Observer.prototype.setValue = function (value) { + if (isObject(this.target)) { + this.adapter(this.key).set(this.target, this.key.path, value); + } + }; + + // Gets the provided key on an object. + Observer.prototype.get = function (key, obj) { + return this.adapter(key).get(obj, key.path); + }; + + // Observes or unobserves a callback on the object using the provided key. + Observer.prototype.set = function (active, key, obj, callback) { + var action = active ? "observe" : "unobserve"; + this.adapter(key)[action](obj, key.path, callback); + }; + + // Returns an array of all unique adapter interfaces available. + Observer.prototype.interfaces = function () { + var interfaces = Object.keys(this.options.adapters); + + Object.keys(sightglass.adapters).forEach(function (i) { + if (! ~interfaces.indexOf(i)) { + interfaces.push(i); + } + }); + + return interfaces; + }; + + // Convenience function to grab the adapter for a specific key. + Observer.prototype.adapter = function (key) { + return this.options.adapters[key.i] || sightglass.adapters[key.i]; + }; + + // Unobserves the entire keypath. + Observer.prototype.unobserve = function () { + var obj; + + this.tokens.forEach(function (token, index) { + if (obj = this.objectPath[index]) { + this.set(false, token, obj, this.update.bind(this)); + } + }, this); + + if (isObject(this.target)) { + this.set(false, this.key, this.target, this.callback); + } + }; + + // Check if a value is an object than can be observed. + function isObject(obj) { + return typeof obj === "object" && obj !== null; + } + + // Error thrower. + function error(message) { + throw new Error("[sightglass] " + message); + } + + // Export module for Node and the browser. + if (typeof module !== "undefined" && module.exports) { + module.exports = sightglass; + } else if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function () { + return this.sightglass = sightglass; + }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else { + this.sightglass = sightglass; + } + }).call(undefined); + +/***/ }, +/* 2 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + var _constants = __webpack_require__(3); + + var OPTIONS = _constants.OPTIONS; + var EXTENSIONS = _constants.EXTENSIONS; + + var rivets = { + // Global binders. + binders: {}, + + // Global components. + components: {}, + + // Global formatters. + formatters: {}, + + // Global sightglass adapters. + adapters: {}, + + // Default attribute prefix. + prefix: "rv", + + // Default template delimiters. + templateDelimiters: ["{", "}"], + + // Default sightglass root interface. + rootInterface: ".", + + // Preload data by default. + preloadData: true, + + // Default event handler. + handler: function handler(context, ev, binding) { + this.call(context, ev, binding.view.models); + }, + + // Merges an object literal into the corresponding global options. + configure: function configure() { + var _this = this; + + var options = arguments[0] === undefined ? {} : arguments[0]; + + Object.keys(options).forEach(function (option) { + var value = options[option]; + + if (EXTENSIONS.indexOf(option) > -1) { + Object.keys(value).forEach(function (key) { + _this[option][key] = value[key]; + }); + } else { + _this[option] = value; + } + }); + } + }; + + module.exports = rivets; + +/***/ }, +/* 3 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + var OPTIONS = ["prefix", "templateDelimiters", "rootInterface", "preloadData", "handler"]; + + exports.OPTIONS = OPTIONS; + var EXTENSIONS = ["binders", "formatters", "components", "adapters"]; + exports.EXTENSIONS = EXTENSIONS; + +/***/ }, +/* 4 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; + + var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + + var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; + + var rivets = _interopRequire(__webpack_require__(2)); + + var _constants = __webpack_require__(3); + + var OPTIONS = _constants.OPTIONS; + var EXTENSIONS = _constants.EXTENSIONS; + + var _bindings = __webpack_require__(5); + + var Binding = _bindings.Binding; + var TextBinding = _bindings.TextBinding; + var ComponentBinding = _bindings.ComponentBinding; + + var parseTemplate = __webpack_require__(6).parseTemplate; + + var defined = function (value) { + return value !== undefined && value !== null; + }; + + // A collection of bindings built from a set of parent nodes. + + var View = (function () { + // The DOM elements and the model objects for binding are passed into the + // constructor along with any local options that should be used throughout the + // context of the view and it's bindings. + + function View(els, models) { + var _this = this; + + var options = arguments[2] === undefined ? {} : arguments[2]; + + _classCallCheck(this, View); + + if (els.jquery || els instanceof Array) { + this.els = els; + } else { + this.els = [els]; + } + + this.models = models; + + EXTENSIONS.forEach(function (extensionType) { + _this[extensionType] = {}; + + if (options[extensionType]) { + Object.keys(options[extensionType]).forEach(function (key) { + _this[extensionType][key] = options[extensionType][key]; + }); + } + + Object.keys(rivets[extensionType]).forEach(function (key) { + if (!defined(_this[extensionType][key])) { + _this[extensionType][key] = rivets[extensionType][key]; + } + }); + }); + + OPTIONS.forEach(function (option) { + _this[option] = defined(options[option]) ? options[option] : rivets[option]; + }); + + this.build(); + } + + _createClass(View, { + options: { + value: function options() { + var _this = this; + + var options = {}; + + EXTENSIONS.concat(OPTIONS).forEach(function (option) { + options[option] = _this[option]; + }); + + return options; + } + }, + bindingRegExp: { + + // Regular expression used to match binding attributes. + + value: function bindingRegExp() { + return new RegExp("^" + this.prefix + "-"); + } + }, + buildBinding: { + value: function buildBinding(binding, node, type, declaration) { + var pipes = declaration.split("|").map(function (pipe) { + return pipe.trim(); + }); + + var context = pipes.shift().split("<").map(function (ctx) { + return ctx.trim(); + }); + + var keypath = context.shift(); + var dependencies = context.shift(); + var options = { formatters: pipes }; + + if (dependencies) { + options.dependencies = dependencies.split(/\s+/); + } + + this.bindings.push(new binding(this, node, type, keypath, options)); + } + }, + build: { + + // Parses the DOM tree and builds `Binding` instances for every matched + // binding declaration. + + value: function build() { + var _this = this; + + this.bindings = []; + + var parse = function (node) { + var block = false; + + if (node.nodeType === 3) { + var delimiters = _this.templateDelimiters; + + if (delimiters) { + var tokens = parseTemplate(node.data, delimiters); + + if (tokens.length) { + if (!(tokens.length === 1 && tokens[0].type === 0)) { + tokens.forEach(function (token) { + var text = document.createTextNode(token.value); + node.parentNode.insertBefore(text, node); + + if (token.type === 1) { + _this.buildBinding(TextBinding, text, null, token.value); + } + }); + + node.parentNode.removeChild(node); + } + } + } + } else if (node.nodeType === 1) { + block = _this.traverse(node); + } + + if (!block) { + Array.prototype.slice.call(node.childNodes).forEach(parse); + } + }; + + this.els.forEach(parse); + + this.bindings.sort(function (a, b) { + var aPriority = defined(a.binder) ? a.binder.priority || 0 : 0; + var bPriority = defined(b.binder) ? b.binder.priority || 0 : 0; + return bPriority - aPriority; + }); + } + }, + traverse: { + value: function traverse(node) { + var _this = this; + + var bindingRegExp = this.bindingRegExp(); + var block = node.nodeName === "SCRIPT" || node.nodeName === "STYLE"; + var attributes = null; + + Array.prototype.slice.call(node.attributes).forEach(function (attribute) { + if (bindingRegExp.test(attribute.name)) { + (function () { + var type = attribute.name.replace(bindingRegExp, ""); + var binder = _this.binders[type]; + + if (!binder) { + Object.keys(_this.binders).forEach(function (identifier) { + var value = _this.binders[identifier]; + + if (identifier !== "*" && identifier.indexOf("*") > -1) { + var regexp = new RegExp("^" + identifier.replace(/\*/g, ".+") + "$"); + + if (regexp.test(type)) { + binder = value; + } + } + }); + } + + if (!defined(binder)) { + binder = _this.binders["*"]; + } + + if (binder.block) { + block = true; + attributes = [attribute]; + } + })(); + } + }); + + attributes = attributes || Array.prototype.slice.call(node.attributes); + + attributes.forEach(function (attribute) { + if (bindingRegExp.test(attribute.name)) { + var type = attribute.name.replace(bindingRegExp, ""); + _this.buildBinding(Binding, node, type, attribute.value); + } + }); + + if (!block) { + var type = node.nodeName.toLowerCase(); + + if (this.components[type] && !node._bound) { + this.bindings.push(new ComponentBinding(this, node, type)); + block = true; + } + } + + return block; + } + }, + select: { + + // Returns an array of bindings where the supplied function evaluates to true. + + value: function select(fn) { + return this.bindings.filter(fn); + } + }, + bind: { + + // Binds all of the current bindings for this view. + + value: function bind() { + this.bindings.forEach(function (binding) { + binding.bind(); + }); + } + }, + unbind: { + + // Unbinds all of the current bindings for this view. + + value: function unbind() { + this.bindings.forEach(function (binding) { + binding.unbind(); + }); + } + }, + sync: { + + // Syncs up the view with the model by running the routines on all bindings. + + value: function sync() { + this.bindings.forEach(function (binding) { + binding.sync(); + }); + } + }, + publish: { + + // Publishes the input values from the view back to the model (reverse sync). + + value: function publish() { + var publishes = this.select(function (binding) { + if (defined(binding.binder)) { + return binding.binder.publishes; + } + }); + + publishes.forEach(function (binding) { + binding.publish(); + }); + } + }, + update: { + + // Updates the view's models along with any affected bindings. + + value: function update() { + var _this = this; + + var models = arguments[0] === undefined ? {} : arguments[0]; + + Object.keys(models).forEach(function (key) { + _this.models[key] = models[key]; + }); + + this.bindings.forEach(function (binding) { + if (defined(binding.update)) { + binding.update(models); + } + }); + } + } + }); + + return View; + })(); + + module.exports = View; + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; + + var _toConsumableArray = function (arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }; + + var _get = function get(object, property, receiver) { var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc && desc.writable) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + + var _inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }; + + var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + + var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var rivets = _interopRequire(__webpack_require__(2)); + + var parseType = __webpack_require__(6).parseType; + + var getInputValue = __webpack_require__(7).getInputValue; + + var defined = function (value) { + return value !== undefined && value !== null; + }; + + // A single binding between a model attribute and a DOM element. + + var Binding = exports.Binding = (function () { + // All information about the binding is passed into the constructor; the + // containing view, the DOM node, the type of binding, the model object and the + // keypath at which to listen for changes. + + function Binding(view, el, type, keypath) { + var options = arguments[4] === undefined ? {} : arguments[4]; + + _classCallCheck(this, Binding); + + this.view = view; + this.el = el; + this.type = type; + this.keypath = keypath; + this.options = options; + this.formatters = options.formatters || []; + this.dependencies = []; + this.formatterObservers = {}; + this.model = undefined; + this.setBinder(); + + this.bind = this.bind.bind(this); + this.unbind = this.unbind.bind(this); + this.sync = this.sync.bind(this); + this.publish = this.publish.bind(this); + } + + _createClass(Binding, { + setBinder: { + + // Sets the binder to use when binding and syncing. + + value: function setBinder() { + var _this = this; + + this.binder = this.view.binders[this.type]; + + if (!this.binder) { + Object.keys(this.view.binders).forEach(function (identifier) { + var value = _this.view.binders[identifier]; + + if (identifier !== "*" && identifier.indexOf("*") > -1) { + var regexp = new RegExp("^" + identifier.replace(/\*/g, ".+") + "$"); + + if (regexp.test(_this.type)) { + _this.binder = value; + _this.args = new RegExp("^" + identifier.replace(/\*/g, "(.+)") + "$").exec(_this.type); + _this.args.shift(); + } + } + }); + } + + if (!defined(this.binder)) { + this.binder = this.view.binders["*"]; + } + + if (this.binder instanceof Function) { + this.binder = { routine: this.binder }; + } + } + }, + observe: { + + // Observes the object keypath to run the provided callback. + + value: function observe(obj, keypath, callback) { + return rivets.sightglass(obj, keypath, callback, { + root: this.view.rootInterface, + adapters: this.view.adapters + }); + } + }, + parseTarget: { + value: function parseTarget() { + var token = parseType(this.keypath); + + if (token.type === 0) { + this.value = token.value; + } else { + this.observer = this.observe(this.view.models, this.keypath, this.sync); + this.model = this.observer.target; + } + } + }, + formattedValue: { + + // Applies all the current formatters to the supplied value and returns the + // formatted value. + + value: function formattedValue(value) { + var _this = this; + + this.formatters.forEach(function (formatterStr, fi) { + var args = formatterStr.match(/[^\s']+|'([^']|'[^\s])*'|"([^"]|"[^\s])*"/g); + var id = args.shift(); + var formatter = _this.view.formatters[id]; + var processedArgs = []; + + args = args.map(parseType); + + args.forEach(function (arg, ai) { + if (arg.type === 0) { + processedArgs.push(arg.value); + } else { + if (!defined(_this.formatterObservers[fi])) { + _this.formatterObservers[fi] = {}; + } + + var observer = _this.formatterObservers[fi][ai]; + + if (!observer) { + observer = _this.observe(_this.view.models, arg.value, _this.sync); + _this.formatterObservers[fi][ai] = observer; + } + + processedArgs.push(observer.value()); + } + }); + + if (formatter && formatter.read instanceof Function) { + value = formatter.read.apply(formatter, [value].concat(processedArgs)); + } else if (formatter instanceof Function) { + value = formatter.apply(undefined, [value].concat(processedArgs)); + } + }); + + return value; + } + }, + eventHandler: { + + // Returns an event handler for the binding around the supplied function. + + value: function eventHandler(fn) { + var binding = this; + var handler = binding.view.handler; + + return function (ev) { + handler.call(fn, this, ev, binding); + }; + } + }, + set: { + + // Sets the value for the binding. This Basically just runs the binding routine + // with the suplied value formatted. + + value: function set(value) { + if (value instanceof Function && !this.binder["function"]) { + value = this.formattedValue(value.call(this.model)); + } else { + value = this.formattedValue(value); + } + + if (this.binder.routine) { + this.binder.routine.call(this, this.el, value); + } + } + }, + sync: { + + // Syncs up the view binding with the model. + + value: function sync() { + var _this = this; + + if (this.observer) { + if (this.model !== this.observer.target) { + var deps = this.options.dependencies; + + this.dependencies.forEach(function (observer) { + observer.unobserve(); + }); + + this.dependencies = []; + this.model = this.observer.target; + + if (defined(model) && deps && deps.length) { + deps.forEach(function (dependency) { + var observer = _this.observe(_this.model, dependency, _this.sync); + _this.dependencies.push(observer); + }); + } + } + + this.set(this.observer.value()); + } else { + this.set(this.value); + } + } + }, + publish: { + + // Publishes the value currently set on the input element back to the model. + + value: function publish() { + var _this = this; + + if (this.observer) { + (function () { + var value = _this.getValue(_this.el); + + _this.formatters.slice(0).reverse().forEach(function (formatter) { + var args = formatter.split(/\s+/); + var id = args.shift(); + var f = _this.view.formatters[id]; + + if (defined(f) && f.publish) { + value = f.publish.apply(f, [value].concat(_toConsumableArray(args))); + } + }); + + _this.observer.setValue(value); + })(); + } + } + }, + bind: { + + // Subscribes to the model for changes at the specified keypath. Bi-directional + // routines will also listen for changes on the element to propagate them back + // to the model. + + value: function bind() { + var _this = this; + + this.parseTarget(); + + if (defined(this.binder.bind)) { + this.binder.bind.call(this, this.el); + } + + if (defined(this.model) && defined(this.options.dependencies)) { + this.options.dependencies.forEach(function (dependency) { + var observer = _this.observe(_this.model, dependency, _this.sync); + _this.dependencies.push(observer); + }); + } + + if (this.view.preloadData) { + this.sync(); + } + } + }, + unbind: { + + // Unsubscribes from the model and the element. + + value: function unbind() { + var _this = this; + + if (defined(this.binder.unbind)) { + this.binder.unbind.call(this, this.el); + } + + if (defined(this.observer)) { + this.observer.unobserve(); + } + + this.dependencies.forEach(function (observer) { + observer.unobserve(); + }); + + this.dependencies = []; + + Object.keys(this.formatterObservers).forEach(function (fi) { + var args = _this.formatterObservers[fi]; + + Object.keys(args).forEach(function (ai) { + args[ai].unobserve(); + }); + }); + + this.formatterObservers = {}; + } + }, + update: { + + // Updates the binding's model from what is currently set on the view. Unbinds + // the old model first and then re-binds with the new model. + + value: function update() { + var models = arguments[0] === undefined ? {} : arguments[0]; + + if (defined(this.observer)) { + this.model = this.observer.target; + } + + if (defined(this.binder.update)) { + this.binder.update.call(this, models); + } + } + }, + getValue: { + + // Returns elements value + + value: function getValue(el) { + if (this.binder && defined(this.binder.getValue)) { + return this.binder.getValue.call(this, el); + } else { + return getInputValue(el); + } + } + } + }); + + return Binding; + })(); + + // component view encapsulated as a binding within it's parent view. + + var ComponentBinding = exports.ComponentBinding = (function (_Binding) { + // Initializes a component binding for the specified view. The raw component + // element is passed in along with the component type. Attributes and scope + // inflections are determined based on the components defined attributes. + + function ComponentBinding(view, el, type) { + var _this = this; + + _classCallCheck(this, ComponentBinding); + + this.view = view; + this.el = el; + this.type = type; + this.component = this.view.components[this.type]; + this["static"] = {}; + this.observers = {}; + this.upstreamObservers = {}; + + var bindingRegExp = view.bindingRegExp(); + + if (this.el.attributes) { + this.el.attributes.forEach(function (attribute) { + if (!bindingRegExp.test(attribute.name)) { + var propertyName = _this.camelCase(attribute.name); + var stat = _this.component["static"]; + + if (stat && stat.indexOf(propertyName) > -1) { + _this["static"][propertyName] = attribute.value; + } else { + _this.observers[propertyName] = attribute.value; + } + } + }); + } + } + + _inherits(ComponentBinding, _Binding); + + _createClass(ComponentBinding, { + sync: { + + // Intercepts `Rivets.Binding::sync` since component bindings are not bound to + // a particular model to update it's value. + + value: function sync() {} + }, + update: { + + // Intercepts `Rivets.Binding::update` since component bindings are not bound + // to a particular model to update it's value. + + value: function update() {} + }, + publish: { + + // Intercepts `Rivets.Binding::publish` since component bindings are not bound + // to a particular model to update it's value. + + value: function publish() {} + }, + locals: { + + // Returns an object map using the component's scope inflections. + + value: function locals() { + var _this = this; + + var result = {}; + + Object.keys(this["static"]).forEach(function (key) { + result[key] = _this["static"][key]; + }); + + Object.keys(this.observers).forEach(function (key) { + result[key] = _this.observers[key].value(); + }); + + return result; + } + }, + camelCase: { + + // Returns a camel-cased version of the string. Used when translating an + // element's attribute name into a property name for the component's scope. + + value: function camelCase(string) { + return string.replace(/-([a-z])/g, function (grouped) { + grouped[1].toUpperCase(); + }); + } + }, + bind: { + + // Intercepts `Rivets.Binding::bind` to build `@componentView` with a localized + // map of models from the root view. Bind `@componentView` on subsequent calls. + + value: function bind() { + var _this = this; + + if (!this.bound) { + Object.keys(this.observers).forEach(function (key) { + var keypath = _this.observers[key]; + + _this.observers[key] = _this.observe(_this.view.models, keypath, (function (key) { + return function () { + _this.componentView.models[key] = _this.observers[key].value(); + }; + }).call(_this, key)); + }); + + this.bound = true; + } + + if (defined(this.componentView)) { + this.componentView.bind(); + } else { + (function () { + _this.el.innerHTML = _this.component.template.call(_this); + var scope = _this.component.initialize.call(_this, _this.el, _this.locals()); + _this.el._bound = true; + + var options = {}; + + EXTENSIONS.forEach(function (extensionType) { + options[extensionType] = {}; + + if (_this.component[extensionType]) { + Object.keys(_this.component[extensionType]).forEach(function (key) { + options[extensionType][key] = _this.component[extensionType][key]; + }); + } + + Object.keys(_this.view[extensionType]).forEach(function (key) { + if (!defined(options[extensionType][key])) { + options[extensionType][key] = _this.view[extensionType][key]; + } + }); + }); + + OPTIONS.forEach(function (option) { + if (defined(_this.component[option])) { + options[option] = _this.component[option]; + } else { + options[option] = _this.view[option]; + } + }); + + _this.componentView = new View(_this.el, scope, options); + _this.componentView.bind(); + + Object.keys(_this.observers).forEach(function (key) { + var observer = _this.observers[key]; + var models = _this.componentView.models; + + var upstream = _this.observe(models, key, (function (key, observer) { + return function () { + observer.setValue(_this.componentView.models[key]); + }; + }).call(_this, key, observer)); + + _this.upstreamObservers[key] = upstream; + }); + })(); + } + } + }, + unbind: { + + // Intercept `Rivets.Binding::unbind` to be called on `@componentView`. + + value: function unbind() { + var _this = this; + + Object.keys(this.upstreamObservers).forEach(function (key) { + _this.upstreamObservers[key].unobserve(); + }); + + Object.keys(this.observers).forEach(function (key) { + _this.observers[key].unobserve(); + }); + + if (defined(this.componentView)) { + this.componentView.unbind.call(this); + } + } + } + }); + + return ComponentBinding; + })(Binding); + + // A text node binding, defined internally to deal with text and element node + // differences while avoiding it being overwritten. + + var TextBinding = exports.TextBinding = (function (_Binding2) { + // Initializes a text binding for the specified view and text node. + + function TextBinding(view, el, type, keypath) { + var options = arguments[4] === undefined ? {} : arguments[4]; + + _classCallCheck(this, TextBinding); + + this.view = view; + this.el = el; + this.type = type; + this.keypath = keypath; + this.options = options; + this.formatters = this.options.formatters || []; + this.dependencies = []; + this.formatterObservers = {}; + + this.binder = { + routine: function (node, value) { + node.data = defined(value) ? value : ""; + } + }; + + this.sync = this.sync.bind(this); + } + + _inherits(TextBinding, _Binding2); + + _createClass(TextBinding, { + sync: { + + // Wrap the call to `sync` to avoid function context issues. + + value: function sync() { + _get(Object.getPrototypeOf(TextBinding.prototype), "sync", this).call(this); + } + } + }); + + return TextBinding; + })(Binding); + +/***/ }, +/* 6 */ +/***/ function(module, exports) { + + + + // Parser and tokenizer for getting the type and value from a string. + "use strict"; + + exports.parseType = parseType; + + // Template parser and tokenizer for mustache-style text content bindings. + // Parses the template and returns a set of tokens, separating static portions + // of text from binding declarations. + exports.parseTemplate = parseTemplate; + Object.defineProperty(exports, "__esModule", { + value: true + }); + var PRIMITIVE = 0; + var KEYPATH = 1; + var TEXT = 0; + var BINDING = 1; + function parseType(string) { + var type = PRIMITIVE; + var value = string; + + if (/^'.*'$|^".*"$/.test(string)) { + value = string.slice(1, -1); + } else if (string === "true") { + value = true; + } else if (string === "false") { + value = false; + } else if (string === "null") { + value = null; + } else if (string === "undefined") { + value = undefined; + } else if (isNaN(Number(string)) === false) { + value = Number(string); + } else { + type = KEYPATH; + } + + return { type: type, value: value }; + } + + function parseTemplate(template, delimiters) { + var tokens = []; + var length = template.length; + var index = 0; + var lastIndex = 0; + + while (lastIndex < length) { + index = template.indexOf(delimiters[0], lastIndex); + + if (index < 0) { + tokens.push({ + type: TEXT, + value: template.slice(lastIndex) + }); + + break; + } else { + if (index > 0 && lastIndex < index) { + tokens.push({ + type: TEXT, + value: template.slice(lastIndex, index) + }); + } + + lastIndex = index + delimiters[0].length; + index = template.indexOf(delimiters[1], lastIndex); + + if (index < 0) { + var substring = template.slice(lastIndex - delimiters[1].length); + lastToken = tokens[tokens.length - 1]; + + if (lastToken && lastToken.type === TEXT) { + lastToken.value += substring; + } else { + tokens.push({ + type: TEXT, + value: substring + }); + } + + break; + } + + var value = template.slice(lastIndex, index).trim(); + + tokens.push({ + type: BINDING, + value: value + }); + + lastIndex = index + delimiters[1].length; + } + } + + return tokens; + } + +/***/ }, +/* 7 */ +/***/ function(module, exports) { + + "use strict"; + + exports.bindEvent = bindEvent; + exports.unbindEvent = unbindEvent; + exports.getInputValue = getInputValue; + Object.defineProperty(exports, "__esModule", { + value: true + }); + var $ = window.jQuery || window.$; + + function bindEvent(el, event, handler) { + if ($) { + $(el).on(event, handler); + } else { + el.addEventListener(event, handler, false); + } + } + + function unbindEvent(el, event, handler) { + if ($) { + $(el).off(event, handler); + } else { + el.removeEventListener(event, handler, false); + } + } + + function getInputValue(el) { + if ($) { + var $el = $(el); + + if ($el.attr("type") === "checkbox") { + return $el.is(":checked"); + } else { + return $el.val(); + } + } else { + if (el.type === "checkbox") { + return el.checked; + } else if (el.type === "select-multiple") { + var _ret = (function () { + var results = []; + + el.options.forEach(function (option) { + if (option.selected) { + results.push(option.value); + } + }); + + return { + v: results + }; + })(); + + if (typeof _ret === "object") { + return _ret.v; + } + } else { + return el.value; + } + } + } + +/***/ }, +/* 8 */ +/***/ function(module, exports) { + + // The default `.` adapter thats comes with Rivets.js. Allows subscribing to + // properties on plain objects, implemented in ES5 natives using + // `Object.defineProperty`. + + "use strict"; + + var defined = function (value) { + return value !== undefined && value !== null; + }; + + var ARRAY_METHODS = ["push", "pop", "shift", "unshift", "sort", "reverse", "splice"]; + + var adapter = { + id: "_rv", + counter: 0, + weakmap: {}, + + weakReference: function weakReference(obj) { + if (!obj.hasOwnProperty(this.id)) { + var id = this.counter++; + + Object.defineProperty(obj, this.id, { + value: id + }); + } + + if (!this.weakmap[obj[this.id]]) { + this.weakmap[obj[this.id]] = { + callbacks: {} + }; + } + + return this.weakmap[obj[this.id]]; + }, + + cleanupWeakReference: function cleanupWeakReference(ref, id) { + if (!Object.keys(ref.callbacks).length) { + if (!(ref.pointers && Object.keys(ref.pointers).length)) { + delete this.weakmap[id]; + } + } + }, + + stubFunction: function stubFunction(obj, fn) { + var original = obj[fn]; + var map = this.weakReference(obj); + var weakmap = this.weakmap; + + obj[fn] = function () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var response = original.apply(obj, args); + + Object.keys(map.pointers).forEach(function (r) { + var k = map.pointers[r]; + + if (defined(weakmap[r])) { + if (weakmap[r].callbacks[k] instanceof Array) { + weakmap[r].callbacks[k].forEach(function (callback) { + callback(); + }); + } + } + }); + + return response; + }; + }, + + observeMutations: function observeMutations(obj, ref, keypath) { + var _this = this; + + if (obj instanceof Array) { + var map = this.weakReference(obj); + + if (!defined(map.pointers)) { + map.pointers = {}; + + ARRAY_METHODS.forEach(function (fn) { + _this.stubFunction(obj, fn); + }); + } + + if (!defined(map.pointers[ref])) { + map.pointers[ref] = []; + } + + if (map.pointers[ref].indexOf(keypath) === -1) { + map.pointers[ref].push(keypath); + } + } + }, + + unobserveMutations: function unobserveMutations(obj, ref, keypath) { + if (obj instanceof Array && defined(obj[this.id])) { + var map = this.weakmap[obj[this.id]]; + + if (map) { + var pointers = map.pointers[ref]; + + if (pointers) { + var idx = pointers.indexOf(keypath); + + if (idx > -1) { + pointers.splice(idx, 1); + } + + if (!pointers.length) { + delete map.pointers[ref]; + } + + this.cleanupWeakReference(map, obj[this.id]); + } + } + } + }, + + observe: function observe(obj, keypath, callback) { + var _this = this; + + var callbacks = this.weakReference(obj).callbacks; + + if (!defined(callbacks[keypath])) { + callbacks[keypath] = []; + var desc = Object.getOwnPropertyDescriptor(obj, keypath); + + if (!(desc && (desc.get || desc.set))) { + (function () { + var value = obj[keypath]; + + Object.defineProperty(obj, keypath, { + enumerable: true, + + get: function () { + return value; + }, + + set: function (newValue) { + if (newValue !== value) { + _this.unobserveMutations(value, obj[_this.id], keypath); + value = newValue; + var map = _this.weakmap[obj[_this.id]]; + + if (map) { + (function () { + var callbacks = map.callbacks; + + if (callbacks[keypath]) { + callbacks[keypath].slice().forEach(function (callback) { + if (callbacks[keypath].indexOf(callback) > -1) { + callback(); + } + }); + } + + _this.observeMutations(newValue, obj[_this.id], keypath); + })(); + } + } + } + }); + })(); + } + } + + if (callbacks[keypath].indexOf(callback) === -1) { + callbacks[keypath].push(callback); + } + + this.observeMutations(obj[keypath], obj[this.id], keypath); + }, + + unobserve: function unobserve(obj, keypath, callback) { + var map = this.weakmap[obj[this.id]]; + + if (map) { + var callbacks = map.callbacks[keypath]; + + if (callbacks) { + var idx = callbacks.indexOf(callback); + + if (idx > -1) { + callbacks.splice(idx, 1); + + if (!callbacks.length) { + delete map.callbacks[keypath]; + } + } + + this.unobserveMutations(obj[keypath], obj[this.id], keypath); + this.cleanupWeakReference(map, obj[this.id]); + } + } + }, + + get: function get(obj, keypath) { + return obj[keypath]; + }, + + set: function (obj, keypath, value) { + obj[keypath] = value; + } + }; + + module.exports = adapter; + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; + + var _taggedTemplateLiteral = function (strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }; + + var rivets = _interopRequire(__webpack_require__(2)); + + var _util = __webpack_require__(7); + + var bindEvent = _util.bindEvent; + var unbindEvent = _util.unbindEvent; + + var CHANGE_EVENT = "change"; + + var defined = function (value) { + return value !== undefined && value !== null; + }; + + var getString = function (value) { + return defined(value) ? value.toString() : undefined; + }; + + var times = function (n, cb) { + for (var i = 0; i < n; i++) { + cb(); + } + }; + + var binders = { + // Sets the element's text value. + text: function (el, value) { + el.textContent = defined(value) ? value : ""; + }, + + // Sets the element's HTML content. + html: function (el, value) { + el.innerHTML = defined(value) ? value : ""; + }, + + // Shows the element when value is true. + show: function (el, value) { + el.style.display = value ? "" : "none"; + }, + + // Hides the element when value is true (negated version of `show` binder). + hide: function (el, value) { + el.style.display = value ? "none" : ""; + }, + + // Enables the element when value is true. + enabled: function (el, value) { + el.disabled = !value; + }, + + // Disables the element when value is true (negated version of `enabled` binder). + disabled: function (el, value) { + el.disabled = !!value; + }, + + // Checks a checkbox or radio input when the value is true. Also sets the model + // property when the input is checked or unchecked (two-way binder). + checked: { + publishes: true, + priority: 2000, + + bind: function bind(el) { + bindEvent(el, CHANGE_EVENT, this.publish); + }, + + unbind: function unbind(el) { + unbindEvent(el, CHANGE_EVENT, this.publish); + }, + + routine: function routine(el, value) { + if (el.type === "radio") { + el.checked = getString(el.value) === getString(value); + } else { + el.checked = !!value; + } + } + }, + + // Unchecks a checkbox or radio input when the value is true (negated version of + // `checked` binder). Also sets the model property when the input is checked or + // unchecked (two-way binder). + unchecked: { + publishes: true, + priority: 2000, + + bind: function bind(el) { + bindEvent(el, CHANGE_EVENT, this.publish); + }, + + unbind: function unbind(el) { + unbindEvent(el, CHANGE_EVENT, this.publish); + }, + + routine: function routine(el, value) { + if (el.type === "radio") { + el.checked = getString(el.value) !== getString(value); + } else { + el.checked = !value; + } + } + }, + + // Sets the element's value. Also sets the model property when the input changes + // (two-way binder). + value: { + publishes: true, + priority: 3000, + + bind: function bind(el) { + if (!(el.tagName === "INPUT" && el.type === "radio")) { + this.event = el.tagName === "SELECT" ? "change" : "input"; + + bindEvent(el, this.event, this.publish); + } + }, + + unbind: function unbind(el) { + if (!(el.tagName === "INPUT" && el.type === "radio")) { + unbindEvent(el, this.event, this.publish); + } + }, + + routine: function routine(el, value) { + if (el.tagName === "INPUT" && el.type === "radio") { + el.setAttribute("value", value); + } else if (window.jQuery) { + el = jQuery(el); + + if (getString(value) !== getString(el.val())) { + el.val(defined(value) ? value : ""); + } + } else { + if (el.type === "select-multiple") { + if (value instanceof Array) { + el.options.forEach(function (option) { + option.selected = value.indexOf(option.value) > -1; + }); + } + } else if (getString(value) !== getString(el.value)) { + el.value = defined(value) ? value : ""; + } + } + } + }, + + // Inserts and binds the element and it's child nodes into the DOM when true. + "if": { + block: true, + priority: 4000, + + bind: function bind(el) { + if (!defined(this.marker)) { + var attr = [this.view.prefix, this.type].join("-").replace("--", "-"); + var declaration = el.getAttribute(attr); + + this.marker = document.createComment(_taggedTemplateLiteral([" rivets: ", " ", " "], [" rivets: ", " ", " "]), this.type, declaration); + this.bound = false; + + el.removeAttribute(attr); + el.parentNode.insertBefore(this.marker, el); + el.parentNode.removeChild(el); + } + }, + + unbind: function unbind() { + if (defined(this.nested)) { + this.nested.unbind(); + } + }, + + routine: function routine(el, value) { + var _this = this; + + if (!!value === !this.bound) { + if (value) { + (function () { + var models = {}; + + Object.keys(_this.view.models).forEach(function (key) { + models[key] = _this.view.models[key]; + }); + + if (defined(_this.nested)) { + _this.nested.bind(); + } else { + _this.nested = rivets.bind(el, models, _this.view.options()); + } + + _this.marker.parentNode.insertBefore(el, _this.marker.nextSibling); + _this.bound = true; + })(); + } else { + el.parentNode.removeChild(el); + this.nested.unbind(); + this.bound = false; + } + } + }, + + update: function update(models) { + if (defined(this.nested)) { + this.nested.update(models); + } + } + }, + + // Removes and unbinds the element and it's child nodes into the DOM when true + // (negated version of `if` binder). + unless: { + block: true, + priority: 4000, + + bind: function bind(el) { + rivets.binders["if"].bind.call(this, el); + }, + + unbind: function unbind() { + rivets.binders["if"].unbind.call(this); + }, + + routine: function routine(el, value) { + rivets.binders["if"].routine.call(this, el, !value); + }, + + update: function update(models) { + rivets.binders["if"].update.call(this, models); + } + }, + + // Binds an event handler on the element. + "on-*": { + "function": true, + priority: 1000, + + unbind: function unbind(el) { + if (defined(this.handler)) { + unbindEvent(el, this.args[0], this.handler); + } + }, + + routine: function routine(el, value) { + if (defined(this.handler)) { + unbindEvent(el, this.args[0], this.handler); + } + + this.handler = this.eventHandler(value); + bindEvent(el, this.args[0], this.handler); + } + }, + + // Appends bound instances of the element in place for each item in the array. + "each-*": { + block: true, + priority: 4000, + + bind: function bind(el) { + if (!defined(this.marker)) { + var attr = [this.view.prefix, this.type].join("-").replace("--", "-"); + this.marker = document.createComment(_taggedTemplateLiteral([" rivets: ", " "], [" rivets: ", " "]), this.type); + this.iterated = []; + + el.removeAttribute(attr); + el.parentNode.insertBefore(this.marker, el); + el.parentNode.removeChild(el); + } else { + this.iterated.forEach(function (view) { + view.bind(); + }); + } + }, + + unbind: function unbind(el) { + if (defined(this.iterated)) { + this.iterated.forEach(function (view) { + view.unbind(); + }); + } + }, + + routine: function routine(el, collection) { + var _this = this; + + var modelName = this.args[0]; + var collection = collection || []; + + if (this.iterated.length > collection.length) { + times(this.iterated.length - collection.length, function () { + var view = _this.iterated.pop(); + view.unbind(); + _this.marker.parentNode.removeChild(view.els[0]); + }); + } + + collection.forEach(function (model, index) { + var data = { index: index }; + data[modelName] = model; + + if (!defined(_this.iterated[index])) { + Object.keys(_this.view.models).forEach(function (key) { + if (!defined(data[key])) { + data[key] = _this.view.models[key]; + } + }); + + var previous = _this.marker; + + if (_this.iterated.length) { + previous = _this.iterated[_this.iterated.length - 1].els[0]; + } + + var options = _this.view.options(); + options.preloadData = true; + + var template = el.cloneNode(true); + var view = rivets.bind(template, data, options); + _this.iterated.push(view); + _this.marker.parentNode.insertBefore(template, previous.nextSibling); + } else if (_this.iterated[index].models[modelName] !== model) { + _this.iterated[index].update(data); + } + }); + + if (el.nodeName === "OPTION") { + this.view.bindings.forEach(function (binding) { + if (binding.el === _this.marker.parentNode && binding.type === "value") { + binding.sync(); + } + }); + } + }, + + update: function update(models) { + var _this = this; + + var data = {}; + + Object.keys(models).forEach(function (key) { + if (key !== _this.args[0]) { + data[key] = models[key]; + } + }); + + this.iterated.forEach(function (view) { + view.update(data); + }); + } + }, + + // Adds or removes the class from the element when value is true or false. + "class-*": function _class(el, value) { + var elClass = " " + el.className + " "; + + if (!value === elClass.indexOf(" " + this.args[0] + " ") > -1) { + if (value) { + el.className = "" + el.className + " " + this.args[0]; + } else { + el.className = elClass.replace(" " + this.args[0] + " ", " ").trim(); + } + } + }, + + // Sets the attribute on the element. If no binder above is matched it will fall + // back to using this binder. + "*": function _(el, value) { + if (defined(value)) { + el.setAttribute(this.type, value); + } else { + el.removeAttribute(this.type); + } + } + }; + + module.exports = binders; + +/***/ } +/******/ ]) +}); +; \ No newline at end of file diff --git a/spec/lib/parsers.js b/spec/lib/parsers.js index 9fdcc0b5a..8019e250c 100644 --- a/spec/lib/parsers.js +++ b/spec/lib/parsers.js @@ -16,18 +16,15 @@ Object.defineProperty(exports, '__esModule', { value: true }); - - // Parser and tokenizer for getting the type and value from a string. exports.parseType = parseType; - - // Template parser and tokenizer for mustache-style text content bindings. - // Parses the template and returns a set of tokens, separating static portions - // of text from binding declarations. exports.parseTemplate = parseTemplate; var PRIMITIVE = 0; var KEYPATH = 1; var TEXT = 0; var BINDING = 1; + + // Parser and tokenizer for getting the type and value from a string. + function parseType(string) { var type = PRIMITIVE; var value = string; @@ -51,6 +48,10 @@ return { type: type, value: value }; } + // Template parser and tokenizer for mustache-style text content bindings. + // Parses the template and returns a set of tokens, separating static portions + // of text from binding declarations. + function parseTemplate(template, delimiters) { var tokens = []; var length = template.length; diff --git a/src/binders.js b/src/binders.js index 0681852da..91738e8f4 100644 --- a/src/binders.js +++ b/src/binders.js @@ -103,7 +103,6 @@ const binders = { if (!(el.tagName === 'INPUT' && el.type === 'radio')) { this.event = el.tagName === 'SELECT' ? 'change' : 'input' - this.publish = this.publish.bind(this) bindEvent(el, this.event, this.publish) } }, diff --git a/src/bindings.js b/src/bindings.js index 3026fa7c6..7962c6a53 100644 --- a/src/bindings.js +++ b/src/bindings.js @@ -26,6 +26,7 @@ export class Binding { this.bind = this.bind.bind(this) this.unbind = this.unbind.bind(this) this.sync = this.sync.bind(this) + this.publish = this.publish.bind(this) } // Sets the binder to use when binding and syncing. From 115b77d38702a0290a3200eaad887631e24de665 Mon Sep 17 00:00:00 2001 From: Jean Ponchon Date: Mon, 28 Sep 2015 11:03:51 +0200 Subject: [PATCH 15/43] fix: Undefined constant (missing import) --- src/bindings.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bindings.js b/src/bindings.js index 7962c6a53..289f0fdd6 100644 --- a/src/bindings.js +++ b/src/bindings.js @@ -1,6 +1,7 @@ import rivets from './rivets' import {parseType} from './parsers' import {getInputValue} from './util' +import {EXTENSIONS, OPTIONS} from './constants' const defined = (value) => { return value !== undefined && value !== null From 437dc3dbc4a618a02ed7f33069d0a3b80e8bd20d Mon Sep 17 00:00:00 2001 From: Jean Ponchon Date: Mon, 28 Sep 2015 11:04:40 +0200 Subject: [PATCH 16/43] fix: Typo, model is undefined, use this.model --- src/bindings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings.js b/src/bindings.js index 289f0fdd6..ee0f9eb72 100644 --- a/src/bindings.js +++ b/src/bindings.js @@ -156,7 +156,7 @@ export class Binding { this.dependencies = [] this.model = this.observer.target - if (defined(model) && deps && deps.length) { + if (defined(this.model) && deps && deps.length) { deps.forEach(dependency => { let observer = this.observe(this.model, dependency, this.sync) this.dependencies.push(observer) From afe534ac45aaed3a2fd9a21fce5a0f976d5c6eb8 Mon Sep 17 00:00:00 2001 From: Jean Ponchon Date: Mon, 28 Sep 2015 11:08:06 +0200 Subject: [PATCH 17/43] fix: lastToken variable not in local scope --- src/parsers.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parsers.js b/src/parsers.js index 71ef349ca..75c99df80 100644 --- a/src/parsers.js +++ b/src/parsers.js @@ -35,6 +35,7 @@ export function parseTemplate(template, delimiters) { let length = template.length let index = 0 let lastIndex = 0 + let lastToken = undefined while (lastIndex < length) { index = template.indexOf(delimiters[0], lastIndex) From bd40124066e5100081bb404f8bacb9d84fb8e253 Mon Sep 17 00:00:00 2001 From: Jean Ponchon Date: Thu, 1 Oct 2015 22:28:00 +0200 Subject: [PATCH 18/43] Fix scope of `lastToken` --- src/parsers.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/parsers.js b/src/parsers.js index 75c99df80..8146c8b6c 100644 --- a/src/parsers.js +++ b/src/parsers.js @@ -35,7 +35,6 @@ export function parseTemplate(template, delimiters) { let length = template.length let index = 0 let lastIndex = 0 - let lastToken = undefined while (lastIndex < length) { index = template.indexOf(delimiters[0], lastIndex) @@ -60,7 +59,7 @@ export function parseTemplate(template, delimiters) { if (index < 0) { let substring = template.slice(lastIndex - delimiters[1].length) - lastToken = tokens[tokens.length - 1] + let lastToken = tokens[tokens.length - 1] if (lastToken && lastToken.type === TEXT) { lastToken.value += substring From f4e69245c01be9b14b81b5c6225c30b7ecee1bda Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Wed, 18 Nov 2015 19:19:44 +0000 Subject: [PATCH 19/43] select-multiple bugfix --- src/binders.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/binders.js b/src/binders.js index 91738e8f4..509501b8e 100644 --- a/src/binders.js +++ b/src/binders.js @@ -125,7 +125,7 @@ const binders = { } else { if (el.type === 'select-multiple') { if (value instanceof Array) { - el.options.forEach(option => { + Array.from(el.options).forEach(option => { option.selected = value.indexOf(option.value) > -1 }) } From a0e34ec7d6cb160bcadc575b204b8fabb04f1694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20Am=C3=A9rico?= Date: Thu, 31 Dec 2015 15:23:27 -0300 Subject: [PATCH 20/43] Use textContent instead of innerText in spec --- spec/rivets/binders.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/rivets/binders.js b/spec/rivets/binders.js index 5c9c80a94..2150aa46c 100644 --- a/spec/rivets/binders.js +++ b/spec/rivets/binders.js @@ -56,20 +56,20 @@ describe("Rivets.binders", function() { it("reflects changes to the model into the DOM", function() { var view = rivets.bind(fragment, model); - Should(fragment.childNodes[1].innerText).be.exactly("0"); + Should(fragment.childNodes[1].textContent).be.exactly("0"); model.items[0].val = "howdy"; - Should(fragment.childNodes[1].innerText).be.exactly("howdy"); + Should(fragment.childNodes[1].textContent).be.exactly("howdy"); }); it("reflects changes to the model into the DOM after unbind/bind", function() { var view = rivets.bind(fragment, model); - Should(fragment.childNodes[1].innerText).be.exactly("0"); + Should(fragment.childNodes[1].textContent).be.exactly("0"); view.unbind(); view.bind(); model.items[0].val = "howdy"; - Should(fragment.childNodes[1].innerText).be.exactly("howdy"); + Should(fragment.childNodes[1].textContent).be.exactly("howdy"); }); it("lets you pop an item", function() { From 48afe7d04f1852306cc67f0e827ac4b25c915095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20Am=C3=A9rico?= Date: Sun, 17 Jan 2016 20:02:41 -0300 Subject: [PATCH 21/43] Don't use view.els.forEach to avoid exception when jquery is used --- src/view.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/view.js b/src/view.js index d2a00e939..9c2ee1550 100644 --- a/src/view.js +++ b/src/view.js @@ -117,7 +117,10 @@ export default class View { } } - this.els.forEach(parse) + let elements = this.els, i, len; + for (i = 0, len = elements.length; i < len; i++) { + parse(elements[i]) + } this.bindings.sort((a, b) => { let aPriority = defined(a.binder) ? (a.binder.priority || 0) : 0 From 08cc3ee9dca5090957bcd990e27d461d6510862d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Cazeaux Date: Mon, 1 Feb 2016 20:51:52 +0100 Subject: [PATCH 22/43] Report regexp to split formatters. Fixed #432 --- spec/rivets/binding.js | 26 ++++++++++++++++++++++++++ src/view.js | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/spec/rivets/binding.js b/spec/rivets/binding.js index e433e5bcf..2b70f010e 100644 --- a/spec/rivets/binding.js +++ b/spec/rivets/binding.js @@ -17,6 +17,32 @@ describe('Rivets.Binding', function() { binding.binder.routine.should.equal(rivets.binders.text) }) + describe('with formatters', function() { + it('register all formatters', function() { + + valueInput = document.createElement('input') + valueInput.setAttribute('type','text') + valueInput.setAttribute('data-value', "obj.name | awesome | radical | totally") + + view = rivets.bind(valueInput, {obj: { name: 'nothing' }}) + binding = view.bindings[0] + + binding.formatters.should.be.eql(['awesome', 'radical', 'totally']) + }) + + it('allows arguments with pipes', function() { + + valueInput = document.createElement('input') + valueInput.setAttribute('type','text') + valueInput.setAttribute('data-value', "obj.name | awesome | totally 'arg | with || pipes'") + + view = rivets.bind(valueInput, {obj: { name: 'nothing' }}) + binding = view.bindings[0] + + binding.formatters.should.be.eql(['awesome', "totally 'arg | with || pipes'"]) + }) + }) + describe('bind()', function() { it('subscribes to the model for changes via the adapter', function() { sinon.spy(adapter, 'observe') diff --git a/src/view.js b/src/view.js index d2a00e939..b499ef412 100644 --- a/src/view.js +++ b/src/view.js @@ -60,7 +60,7 @@ export default class View { } buildBinding(binding, node, type, declaration) { - let pipes = declaration.split('|').map(pipe => { + let pipes = declaration.match(/((?:'[^']*')*(?:(?:[^\|']+(?:'[^']*')*[^\|']*)+|[^\|]+))|^$/g).map(pipe => { return pipe.trim() }) From 0663decbaa7b0079cdd6f1b3407c02b6e763b4cb Mon Sep 17 00:00:00 2001 From: Jean-Christophe Cazeaux Date: Mon, 1 Feb 2016 21:09:35 +0100 Subject: [PATCH 23/43] Missed a test in the reports --- spec/rivets/binding.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spec/rivets/binding.js b/spec/rivets/binding.js index 2b70f010e..17e25a5ac 100644 --- a/spec/rivets/binding.js +++ b/spec/rivets/binding.js @@ -299,6 +299,21 @@ describe('Rivets.Binding', function() { binding.formattedValue('jacket').should.equal('super awesome jacket') }) }) + + describe('with a formatter string with pipes in argument', function() { + beforeEach(function () { + + view.formatters.totally = function (value, prefix) { + return prefix + ' totally ' + value + } + + binding.formatters.push("totally 'arg | with || pipes'") + }) + + it('applies the formatter with arguments with pipes', function () { + binding.formattedValue('jacket').should.equal('arg | with || pipes totally jacket') + }) + }) }) describe('getValue()', function() { From 6328793a08390dea0a54148634d51d827cfaea35 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Cazeaux Date: Mon, 1 Feb 2016 22:29:16 +0100 Subject: [PATCH 24/43] Report for 'Constant string in component HTML attributes #478'. New tests for components reveal an error in forEach on attributes: attributes is not an array. An error was revealed too in View import : cyclic dependency between view and binding; --- spec/rivets/component_binding.js | 69 ++++++++++++++++++++++++++++++++ spec/runner.html | 1 + src/bindings.js | 14 ++++--- 3 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 spec/rivets/component_binding.js diff --git a/spec/rivets/component_binding.js b/spec/rivets/component_binding.js new file mode 100644 index 000000000..f441f3e19 --- /dev/null +++ b/spec/rivets/component_binding.js @@ -0,0 +1,69 @@ +describe('Component binding', function() { + var scope, element, component, componentRoot + + beforeEach(function() { + element = document.createElement('div') + element.innerHTML = '' + componentRoot = element.firstChild + scope = { name: 'Rivets' } + component = rivets.components.test = { + initialize: sinon.stub().returns(scope), + template: function() {} + } + }) + + it('renders "template" as a string', function() { + rivets.components.test.template = function() {return '

test

'} + rivets.bind(element) + + componentRoot.innerHTML.should.equal(component.template()) + }) + + describe('initialize()', function() { + var locals + + beforeEach(function() { + locals = { object: { name: 'Rivets locals' } } + componentRoot.setAttribute('item', 'object') + }) + + it('receives element as first argument and attributes as second', function() { + rivets.bind(element, locals) + + component.initialize.calledWith(componentRoot, { item: locals.object }).should.be.true + }) + + it('receives primitives attributes', function() { + componentRoot.setAttribute('primitivestring', "'value'") + componentRoot.setAttribute('primitivenumber', "42") + componentRoot.setAttribute('primitiveboolean', "true") + rivets.bind(element, locals) + + component.initialize.calledWith(componentRoot, { item: locals.object, + primitivestring: 'value', + primitivenumber: 42, + primitiveboolean: true }) + .should.be.true + }) + + it('returns attributes assigned to "static" property as they are', function() { + var type = 'text' + + component.static = ['type'] + componentRoot.setAttribute('type', type) + rivets.bind(element, locals) + + component.initialize.calledWith(componentRoot, { item: locals.object, type: type }).should.be.true + }) + }) + + describe('when "template" is a function', function() { + it('renders returned string as component template', function() { + component.template = sinon.stub().returns('

{ name }

') + rivets.bind(element) + + componentRoot.innerHTML.should.equal('

' + scope.name + '

') + }) + }) + +}) \ No newline at end of file diff --git a/spec/runner.html b/spec/runner.html index 0a9a1ae8a..61b1b0eba 100644 --- a/spec/runner.html +++ b/spec/runner.html @@ -22,6 +22,7 @@ + diff --git a/src/bindings.js b/src/bindings.js index ee0f9eb72..3360aaa1e 100644 --- a/src/bindings.js +++ b/src/bindings.js @@ -278,18 +278,23 @@ export class ComponentBinding extends Binding { let bindingRegExp = view.bindingRegExp() if (this.el.attributes) { - this.el.attributes.forEach(attribute => { + let attribute = null + for (let i = 0 ; i < this.el.attributes.length ; i++) { + attribute = this.el.attributes[i] if (!bindingRegExp.test(attribute.name)) { let propertyName = this.camelCase(attribute.name) let stat = this.component.static - + // Parse attribute value to check type (primitive, keypath...) + let token = parseType(attribute.value) if (stat && stat.indexOf(propertyName) > -1) { this.static[propertyName] = attribute.value + } else if (token.type === 0){ + this.static[propertyName] = token.value } else { this.observers[propertyName] = attribute.value } } - }) + } } } @@ -379,8 +384,7 @@ export class ComponentBinding extends Binding { } }) - this.componentView = new View(this.el, scope, options) - this.componentView.bind() + this.componentView = rivets.bind(this.el, scope, options) Object.keys(this.observers).forEach(key => { let observer = this.observers[key] From bed4510d79cac7175b8d5d76c5b5c2240f2dcb89 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Cazeaux Date: Wed, 3 Feb 2016 21:13:46 +0100 Subject: [PATCH 25/43] Fat arrow function simplification --- src/view.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/view.js b/src/view.js index b499ef412..dd475992e 100644 --- a/src/view.js +++ b/src/view.js @@ -60,13 +60,9 @@ export default class View { } buildBinding(binding, node, type, declaration) { - let pipes = declaration.match(/((?:'[^']*')*(?:(?:[^\|']+(?:'[^']*')*[^\|']*)+|[^\|]+))|^$/g).map(pipe => { - return pipe.trim() - }) + let pipes = declaration.match(/((?:'[^']*')*(?:(?:[^\|']+(?:'[^']*')*[^\|']*)+|[^\|]+))|^$/g).map(pipe => pipe.trim()) - let context = pipes.shift().split('<').map(ctx => { - return ctx.trim() - }) + let context = pipes.shift().split('<').map(ctx => ctx.trim()) let keypath = context.shift() let dependencies = context.shift() From 06fab8d2fc530c8a28e69bcb216898470c9c00ed Mon Sep 17 00:00:00 2001 From: Jean-Christophe Cazeaux Date: Wed, 3 Feb 2016 22:10:51 +0100 Subject: [PATCH 26/43] Report on ES6 branch : generated index for nested rv-each. Docs included --- docs/docs/guide/_sections/iteration/index.md | 17 +++++++++++ .../docs/guide/_sections/usage/configuring.md | 5 ++++ spec/rivets/binders.js | 28 +++++++++++++++++++ spec/rivets/functional.js | 20 +++++++++++++ src/binders.js | 1 + src/rivets.js | 6 ++++ 6 files changed, 77 insertions(+) create mode 100644 docs/docs/guide/_sections/iteration/index.md diff --git a/docs/docs/guide/_sections/iteration/index.md b/docs/docs/guide/_sections/iteration/index.md new file mode 100644 index 000000000..e6c2b7091 --- /dev/null +++ b/docs/docs/guide/_sections/iteration/index.md @@ -0,0 +1,17 @@ +To access the index of the current iteration use the syntax `%item%`, Where `item` is the name of the model you provided in `rv-each-[item]`. You can also access the index of the iteration by using the key `index` but using this will access only the current iterations index. Note that when nesting `rv-each`'s the parent index is still accessible within the scope via the model name. + +```html +
    +
  • + User Index : { %user% } + +
      +
    • + Comment Index : { %comment% } + + User Index : { %user% } +
    • +
    +
  • +
      +``` \ No newline at end of file diff --git a/docs/docs/guide/_sections/usage/configuring.md b/docs/docs/guide/_sections/usage/configuring.md index d0d2a3392..ddfa0fd60 100644 --- a/docs/docs/guide/_sections/usage/configuring.md +++ b/docs/docs/guide/_sections/usage/configuring.md @@ -15,6 +15,11 @@ rivets.configure({ // Template delimiters for text bindings templateDelimiters: ['{', '}'], + // Alias for index in rv-each binder + iterationAlias : function(modelName) { + return '%' + modelName + '%'; + }, + // Augment the event handler of the on-* binder handler: function(target, event, binding) { this.call(target, event, binding.view.models) diff --git a/spec/rivets/binders.js b/spec/rivets/binders.js index 2150aa46c..4a7750296 100644 --- a/spec/rivets/binders.js +++ b/spec/rivets/binders.js @@ -112,6 +112,34 @@ describe("Rivets.binders", function() { }); }); + describe("nested-each-*", function() { + var fragment; + var el; + var nestedEl; + var model; + + beforeEach(function() { + fragment = document.createDocumentFragment(); + el = document.createElement("span"); + el.setAttribute("rv-each-item", "items"); + nestedEl = document.createElement("span"); + nestedEl.setAttribute("rv-each-nested", "item.val"); + nestedEl.innerText = "{%item%}-{%nested%}"; + el.appendChild(nestedEl); + fragment.appendChild(el); + + model = { items: [{val: [{val: 0},{val: 1}]},{val: [{val: 2},{val: 3}]},{val: [{val: 4},{val: 5}]}] }; + }); + + it("lets you get all the indexes", function() { + var view = rivets.bind(el, model); + + Should(fragment.childNodes[1].childNodes[1].innerText).be.exactly('0-0'); + Should(fragment.childNodes[1].childNodes[2].innerText).be.exactly('0-1'); + Should(fragment.childNodes[2].childNodes[2].innerText).be.exactly('1-1'); + }); + }); + describe("if", function() { var fragment; var el; diff --git a/spec/rivets/functional.js b/spec/rivets/functional.js index f6d585096..fca2d95d6 100644 --- a/spec/rivets/functional.js +++ b/spec/rivets/functional.js @@ -231,6 +231,26 @@ describe('Functional', function() { el.getElementsByTagName('li')[0].getAttribute('index').should.equal('0') el.getElementsByTagName('li')[1].getAttribute('index').should.equal('1') }) + + it('should allow binding to the iterated element index by using the %item% syntax', function() { + listItem.setAttribute('data-index', '%item%') + rivets.bind(el, bindData) + el.getElementsByTagName('li')[0].getAttribute('index').should.equal('0') + el.getElementsByTagName('li')[1].getAttribute('index').should.equal('1') + }) + + it('should allow the developer to configure the index attribute available in the iteration', function() { + rivets.configure({ + iterationAlias : function(modelName) { + return modelName + 'Index'; + } + }); + + listItem.setAttribute('data-index', 'itemIndex') + rivets.bind(el, bindData) + el.getElementsByTagName('li')[0].getAttribute('index').should.equal('0') + el.getElementsByTagName('li')[1].getAttribute('index').should.equal('1') + }) }) }) diff --git a/src/binders.js b/src/binders.js index 509501b8e..fdd93a2be 100644 --- a/src/binders.js +++ b/src/binders.js @@ -280,6 +280,7 @@ const binders = { collection.forEach((model, index) => { let data = {index: index} + data[rivets.iterationAlias(modelName)] = index data[modelName] = model if (!defined(this.iterated[index])) { diff --git a/src/rivets.js b/src/rivets.js index c55215013..57121e68f 100644 --- a/src/rivets.js +++ b/src/rivets.js @@ -25,6 +25,12 @@ const rivets = { // Preload data by default. preloadData: true, + // Alias for index in rv-each binder + iterationAlias : function(modelName) { + return '%' + modelName + '%' + }, + + // Default event handler. handler: function(context, ev, binding) { this.call(context, ev, binding.view.models) From 9381e97d08b8485b94e4723fbbe33ad578764039 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Cazeaux Date: Thu, 4 Feb 2016 22:41:52 +0100 Subject: [PATCH 27/43] Report Fix uncorrect '0' receive by binders if html attribute is not defined (#567) --- spec/rivets/binders.js | 25 +++++++++++++++++++++++++ src/parsers.js | 3 +++ 2 files changed, 28 insertions(+) diff --git a/spec/rivets/binders.js b/spec/rivets/binders.js index 2150aa46c..03960441d 100644 --- a/spec/rivets/binders.js +++ b/spec/rivets/binders.js @@ -184,4 +184,29 @@ describe("Rivets.binders", function() { Should(fragment.childNodes.length).be.exactly(1); }); }); + + describe("Custom binder with no attribute value", function() { + rivets.binders["custom-binder"] = function(el, value) { + el.innerHTML = "received " + value; + }; + beforeEach(function() { + fragment = document.createDocumentFragment(); + el = document.createElement("div"); + + fragment.appendChild(el); + + model = {}; + }); + + it("receives undefined when html attribute is not specified", function() { + el.innerHTML = "
      "; + var view = rivets.bind(fragment, model); + Should(el.children[0].innerHTML).be.exactly('received undefined'); + }); + it("receives undefined when html attribute is not specified", function() { + el.innerHTML = "
      "; + var view = rivets.bind(fragment, model); + Should(el.children[0].innerHTML).be.exactly('received undefined'); + }); + }); }); diff --git a/src/parsers.js b/src/parsers.js index 8146c8b6c..4a29cbb2f 100644 --- a/src/parsers.js +++ b/src/parsers.js @@ -18,6 +18,9 @@ export function parseType(string) { value = null } else if (string === 'undefined') { value = undefined + } else if (string === '') { + // string==='' means the Html attribute is empty, binder should receive undefined + value = undefined } else if (isNaN(Number(string)) === false) { value = Number(string) } else { From 1f45a58af0e959568f5c1d7d028d501562fa0ad2 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Cazeaux Date: Fri, 5 Feb 2016 20:44:45 +0100 Subject: [PATCH 28/43] Code rework --- spec/rivets/binders.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/spec/rivets/binders.js b/spec/rivets/binders.js index 03960441d..e0abc9208 100644 --- a/spec/rivets/binders.js +++ b/spec/rivets/binders.js @@ -190,22 +190,18 @@ describe("Rivets.binders", function() { el.innerHTML = "received " + value; }; beforeEach(function() { - fragment = document.createDocumentFragment(); el = document.createElement("div"); - - fragment.appendChild(el); - - model = {}; }); it("receives undefined when html attribute is not specified", function() { el.innerHTML = "
      "; - var view = rivets.bind(fragment, model); + var view = rivets.bind(el); Should(el.children[0].innerHTML).be.exactly('received undefined'); }); + it("receives undefined when html attribute is not specified", function() { el.innerHTML = "
      "; - var view = rivets.bind(fragment, model); + var view = rivets.bind(el); Should(el.children[0].innerHTML).be.exactly('received undefined'); }); }); From b5cf6c1b8e6e8862be6e9df920f0f915f1a740cf Mon Sep 17 00:00:00 2001 From: Jean-Christophe Cazeaux Date: Sun, 7 Feb 2016 14:30:12 +0100 Subject: [PATCH 29/43] Fix camelcase of dashed attributes in components. #574 --- spec/rivets/component_binding.js | 10 ++++++++++ src/bindings.js | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/spec/rivets/component_binding.js b/spec/rivets/component_binding.js index f441f3e19..ca4c95501 100644 --- a/spec/rivets/component_binding.js +++ b/spec/rivets/component_binding.js @@ -55,6 +55,16 @@ describe('Component binding', function() { component.initialize.calledWith(componentRoot, { item: locals.object, type: type }).should.be.true }) + + it('camelcases dashed attributes', function() { + var myNiceAttribute = 'text' + + component.static = ['myNiceAttribute'] + componentRoot.setAttribute('my-nice-attribute', myNiceAttribute) + rivets.bind(element, locals) + + component.initialize.calledWith(componentRoot, { item: locals.object, myNiceAttribute: myNiceAttribute }).should.be.true + }) }) describe('when "template" is a function', function() { diff --git a/src/bindings.js b/src/bindings.js index 3360aaa1e..8803c9367 100644 --- a/src/bindings.js +++ b/src/bindings.js @@ -330,7 +330,7 @@ export class ComponentBinding extends Binding { // element's attribute name into a property name for the component's scope. camelCase(string) { return string.replace(/-([a-z])/g, grouped => { - grouped[1].toUpperCase() + return grouped[1].toUpperCase() }) } From dd7ac1f168cca95246536917d1ffc34a4d8ac3fa Mon Sep 17 00:00:00 2001 From: Jean-Christophe Cazeaux Date: Mon, 8 Feb 2016 22:17:59 +0100 Subject: [PATCH 30/43] Report PR #546 on ES6 branch --- src/adapter.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/adapter.js b/src/adapter.js index 4254a3c86..084d9f2ae 100644 --- a/src/adapter.js +++ b/src/adapter.js @@ -144,10 +144,8 @@ const adapter = { let callbacks = map.callbacks if (callbacks[keypath]) { - callbacks[keypath].slice().forEach(callback => { - if (callbacks[keypath].indexOf(callback) > -1) { - callback() - } + callbacks[keypath].forEach(cb => { + cb() }) } From 2ac7e8b2c887c6af108f6254dc0ca97bfa39c16d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Cazeaux Date: Mon, 18 Apr 2016 16:06:43 +0200 Subject: [PATCH 31/43] Report for PR #612 Set bound to false when unbinding 'if' binder --- spec/rivets/binders.js | 18 ++++++++++++++++++ src/binders.js | 1 + 2 files changed, 19 insertions(+) diff --git a/spec/rivets/binders.js b/spec/rivets/binders.js index 2150aa46c..a27d216d9 100644 --- a/spec/rivets/binders.js +++ b/spec/rivets/binders.js @@ -183,5 +183,23 @@ describe("Rivets.binders", function() { // 1 for the comment placeholder Should(fragment.childNodes.length).be.exactly(1); }); + + it("rebindes nested if", function() { + var nestedEl = document.createElement("div") + nestedEl.setAttribute("rv-if", "data.showNested"); + nestedEl.innerHTML = "{ data.countNested }"; + el.appendChild(nestedEl); + + var view = rivets.bind(fragment, model); + + model.data.countNested = "1"; + model.data.showNested = true; + Should(nestedEl.innerHTML).be.exactly("1"); + model.data.show = false; + model.data.show = true; + model.data.countNested = "42"; + + Should(nestedEl.innerHTML).be.exactly("42"); + }); }); }); diff --git a/src/binders.js b/src/binders.js index 509501b8e..0c4753457 100644 --- a/src/binders.js +++ b/src/binders.js @@ -158,6 +158,7 @@ const binders = { unbind: function() { if (defined(this.nested)) { this.nested.unbind() + this.bound = false } }, From 1b4c72c7b2b42890dd994d0c016e825608b25898 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 31 May 2016 12:21:48 +0100 Subject: [PATCH 32/43] Updated babel for test build + included required packages for testing. Should Fix #538 --- package.json | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 2dfa4ac4b..d0e51c4c8 100644 --- a/package.json +++ b/package.json @@ -19,17 +19,20 @@ "sightglass": "~0.2.4" }, "scripts": { - "pretest": "babel src/parsers.js --out-file spec/lib/parsers.js --modules umd", + "pretest": "babel src/parsers.js --out-file spec/lib/parsers.js --presets es2015 --plugins transform-es2015-modules-umd", "test": "mocha-phantomjs spec/runner.html", "build": "webpack" }, "devDependencies": { - "babel-core": "^4.7.16", - "babel-loader": "^4.2.0", + "minimist": "~1.1.0", "mocha": "~1.20.1", "should": "~4.0.4", "sinon": "~1.10.2", - "webpack": "^1.7.3" + "webpack": "^1.7.3", + "babel-cli": "^6.0.0", + "mocha-phantomjs": "3.5.3", + "babel-preset-es2015": "6.9.0", + "babel-plugin-transform-es2015-modules-umd" : "6.8.0" } } From 84fead1476fe94294059163076d47b10ed37f582 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 31 May 2016 14:23:14 +0100 Subject: [PATCH 33/43] Fixed binding to allow multiple strings, ES6 version of #624 Fixes #620 --- spec/rivets/binding.js | 4 ++-- src/view.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/rivets/binding.js b/spec/rivets/binding.js index 17e25a5ac..4cb196783 100644 --- a/spec/rivets/binding.js +++ b/spec/rivets/binding.js @@ -34,12 +34,12 @@ describe('Rivets.Binding', function() { valueInput = document.createElement('input') valueInput.setAttribute('type','text') - valueInput.setAttribute('data-value', "obj.name | awesome | totally 'arg | with || pipes'") + valueInput.setAttribute('data-value', "obj.name | awesome | totally 'arg | with || pipes' 'arg2' 'arg3' | another 'arg|' 'is|' 'ok'") view = rivets.bind(valueInput, {obj: { name: 'nothing' }}) binding = view.bindings[0] - binding.formatters.should.be.eql(['awesome', "totally 'arg | with || pipes'"]) + binding.formatters.should.be.eql(['awesome', "totally 'arg | with || pipes' 'arg2' 'arg3'", "another 'arg|' 'is|' 'ok'"]) }) }) diff --git a/src/view.js b/src/view.js index 801355992..a23cf9144 100644 --- a/src/view.js +++ b/src/view.js @@ -60,7 +60,7 @@ export default class View { } buildBinding(binding, node, type, declaration) { - let pipes = declaration.match(/((?:'[^']*')*(?:(?:[^\|']+(?:'[^']*')*[^\|']*)+|[^\|]+))|^$/g).map(pipe => pipe.trim()) + let pipes = declaration.match(/((?:'[^']*')*(?:(?:[^\|']*(?:'[^']*')+[^\|']*)+|[^\|]+))|^$/g).map(pipe => pipe.trim()) let context = pipes.shift().split('<').map(ctx => ctx.trim()) From 90ded8126ee1f7b55c3a5f43034481148c44b43d Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 31 May 2016 14:36:01 +0100 Subject: [PATCH 34/43] testing Travis CI --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..33d4baf04 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,2 @@ +language: node_js +node_js: "5" \ No newline at end of file From 6e6d5274502afa58217cfd6a002eff5ca8773025 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 31 May 2016 17:12:28 +0100 Subject: [PATCH 35/43] get ES6 branch working with Travis CI --- .travis.yml | 5 ++++- npm-shrinkwrap.json | 13 +++++++++++++ package.json | 10 +++++++--- src/export.js | 2 +- 4 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 npm-shrinkwrap.json diff --git a/.travis.yml b/.travis.yml index 33d4baf04..3a7337e08 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,2 +1,5 @@ language: node_js -node_js: "5" \ No newline at end of file +node_js: "5" + +before_script: + - webpack \ No newline at end of file diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json new file mode 100644 index 000000000..b713cbdc0 --- /dev/null +++ b/npm-shrinkwrap.json @@ -0,0 +1,13 @@ +{ + "dependencies": { + "mocha-phantomjs": { + "version":"4.0.2", + "dependencies": { + "phantomjs": { + "version": "~2.1.7", + "from": "phantomjs@1.9.7-15" + } + } + } + } +} diff --git a/package.json b/package.json index d0e51c4c8..abf46eddb 100644 --- a/package.json +++ b/package.json @@ -26,13 +26,17 @@ "devDependencies": { "minimist": "~1.1.0", - "mocha": "~1.20.1", + "mocha": "2.5.3", "should": "~4.0.4", "sinon": "~1.10.2", "webpack": "^1.7.3", "babel-cli": "^6.0.0", - "mocha-phantomjs": "3.5.3", + "mocha-phantomjs": "4.0.2", + "babel-loader" : "6.2.4", + "commander": "^2.8.1", + "mocha-phantomjs-core" : "1.3.1", "babel-preset-es2015": "6.9.0", - "babel-plugin-transform-es2015-modules-umd" : "6.8.0" + "babel-plugin-transform-es2015-modules-umd": "6.8.0", + "phantomjs": "2.1.7" } } diff --git a/src/export.js b/src/export.js index 1f870c7a5..0487433ef 100644 --- a/src/export.js +++ b/src/export.js @@ -25,7 +25,7 @@ const factory = sightglass => { el = document.createElement('div') } - let component = rivets.components[component] + component = rivets.components[component] el.innerHTML = component.template.call(rivets, el) let scope = component.initialize.call(rivets, el, data) From fbda5ade8de0fddea367ed659580c958a10d179f Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 1 Jun 2016 09:42:01 +0100 Subject: [PATCH 36/43] Updated webpack version (and fixed associated invalid syntax / errors) --- dist/rivets.bundled.js | 2579 ++++++++++++++++++++++------------------ package.json | 6 +- spec/lib/parsers.js | 10 +- src/binders.js | 4 +- src/bindings.js | 82 +- src/export.js | 4 +- webpack.config.js | 9 +- 7 files changed, 1469 insertions(+), 1225 deletions(-) diff --git a/dist/rivets.bundled.js b/dist/rivets.bundled.js index 5151f76c7..eba1edf26 100644 --- a/dist/rivets.bundled.js +++ b/dist/rivets.bundled.js @@ -2,7 +2,7 @@ if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) - define(factory); + define([], factory); else if(typeof exports === 'object') exports["rivets"] = factory(); else @@ -54,461 +54,562 @@ return /******/ (function(modules) { // webpackBootstrap /* 0 */ /***/ function(module, exports, __webpack_require__) { - "use strict"; + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, exports, __webpack_require__(1), __webpack_require__(2), __webpack_require__(4), __webpack_require__(8), __webpack_require__(9)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports !== "undefined") { + factory(module, exports, require('sightglass'), require('./rivets'), require('./view'), require('./adapter'), require('./binders')); + } else { + var mod = { + exports: {} + }; + factory(mod, mod.exports, global.sightglass, global.rivets, global.view, global.adapter, global.binders); + global._export = mod.exports; + } + })(this, function (module, exports, _sightglass, _rivets, _view, _adapter, _binders) { + 'use strict'; - var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; + Object.defineProperty(exports, "__esModule", { + value: true + }); - var sightglass = _interopRequire(__webpack_require__(1)); + var _sightglass2 = _interopRequireDefault(_sightglass); - var rivets = _interopRequire(__webpack_require__(2)); + var _rivets2 = _interopRequireDefault(_rivets); - var View = _interopRequire(__webpack_require__(4)); + var _view2 = _interopRequireDefault(_view); - var adapter = _interopRequire(__webpack_require__(8)); + var _adapter2 = _interopRequireDefault(_adapter); - var binders = _interopRequire(__webpack_require__(9)); + var _binders2 = _interopRequireDefault(_binders); - // Module factory. Integrates sightglass and public API methods. Returns the - // public interface. - var factory = function (sightglass) { - rivets.sightglass = sightglass; - rivets.binders = binders; - rivets.adapters["."] = adapter; + function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + default: obj + }; + } - // Binds some data to a template / element. Retuddrns a Rivets.View instance. - rivets.bind = function (el) { - var models = arguments[1] === undefined ? {} : arguments[1]; - var options = arguments[2] === undefined ? {} : arguments[2]; + // Module factory. Integrates sightglass and public API methods. Returns the + // public interface. + var factory = function factory(sightglass) { + _rivets2.default.sightglass = sightglass; + _rivets2.default.binders = _binders2.default; + _rivets2.default.adapters['.'] = _adapter2.default; + + // Binds some data to a template / element. Retuddrns a Rivets.View instance. + _rivets2.default.bind = function (el) { + var models = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; + + var view = new _view2.default(el, models, options); + view.bind(); + return view; + }; - var view = new View(el, models, options); - view.bind(); - return view; - }; + // Initializes a new instance of a component on the specified element and + // returns a Rivets.View instance. + _rivets2.default.init = function (_component, el) { + var data = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - // Initializes a new instance of a component on the specified element and - // returns a Rivets.View instance. - rivets.init = function (component, el) { - var data = arguments[2] === undefined ? {} : arguments[2]; + if (!el) { + el = document.createElement('div'); + } - if (!el) { - el = document.createElement("div"); - } + var component = _rivets2.default.components[_component]; + el.innerHTML = component.template.call(_rivets2.default, el); + var scope = component.initialize.call(_rivets2.default, el, data); - var component = rivets.components[component]; - el.innerHTML = component.template.call(rivets, el); - var scope = component.initialize.call(rivets, el, data); + var view = new _view2.default(el, scope); + view.bind(); + return view; + }; - var view = new View(el, scope); - view.bind(); - return view; + return _rivets2.default; }; - return rivets; - }; - - module.exports = factory(sightglass); + exports.default = factory(_sightglass2.default); + module.exports = exports['default']; + }); /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { - var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict"; - - (function () { - // Public sightglass interface. - function sightglass(obj, keypath, callback, options) { - return new Observer(obj, keypath, callback, options); + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports !== "undefined") { + factory(module); + } else { + var mod = { + exports: {} + }; + factory(mod); + global.index = mod.exports; } + })(this, function (module) { + 'use strict'; - // Batteries not included. - sightglass.adapters = {}; - - // Constructs a new keypath observer and kicks things off. - function Observer(obj, keypath, callback, options) { - this.options = options || {}; - this.options.adapters = this.options.adapters || {}; - this.obj = obj; - this.keypath = keypath; - this.callback = callback; - this.objectPath = []; - this.parse(); - - if (isObject(this.target = this.realize())) { - this.set(true, this.key, this.target, this.callback); + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; + } : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; + }; + + (function () { + // Public sightglass interface. + function sightglass(obj, keypath, callback, options) { + return new Observer(obj, keypath, callback, options); } - } - // Tokenizes the provided keypath string into interface + path tokens for the - // observer to work with. - Observer.tokenize = function (keypath, interfaces, root) { - var tokens = []; - var current = { i: root, path: "" }; - var index, chr; + // Batteries not included. + sightglass.adapters = {}; - for (index = 0; index < keypath.length; index++) { - chr = keypath.charAt(index); + // Constructs a new keypath observer and kicks things off. + function Observer(obj, keypath, callback, options) { + this.options = options || {}; + this.options.adapters = this.options.adapters || {}; + this.obj = obj; + this.keypath = keypath; + this.callback = callback; + this.objectPath = []; + this.update = this.update.bind(this); + this.parse(); - if (!! ~interfaces.indexOf(chr)) { - tokens.push(current); - current = { i: chr, path: "" }; - } else { - current.path += chr; + if (isObject(this.target = this.realize())) { + this.set(true, this.key, this.target, this.callback); } } - tokens.push(current); - return tokens; - }; + // Tokenizes the provided keypath string into interface + path tokens for the + // observer to work with. + Observer.tokenize = function (keypath, interfaces, root) { + var tokens = []; + var current = { i: root, path: '' }; + var index, chr; - // Parses the keypath using the interfaces defined on the view. Sets variables - // for the tokenized keypath as well as the end key. - Observer.prototype.parse = function () { - var interfaces = this.interfaces(); - var root, path; + for (index = 0; index < keypath.length; index++) { + chr = keypath.charAt(index); - if (!interfaces.length) { - error("Must define at least one adapter interface."); - } + if (!! ~interfaces.indexOf(chr)) { + tokens.push(current); + current = { i: chr, path: '' }; + } else { + current.path += chr; + } + } - if (!! ~interfaces.indexOf(this.keypath[0])) { - root = this.keypath[0]; - path = this.keypath.substr(1); - } else { - if (typeof (root = this.options.root || sightglass.root) === "undefined") { - error("Must define a default root adapter."); + tokens.push(current); + return tokens; + }; + + // Parses the keypath using the interfaces defined on the view. Sets variables + // for the tokenized keypath as well as the end key. + Observer.prototype.parse = function () { + var interfaces = this.interfaces(); + var root, path; + + if (!interfaces.length) { + error('Must define at least one adapter interface.'); } - path = this.keypath; - } + if (!! ~interfaces.indexOf(this.keypath[0])) { + root = this.keypath[0]; + path = this.keypath.substr(1); + } else { + if (typeof (root = this.options.root || sightglass.root) === 'undefined') { + error('Must define a default root adapter.'); + } - this.tokens = Observer.tokenize(path, interfaces, root); - this.key = this.tokens.pop(); - }; + path = this.keypath; + } - // Realizes the full keypath, attaching observers for every key and correcting - // old observers to any changed objects in the keypath. - Observer.prototype.realize = function () { - var current = this.obj; - var unreached = false; - var prev; - - this.tokens.forEach(function (token, index) { - if (isObject(current)) { - if (typeof this.objectPath[index] !== "undefined") { - if (current !== (prev = this.objectPath[index])) { - this.set(false, token, prev, this.update.bind(this)); - this.set(true, token, current, this.update.bind(this)); + this.tokens = Observer.tokenize(path, interfaces, root); + this.key = this.tokens.pop(); + }; + + // Realizes the full keypath, attaching observers for every key and correcting + // old observers to any changed objects in the keypath. + Observer.prototype.realize = function () { + var current = this.obj; + var unreached = false; + var prev; + + this.tokens.forEach(function (token, index) { + if (isObject(current)) { + if (typeof this.objectPath[index] !== 'undefined') { + if (current !== (prev = this.objectPath[index])) { + this.set(false, token, prev, this.update); + this.set(true, token, current, this.update); + this.objectPath[index] = current; + } + } else { + this.set(true, token, current, this.update); this.objectPath[index] = current; } + + current = this.get(token, current); } else { - this.set(true, token, current, this.update.bind(this)); - this.objectPath[index] = current; - } + if (unreached === false) { + unreached = index; + } - current = this.get(token, current); - } else { - if (unreached === false) { - unreached = index; + if (prev = this.objectPath[index]) { + this.set(false, token, prev, this.update); + } } + }, this); - if (prev = this.objectPath[index]) { - this.set(false, token, prev, this.update.bind(this)); - } + if (unreached !== false) { + this.objectPath.splice(unreached); } - }, this); - if (unreached !== false) { - this.objectPath.splice(unreached); - } + return current; + }; - return current; - }; + // Updates the keypath. This is called when any intermediary key is changed. + Observer.prototype.update = function () { + var next, oldValue; - // Updates the keypath. This is called when any intermediary key is changed. - Observer.prototype.update = function () { - var next, oldValue; + if ((next = this.realize()) !== this.target) { + if (isObject(this.target)) { + this.set(false, this.key, this.target, this.callback); + } - if ((next = this.realize()) !== this.target) { - if (isObject(this.target)) { - this.set(false, this.key, this.target, this.callback); - } + if (isObject(next)) { + this.set(true, this.key, next, this.callback); + } - if (isObject(next)) { - this.set(true, this.key, next, this.callback); - } + oldValue = this.value(); + this.target = next; - oldValue = this.value(); - this.target = next; + if (this.value() !== oldValue) this.callback(); + } + }; - if (this.value() !== oldValue) this.callback(); - } - }; + // Reads the current end value of the observed keypath. Returns undefined if + // the full keypath is unreachable. + Observer.prototype.value = function () { + if (isObject(this.target)) { + return this.get(this.key, this.target); + } + }; - // Reads the current end value of the observed keypath. Returns undefined if - // the full keypath is unreachable. - Observer.prototype.value = function () { - if (isObject(this.target)) { - return this.get(this.key, this.target); - } - }; + // Sets the current end value of the observed keypath. Calling setValue when + // the full keypath is unreachable is a no-op. + Observer.prototype.setValue = function (value) { + if (isObject(this.target)) { + this.adapter(this.key).set(this.target, this.key.path, value); + } + }; - // Sets the current end value of the observed keypath. Calling setValue when - // the full keypath is unreachable is a no-op. - Observer.prototype.setValue = function (value) { - if (isObject(this.target)) { - this.adapter(this.key).set(this.target, this.key.path, value); - } - }; + // Gets the provided key on an object. + Observer.prototype.get = function (key, obj) { + return this.adapter(key).get(obj, key.path); + }; - // Gets the provided key on an object. - Observer.prototype.get = function (key, obj) { - return this.adapter(key).get(obj, key.path); - }; + // Observes or unobserves a callback on the object using the provided key. + Observer.prototype.set = function (active, key, obj, callback) { + var action = active ? 'observe' : 'unobserve'; + this.adapter(key)[action](obj, key.path, callback); + }; - // Observes or unobserves a callback on the object using the provided key. - Observer.prototype.set = function (active, key, obj, callback) { - var action = active ? "observe" : "unobserve"; - this.adapter(key)[action](obj, key.path, callback); - }; + // Returns an array of all unique adapter interfaces available. + Observer.prototype.interfaces = function () { + var interfaces = Object.keys(this.options.adapters); - // Returns an array of all unique adapter interfaces available. - Observer.prototype.interfaces = function () { - var interfaces = Object.keys(this.options.adapters); + Object.keys(sightglass.adapters).forEach(function (i) { + if (! ~interfaces.indexOf(i)) { + interfaces.push(i); + } + }); - Object.keys(sightglass.adapters).forEach(function (i) { - if (! ~interfaces.indexOf(i)) { - interfaces.push(i); - } - }); + return interfaces; + }; - return interfaces; - }; + // Convenience function to grab the adapter for a specific key. + Observer.prototype.adapter = function (key) { + return this.options.adapters[key.i] || sightglass.adapters[key.i]; + }; - // Convenience function to grab the adapter for a specific key. - Observer.prototype.adapter = function (key) { - return this.options.adapters[key.i] || sightglass.adapters[key.i]; - }; + // Unobserves the entire keypath. + Observer.prototype.unobserve = function () { + var obj; - // Unobserves the entire keypath. - Observer.prototype.unobserve = function () { - var obj; + this.tokens.forEach(function (token, index) { + if (obj = this.objectPath[index]) { + this.set(false, token, obj, this.update); + } + }, this); - this.tokens.forEach(function (token, index) { - if (obj = this.objectPath[index]) { - this.set(false, token, obj, this.update.bind(this)); + if (isObject(this.target)) { + this.set(false, this.key, this.target, this.callback); } - }, this); + }; - if (isObject(this.target)) { - this.set(false, this.key, this.target, this.callback); + // Check if a value is an object than can be observed. + function isObject(obj) { + return (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' && obj !== null; } - }; - - // Check if a value is an object than can be observed. - function isObject(obj) { - return typeof obj === "object" && obj !== null; - } - // Error thrower. - function error(message) { - throw new Error("[sightglass] " + message); - } + // Error thrower. + function error(message) { + throw new Error('[sightglass] ' + message); + } - // Export module for Node and the browser. - if (typeof module !== "undefined" && module.exports) { - module.exports = sightglass; - } else if (true) { - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function () { - return this.sightglass = sightglass; - }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else { - this.sightglass = sightglass; - } - }).call(undefined); + // Export module for Node and the browser. + if (typeof module !== 'undefined' && module.exports) { + module.exports = sightglass; + } else if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function () { + return this.sightglass = sightglass; + }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else { + this.sightglass = sightglass; + } + }).call(undefined); + }); /***/ }, /* 2 */ /***/ function(module, exports, __webpack_require__) { - "use strict"; + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, exports, __webpack_require__(3)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports !== "undefined") { + factory(module, exports, require('./constants')); + } else { + var mod = { + exports: {} + }; + factory(mod, mod.exports, global.constants); + global.rivets = mod.exports; + } + })(this, function (module, exports, _constants) { + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); - var _constants = __webpack_require__(3); - var OPTIONS = _constants.OPTIONS; - var EXTENSIONS = _constants.EXTENSIONS; + var rivets = { + // Global binders. + binders: {}, - var rivets = { - // Global binders. - binders: {}, + // Global components. + components: {}, - // Global components. - components: {}, + // Global formatters. + formatters: {}, - // Global formatters. - formatters: {}, + // Global sightglass adapters. + adapters: {}, - // Global sightglass adapters. - adapters: {}, + // Default attribute prefix. + prefix: 'rv', - // Default attribute prefix. - prefix: "rv", + // Default template delimiters. + templateDelimiters: ['{', '}'], - // Default template delimiters. - templateDelimiters: ["{", "}"], + // Default sightglass root interface. + rootInterface: '.', - // Default sightglass root interface. - rootInterface: ".", + // Preload data by default. + preloadData: true, - // Preload data by default. - preloadData: true, + // Alias for index in rv-each binder + iterationAlias: function iterationAlias(modelName) { + return '%' + modelName + '%'; + }, - // Default event handler. - handler: function handler(context, ev, binding) { - this.call(context, ev, binding.view.models); - }, + // Default event handler. + handler: function handler(context, ev, binding) { + this.call(context, ev, binding.view.models); + }, - // Merges an object literal into the corresponding global options. - configure: function configure() { - var _this = this; + // Merges an object literal into the corresponding global options. + configure: function configure() { + var _this = this; - var options = arguments[0] === undefined ? {} : arguments[0]; + var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - Object.keys(options).forEach(function (option) { - var value = options[option]; + Object.keys(options).forEach(function (option) { + var value = options[option]; - if (EXTENSIONS.indexOf(option) > -1) { - Object.keys(value).forEach(function (key) { - _this[option][key] = value[key]; - }); - } else { - _this[option] = value; - } - }); - } - }; + if (_constants.EXTENSIONS.indexOf(option) > -1) { + Object.keys(value).forEach(function (key) { + _this[option][key] = value[key]; + }); + } else { + _this[option] = value; + } + }); + } + }; - module.exports = rivets; + exports.default = rivets; + module.exports = exports['default']; + }); /***/ }, /* 3 */ -/***/ function(module, exports) { +/***/ function(module, exports, __webpack_require__) { - "use strict"; + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [exports], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports !== "undefined") { + factory(exports); + } else { + var mod = { + exports: {} + }; + factory(mod.exports); + global.constants = mod.exports; + } + })(this, function (exports) { + 'use strict'; - Object.defineProperty(exports, "__esModule", { - value: true - }); - var OPTIONS = ["prefix", "templateDelimiters", "rootInterface", "preloadData", "handler"]; + Object.defineProperty(exports, "__esModule", { + value: true + }); + var OPTIONS = exports.OPTIONS = ['prefix', 'templateDelimiters', 'rootInterface', 'preloadData', 'handler']; - exports.OPTIONS = OPTIONS; - var EXTENSIONS = ["binders", "formatters", "components", "adapters"]; - exports.EXTENSIONS = EXTENSIONS; + var EXTENSIONS = exports.EXTENSIONS = ['binders', 'formatters', 'components', 'adapters']; + }); /***/ }, /* 4 */ /***/ function(module, exports, __webpack_require__) { - "use strict"; - - var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; - - var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, exports, __webpack_require__(2), __webpack_require__(3), __webpack_require__(5), __webpack_require__(6)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports !== "undefined") { + factory(module, exports, require('./rivets'), require('./constants'), require('./bindings'), require('./parsers')); + } else { + var mod = { + exports: {} + }; + factory(mod, mod.exports, global.rivets, global.constants, global.bindings, global.parsers); + global.view = mod.exports; + } + })(this, function (module, exports, _rivets, _constants, _bindings, _parsers) { + 'use strict'; - var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; + Object.defineProperty(exports, "__esModule", { + value: true + }); - var rivets = _interopRequire(__webpack_require__(2)); + var _rivets2 = _interopRequireDefault(_rivets); - var _constants = __webpack_require__(3); + function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + default: obj + }; + } - var OPTIONS = _constants.OPTIONS; - var EXTENSIONS = _constants.EXTENSIONS; + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } - var _bindings = __webpack_require__(5); + var _createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } - var Binding = _bindings.Binding; - var TextBinding = _bindings.TextBinding; - var ComponentBinding = _bindings.ComponentBinding; + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); - var parseTemplate = __webpack_require__(6).parseTemplate; + var defined = function defined(value) { + return value !== undefined && value !== null; + }; - var defined = function (value) { - return value !== undefined && value !== null; - }; + // A collection of bindings built from a set of parent nodes. - // A collection of bindings built from a set of parent nodes. + var View = function () { + // The DOM elements and the model objects for binding are passed into the + // constructor along with any local options that should be used throughout the + // context of the view and it's bindings. - var View = (function () { - // The DOM elements and the model objects for binding are passed into the - // constructor along with any local options that should be used throughout the - // context of the view and it's bindings. + function View(els, models) { + var _this = this; - function View(els, models) { - var _this = this; + var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - var options = arguments[2] === undefined ? {} : arguments[2]; + _classCallCheck(this, View); - _classCallCheck(this, View); + if (els.jquery || els instanceof Array) { + this.els = els; + } else { + this.els = [els]; + } - if (els.jquery || els instanceof Array) { - this.els = els; - } else { - this.els = [els]; - } + this.models = models; - this.models = models; + _constants.EXTENSIONS.forEach(function (extensionType) { + _this[extensionType] = {}; - EXTENSIONS.forEach(function (extensionType) { - _this[extensionType] = {}; + if (options[extensionType]) { + Object.keys(options[extensionType]).forEach(function (key) { + _this[extensionType][key] = options[extensionType][key]; + }); + } - if (options[extensionType]) { - Object.keys(options[extensionType]).forEach(function (key) { - _this[extensionType][key] = options[extensionType][key]; + Object.keys(_rivets2.default[extensionType]).forEach(function (key) { + if (!defined(_this[extensionType][key])) { + _this[extensionType][key] = _rivets2.default[extensionType][key]; + } }); - } - - Object.keys(rivets[extensionType]).forEach(function (key) { - if (!defined(_this[extensionType][key])) { - _this[extensionType][key] = rivets[extensionType][key]; - } }); - }); - OPTIONS.forEach(function (option) { - _this[option] = defined(options[option]) ? options[option] : rivets[option]; - }); + _constants.OPTIONS.forEach(function (option) { + _this[option] = defined(options[option]) ? options[option] : _rivets2.default[option]; + }); - this.build(); - } + this.build(); + } - _createClass(View, { - options: { + _createClass(View, [{ + key: 'options', value: function options() { - var _this = this; + var _this2 = this; var options = {}; - EXTENSIONS.concat(OPTIONS).forEach(function (option) { - options[option] = _this[option]; + _constants.EXTENSIONS.concat(_constants.OPTIONS).forEach(function (option) { + options[option] = _this2[option]; }); return options; } - }, - bindingRegExp: { - - // Regular expression used to match binding attributes. - + }, { + key: 'bindingRegExp', value: function bindingRegExp() { - return new RegExp("^" + this.prefix + "-"); + return new RegExp('^' + this.prefix + '-'); } - }, - buildBinding: { + }, { + key: 'buildBinding', value: function buildBinding(binding, node, type, declaration) { - var pipes = declaration.split("|").map(function (pipe) { + var pipes = declaration.match(/((?:'[^']*')*(?:(?:[^\|']+(?:'[^']*')*[^\|']*)+|[^\|]+))|^$/g).map(function (pipe) { return pipe.trim(); }); - var context = pipes.shift().split("<").map(function (ctx) { + var context = pipes.shift().split('<').map(function (ctx) { return ctx.trim(); }); @@ -522,25 +623,21 @@ return /******/ (function(modules) { // webpackBootstrap this.bindings.push(new binding(this, node, type, keypath, options)); } - }, - build: { - - // Parses the DOM tree and builds `Binding` instances for every matched - // binding declaration. - + }, { + key: 'build', value: function build() { - var _this = this; + var _this3 = this; this.bindings = []; - var parse = function (node) { + var parse = function parse(node) { var block = false; if (node.nodeType === 3) { - var delimiters = _this.templateDelimiters; + var delimiters = _this3.templateDelimiters; if (delimiters) { - var tokens = parseTemplate(node.data, delimiters); + var tokens = (0, _parsers.parseTemplate)(node.data, delimiters); if (tokens.length) { if (!(tokens.length === 1 && tokens[0].type === 0)) { @@ -549,7 +646,7 @@ return /******/ (function(modules) { // webpackBootstrap node.parentNode.insertBefore(text, node); if (token.type === 1) { - _this.buildBinding(TextBinding, text, null, token.value); + _this3.buildBinding(_bindings.TextBinding, text, null, token.value); } }); @@ -558,7 +655,7 @@ return /******/ (function(modules) { // webpackBootstrap } } } else if (node.nodeType === 1) { - block = _this.traverse(node); + block = _this3.traverse(node); } if (!block) { @@ -566,7 +663,12 @@ return /******/ (function(modules) { // webpackBootstrap } }; - this.els.forEach(parse); + var elements = this.els, + i = void 0, + len = void 0; + for (i = 0, len = elements.length; i < len; i++) { + parse(elements[i]); + } this.bindings.sort(function (a, b) { var aPriority = defined(a.binder) ? a.binder.priority || 0 : 0; @@ -574,27 +676,27 @@ return /******/ (function(modules) { // webpackBootstrap return bPriority - aPriority; }); } - }, - traverse: { + }, { + key: 'traverse', value: function traverse(node) { - var _this = this; + var _this4 = this; var bindingRegExp = this.bindingRegExp(); - var block = node.nodeName === "SCRIPT" || node.nodeName === "STYLE"; + var block = node.nodeName === 'SCRIPT' || node.nodeName === 'STYLE'; var attributes = null; Array.prototype.slice.call(node.attributes).forEach(function (attribute) { if (bindingRegExp.test(attribute.name)) { (function () { - var type = attribute.name.replace(bindingRegExp, ""); - var binder = _this.binders[type]; + var type = attribute.name.replace(bindingRegExp, ''); + var binder = _this4.binders[type]; if (!binder) { - Object.keys(_this.binders).forEach(function (identifier) { - var value = _this.binders[identifier]; + Object.keys(_this4.binders).forEach(function (identifier) { + var value = _this4.binders[identifier]; - if (identifier !== "*" && identifier.indexOf("*") > -1) { - var regexp = new RegExp("^" + identifier.replace(/\*/g, ".+") + "$"); + if (identifier !== '*' && identifier.indexOf('*') > -1) { + var regexp = new RegExp('^' + identifier.replace(/\*/g, '.+') + '$'); if (regexp.test(type)) { binder = value; @@ -604,7 +706,7 @@ return /******/ (function(modules) { // webpackBootstrap } if (!defined(binder)) { - binder = _this.binders["*"]; + binder = _this4.binders['*']; } if (binder.block) { @@ -619,8 +721,8 @@ return /******/ (function(modules) { // webpackBootstrap attributes.forEach(function (attribute) { if (bindingRegExp.test(attribute.name)) { - var type = attribute.name.replace(bindingRegExp, ""); - _this.buildBinding(Binding, node, type, attribute.value); + var type = attribute.name.replace(bindingRegExp, ''); + _this4.buildBinding(_bindings.Binding, node, type, attribute.value); } }); @@ -628,56 +730,41 @@ return /******/ (function(modules) { // webpackBootstrap var type = node.nodeName.toLowerCase(); if (this.components[type] && !node._bound) { - this.bindings.push(new ComponentBinding(this, node, type)); + this.bindings.push(new _bindings.ComponentBinding(this, node, type)); block = true; } } return block; } - }, - select: { - - // Returns an array of bindings where the supplied function evaluates to true. - + }, { + key: 'select', value: function select(fn) { return this.bindings.filter(fn); } - }, - bind: { - - // Binds all of the current bindings for this view. - + }, { + key: 'bind', value: function bind() { this.bindings.forEach(function (binding) { binding.bind(); }); } - }, - unbind: { - - // Unbinds all of the current bindings for this view. - + }, { + key: 'unbind', value: function unbind() { this.bindings.forEach(function (binding) { binding.unbind(); }); } - }, - sync: { - - // Syncs up the view with the model by running the routines on all bindings. - + }, { + key: 'sync', value: function sync() { this.bindings.forEach(function (binding) { binding.sync(); }); } - }, - publish: { - - // Publishes the input values from the view back to the model (reverse sync). - + }, { + key: 'publish', value: function publish() { var publishes = this.select(function (binding) { if (defined(binding.binder)) { @@ -689,18 +776,15 @@ return /******/ (function(modules) { // webpackBootstrap binding.publish(); }); } - }, - update: { - - // Updates the view's models along with any affected bindings. - + }, { + key: 'update', value: function update() { - var _this = this; + var _this5 = this; - var models = arguments[0] === undefined ? {} : arguments[0]; + var models = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; Object.keys(models).forEach(function (key) { - _this.models[key] = models[key]; + _this5.models[key] = models[key]; }); this.bindings.forEach(function (binding) { @@ -709,80 +793,170 @@ return /******/ (function(modules) { // webpackBootstrap } }); } - } - }); + }]); - return View; - })(); + return View; + }(); - module.exports = View; + exports.default = View; + module.exports = exports['default']; + }); /***/ }, /* 5 */ /***/ function(module, exports, __webpack_require__) { - "use strict"; + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [exports, __webpack_require__(2), __webpack_require__(6), __webpack_require__(7), __webpack_require__(3)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports !== "undefined") { + factory(exports, require('./rivets'), require('./parsers'), require('./util'), require('./constants')); + } else { + var mod = { + exports: {} + }; + factory(mod.exports, global.rivets, global.parsers, global.util, global.constants); + global.bindings = mod.exports; + } + })(this, function (exports, _rivets, _parsers, _util, _constants) { + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.TextBinding = exports.ComponentBinding = exports.Binding = undefined; + + var _rivets2 = _interopRequireDefault(_rivets); - var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; + function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + default: obj + }; + } - var _toConsumableArray = function (arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }; + var _get = function get(object, property, receiver) { + if (object === null) object = Function.prototype; + var desc = Object.getOwnPropertyDescriptor(object, property); - var _get = function get(object, property, receiver) { var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc && desc.writable) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + if (desc === undefined) { + var parent = Object.getPrototypeOf(object); - var _inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }; + if (parent === null) { + return undefined; + } else { + return get(parent, property, receiver); + } + } else if ("value" in desc) { + return desc.value; + } else { + var getter = desc.get; - var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + if (getter === undefined) { + return undefined; + } - var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; + return getter.call(receiver); + } + }; - Object.defineProperty(exports, "__esModule", { - value: true - }); + function _possibleConstructorReturn(self, call) { + if (!self) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } - var rivets = _interopRequire(__webpack_require__(2)); + return call && (typeof call === "object" || typeof call === "function") ? call : self; + } - var parseType = __webpack_require__(6).parseType; + function _inherits(subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); + } - var getInputValue = __webpack_require__(7).getInputValue; + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + enumerable: false, + writable: true, + configurable: true + } + }); + if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; + } - var defined = function (value) { - return value !== undefined && value !== null; - }; + function _toConsumableArray(arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { + arr2[i] = arr[i]; + } - // A single binding between a model attribute and a DOM element. + return arr2; + } else { + return Array.from(arr); + } + } - var Binding = exports.Binding = (function () { - // All information about the binding is passed into the constructor; the - // containing view, the DOM node, the type of binding, the model object and the - // keypath at which to listen for changes. + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } - function Binding(view, el, type, keypath) { - var options = arguments[4] === undefined ? {} : arguments[4]; + var _createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } - _classCallCheck(this, Binding); + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); - this.view = view; - this.el = el; - this.type = type; - this.keypath = keypath; - this.options = options; - this.formatters = options.formatters || []; - this.dependencies = []; - this.formatterObservers = {}; - this.model = undefined; - this.setBinder(); + var defined = function defined(value) { + return value !== undefined && value !== null; + }; - this.bind = this.bind.bind(this); - this.unbind = this.unbind.bind(this); - this.sync = this.sync.bind(this); - this.publish = this.publish.bind(this); - } + // A single binding between a model attribute and a DOM element. + + var Binding = exports.Binding = function () { + // All information about the binding is passed into the constructor; the + // containing view, the DOM node, the type of binding, the model object and the + // keypath at which to listen for changes. + + function Binding(view, el, type, keypath) { + var options = arguments.length <= 4 || arguments[4] === undefined ? {} : arguments[4]; + + _classCallCheck(this, Binding); + + this.view = view; + this.el = el; + this.type = type; + this.keypath = keypath; + this.options = options; + this.formatters = options.formatters || []; + this.dependencies = []; + this.formatterObservers = {}; + this.model = undefined; + this.setBinder(); + + this.bind = this.bind.bind(this); + this.unbind = this.unbind.bind(this); + this.sync = this.sync.bind(this); + this.publish = this.publish.bind(this); + } - _createClass(Binding, { - setBinder: { + // Sets the binder to use when binding and syncing. - // Sets the binder to use when binding and syncing. + _createClass(Binding, [{ + key: 'setBinder', value: function setBinder() { var _this = this; @@ -792,12 +966,12 @@ return /******/ (function(modules) { // webpackBootstrap Object.keys(this.view.binders).forEach(function (identifier) { var value = _this.view.binders[identifier]; - if (identifier !== "*" && identifier.indexOf("*") > -1) { - var regexp = new RegExp("^" + identifier.replace(/\*/g, ".+") + "$"); + if (identifier !== '*' && identifier.indexOf('*') > -1) { + var regexp = new RegExp('^' + identifier.replace(/\*/g, '.+') + '$'); if (regexp.test(_this.type)) { _this.binder = value; - _this.args = new RegExp("^" + identifier.replace(/\*/g, "(.+)") + "$").exec(_this.type); + _this.args = new RegExp('^' + identifier.replace(/\*/g, '(.+)') + '$').exec(_this.type); _this.args.shift(); } } @@ -805,28 +979,28 @@ return /******/ (function(modules) { // webpackBootstrap } if (!defined(this.binder)) { - this.binder = this.view.binders["*"]; + this.binder = this.view.binders['*']; } if (this.binder instanceof Function) { this.binder = { routine: this.binder }; } } - }, - observe: { // Observes the object keypath to run the provided callback. + }, { + key: 'observe', value: function observe(obj, keypath, callback) { - return rivets.sightglass(obj, keypath, callback, { + return _rivets2.default.sightglass(obj, keypath, callback, { root: this.view.rootInterface, adapters: this.view.adapters }); } - }, - parseTarget: { + }, { + key: 'parseTarget', value: function parseTarget() { - var token = parseType(this.keypath); + var token = (0, _parsers.parseType)(this.keypath); if (token.type === 0) { this.value = token.value; @@ -835,36 +1009,36 @@ return /******/ (function(modules) { // webpackBootstrap this.model = this.observer.target; } } - }, - formattedValue: { // Applies all the current formatters to the supplied value and returns the // formatted value. + }, { + key: 'formattedValue', value: function formattedValue(value) { - var _this = this; + var _this2 = this; this.formatters.forEach(function (formatterStr, fi) { var args = formatterStr.match(/[^\s']+|'([^']|'[^\s])*'|"([^"]|"[^\s])*"/g); var id = args.shift(); - var formatter = _this.view.formatters[id]; + var formatter = _this2.view.formatters[id]; var processedArgs = []; - args = args.map(parseType); + args = args.map(_parsers.parseType); args.forEach(function (arg, ai) { if (arg.type === 0) { processedArgs.push(arg.value); } else { - if (!defined(_this.formatterObservers[fi])) { - _this.formatterObservers[fi] = {}; + if (!defined(_this2.formatterObservers[fi])) { + _this2.formatterObservers[fi] = {}; } - var observer = _this.formatterObservers[fi][ai]; + var observer = _this2.formatterObservers[fi][ai]; if (!observer) { - observer = _this.observe(_this.view.models, arg.value, _this.sync); - _this.formatterObservers[fi][ai] = observer; + observer = _this2.observe(_this2.view.models, arg.value, _this2.sync); + _this2.formatterObservers[fi][ai] = observer; } processedArgs.push(observer.value()); @@ -880,11 +1054,11 @@ return /******/ (function(modules) { // webpackBootstrap return value; } - }, - eventHandler: { // Returns an event handler for the binding around the supplied function. + }, { + key: 'eventHandler', value: function eventHandler(fn) { var binding = this; var handler = binding.view.handler; @@ -893,14 +1067,14 @@ return /******/ (function(modules) { // webpackBootstrap handler.call(fn, this, ev, binding); }; } - }, - set: { // Sets the value for the binding. This Basically just runs the binding routine // with the suplied value formatted. + }, { + key: 'set', value: function set(value) { - if (value instanceof Function && !this.binder["function"]) { + if (value instanceof Function && !this.binder.function) { value = this.formattedValue(value.call(this.model)); } else { value = this.formattedValue(value); @@ -910,13 +1084,13 @@ return /******/ (function(modules) { // webpackBootstrap this.binder.routine.call(this, this.el, value); } } - }, - sync: { // Syncs up the view binding with the model. + }, { + key: 'sync', value: function sync() { - var _this = this; + var _this3 = this; if (this.observer) { if (this.model !== this.observer.target) { @@ -929,10 +1103,10 @@ return /******/ (function(modules) { // webpackBootstrap this.dependencies = []; this.model = this.observer.target; - if (defined(model) && deps && deps.length) { + if (defined(this.model) && deps && deps.length) { deps.forEach(function (dependency) { - var observer = _this.observe(_this.model, dependency, _this.sync); - _this.dependencies.push(observer); + var observer = _this3.observe(_this3.model, dependency, _this3.sync); + _this3.dependencies.push(observer); }); } } @@ -942,41 +1116,41 @@ return /******/ (function(modules) { // webpackBootstrap this.set(this.value); } } - }, - publish: { // Publishes the value currently set on the input element back to the model. + }, { + key: 'publish', value: function publish() { - var _this = this; + var _this4 = this; if (this.observer) { (function () { - var value = _this.getValue(_this.el); + var value = _this4.getValue(_this4.el); - _this.formatters.slice(0).reverse().forEach(function (formatter) { + _this4.formatters.slice(0).reverse().forEach(function (formatter) { var args = formatter.split(/\s+/); var id = args.shift(); - var f = _this.view.formatters[id]; + var f = _this4.view.formatters[id]; if (defined(f) && f.publish) { value = f.publish.apply(f, [value].concat(_toConsumableArray(args))); } }); - _this.observer.setValue(value); + _this4.observer.setValue(value); })(); } } - }, - bind: { // Subscribes to the model for changes at the specified keypath. Bi-directional // routines will also listen for changes on the element to propagate them back // to the model. + }, { + key: 'bind', value: function bind() { - var _this = this; + var _this5 = this; this.parseTarget(); @@ -986,8 +1160,8 @@ return /******/ (function(modules) { // webpackBootstrap if (defined(this.model) && defined(this.options.dependencies)) { this.options.dependencies.forEach(function (dependency) { - var observer = _this.observe(_this.model, dependency, _this.sync); - _this.dependencies.push(observer); + var observer = _this5.observe(_this5.model, dependency, _this5.sync); + _this5.dependencies.push(observer); }); } @@ -995,13 +1169,13 @@ return /******/ (function(modules) { // webpackBootstrap this.sync(); } } - }, - unbind: { // Unsubscribes from the model and the element. + }, { + key: 'unbind', value: function unbind() { - var _this = this; + var _this6 = this; if (defined(this.binder.unbind)) { this.binder.unbind.call(this, this.el); @@ -1018,7 +1192,7 @@ return /******/ (function(modules) { // webpackBootstrap this.dependencies = []; Object.keys(this.formatterObservers).forEach(function (fi) { - var args = _this.formatterObservers[fi]; + var args = _this6.formatterObservers[fi]; Object.keys(args).forEach(function (ai) { args[ai].unobserve(); @@ -1027,14 +1201,14 @@ return /******/ (function(modules) { // webpackBootstrap this.formatterObservers = {}; } - }, - update: { // Updates the binding's model from what is currently set on the view. Unbinds // the old model first and then re-binds with the new model. + }, { + key: 'update', value: function update() { - var models = arguments[0] === undefined ? {} : arguments[0]; + var models = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; if (defined(this.observer)) { this.model = this.observer.target; @@ -1044,134 +1218,129 @@ return /******/ (function(modules) { // webpackBootstrap this.binder.update.call(this, models); } } - }, - getValue: { // Returns elements value + }, { + key: 'getValue', value: function getValue(el) { if (this.binder && defined(this.binder.getValue)) { return this.binder.getValue.call(this, el); } else { - return getInputValue(el); + return (0, _util.getInputValue)(el); } } - } - }); + }]); - return Binding; - })(); + return Binding; + }(); - // component view encapsulated as a binding within it's parent view. + var ComponentBinding = exports.ComponentBinding = function (_Binding) { + _inherits(ComponentBinding, _Binding); - var ComponentBinding = exports.ComponentBinding = (function (_Binding) { - // Initializes a component binding for the specified view. The raw component - // element is passed in along with the component type. Attributes and scope - // inflections are determined based on the components defined attributes. + // Initializes a component binding for the specified view. The raw component + // element is passed in along with the component type. Attributes and scope + // inflections are determined based on the components defined attributes. - function ComponentBinding(view, el, type) { - var _this = this; + function ComponentBinding(view, el, type) { + _classCallCheck(this, ComponentBinding); - _classCallCheck(this, ComponentBinding); + var _this7 = _possibleConstructorReturn(this, Object.getPrototypeOf(ComponentBinding).call(this, view, el, type)); - this.view = view; - this.el = el; - this.type = type; - this.component = this.view.components[this.type]; - this["static"] = {}; - this.observers = {}; - this.upstreamObservers = {}; + _this7.component = _this7.view.components[_this7.type]; + _this7.static = {}; + _this7.observers = {}; + _this7.upstreamObservers = {}; - var bindingRegExp = view.bindingRegExp(); + var bindingRegExp = view.bindingRegExp(); - if (this.el.attributes) { - this.el.attributes.forEach(function (attribute) { - if (!bindingRegExp.test(attribute.name)) { - var propertyName = _this.camelCase(attribute.name); - var stat = _this.component["static"]; + if (_this7.el.attributes) { + _this7.el.attributes.forEach(function (attribute) { + if (!bindingRegExp.test(attribute.name)) { + var propertyName = _this7.camelCase(attribute.name); + var stat = _this7.component.static; - if (stat && stat.indexOf(propertyName) > -1) { - _this["static"][propertyName] = attribute.value; - } else { - _this.observers[propertyName] = attribute.value; + if (stat && stat.indexOf(propertyName) > -1) { + _this7.static[propertyName] = attribute.value; + } else { + _this7.observers[propertyName] = attribute.value; + } } - } - }); + }); + } + return _this7; } - } - - _inherits(ComponentBinding, _Binding); - _createClass(ComponentBinding, { - sync: { + // Intercepts `Rivets.Binding::sync` since component bindings are not bound to + // a particular model to update it's value. - // Intercepts `Rivets.Binding::sync` since component bindings are not bound to - // a particular model to update it's value. + _createClass(ComponentBinding, [{ + key: 'sync', value: function sync() {} - }, - update: { // Intercepts `Rivets.Binding::update` since component bindings are not bound // to a particular model to update it's value. + }, { + key: 'update', value: function update() {} - }, - publish: { // Intercepts `Rivets.Binding::publish` since component bindings are not bound // to a particular model to update it's value. + }, { + key: 'publish', value: function publish() {} - }, - locals: { // Returns an object map using the component's scope inflections. + }, { + key: 'locals', value: function locals() { - var _this = this; + var _this8 = this; var result = {}; - Object.keys(this["static"]).forEach(function (key) { - result[key] = _this["static"][key]; + Object.keys(this.static).forEach(function (key) { + result[key] = _this8.static[key]; }); Object.keys(this.observers).forEach(function (key) { - result[key] = _this.observers[key].value(); + result[key] = _this8.observers[key].value(); }); return result; } - }, - camelCase: { // Returns a camel-cased version of the string. Used when translating an // element's attribute name into a property name for the component's scope. + }, { + key: 'camelCase', value: function camelCase(string) { return string.replace(/-([a-z])/g, function (grouped) { grouped[1].toUpperCase(); }); } - }, - bind: { // Intercepts `Rivets.Binding::bind` to build `@componentView` with a localized // map of models from the root view. Bind `@componentView` on subsequent calls. + }, { + key: 'bind', value: function bind() { - var _this = this; + var _this9 = this; if (!this.bound) { Object.keys(this.observers).forEach(function (key) { - var keypath = _this.observers[key]; + var keypath = _this9.observers[key]; - _this.observers[key] = _this.observe(_this.view.models, keypath, (function (key) { + _this9.observers[key] = _this9.observe(_this9.view.models, keypath, function (key) { return function () { - _this.componentView.models[key] = _this.observers[key].value(); + _this9.componentView.models[key] = _this9.observers[key].value(); }; - }).call(_this, key)); + }.call(_this9, key)); }); this.bound = true; @@ -1181,887 +1350,957 @@ return /******/ (function(modules) { // webpackBootstrap this.componentView.bind(); } else { (function () { - _this.el.innerHTML = _this.component.template.call(_this); - var scope = _this.component.initialize.call(_this, _this.el, _this.locals()); - _this.el._bound = true; + _this9.el.innerHTML = _this9.component.template.call(_this9); + var scope = _this9.component.initialize.call(_this9, _this9.el, _this9.locals()); + _this9.el._bound = true; var options = {}; - EXTENSIONS.forEach(function (extensionType) { + _constants.EXTENSIONS.forEach(function (extensionType) { options[extensionType] = {}; - if (_this.component[extensionType]) { - Object.keys(_this.component[extensionType]).forEach(function (key) { - options[extensionType][key] = _this.component[extensionType][key]; + if (_this9.component[extensionType]) { + Object.keys(_this9.component[extensionType]).forEach(function (key) { + options[extensionType][key] = _this9.component[extensionType][key]; }); } - Object.keys(_this.view[extensionType]).forEach(function (key) { + Object.keys(_this9.view[extensionType]).forEach(function (key) { if (!defined(options[extensionType][key])) { - options[extensionType][key] = _this.view[extensionType][key]; + options[extensionType][key] = _this9.view[extensionType][key]; } }); }); - OPTIONS.forEach(function (option) { - if (defined(_this.component[option])) { - options[option] = _this.component[option]; + _constants.OPTIONS.forEach(function (option) { + if (defined(_this9.component[option])) { + options[option] = _this9.component[option]; } else { - options[option] = _this.view[option]; + options[option] = _this9.view[option]; } }); - _this.componentView = new View(_this.el, scope, options); - _this.componentView.bind(); + _this9.componentView = new View(_this9.el, scope, options); + _this9.componentView.bind(); - Object.keys(_this.observers).forEach(function (key) { - var observer = _this.observers[key]; - var models = _this.componentView.models; + Object.keys(_this9.observers).forEach(function (key) { + var observer = _this9.observers[key]; + var models = _this9.componentView.models; - var upstream = _this.observe(models, key, (function (key, observer) { + var upstream = _this9.observe(models, key, function (key, observer) { return function () { - observer.setValue(_this.componentView.models[key]); + observer.setValue(_this9.componentView.models[key]); }; - }).call(_this, key, observer)); + }.call(_this9, key, observer)); - _this.upstreamObservers[key] = upstream; + _this9.upstreamObservers[key] = upstream; }); })(); } } - }, - unbind: { // Intercept `Rivets.Binding::unbind` to be called on `@componentView`. + }, { + key: 'unbind', value: function unbind() { - var _this = this; + var _this10 = this; Object.keys(this.upstreamObservers).forEach(function (key) { - _this.upstreamObservers[key].unobserve(); + _this10.upstreamObservers[key].unobserve(); }); Object.keys(this.observers).forEach(function (key) { - _this.observers[key].unobserve(); + _this10.observers[key].unobserve(); }); if (defined(this.componentView)) { this.componentView.unbind.call(this); } } - } - }); + }]); - return ComponentBinding; - })(Binding); + return ComponentBinding; + }(Binding); - // A text node binding, defined internally to deal with text and element node - // differences while avoiding it being overwritten. + var TextBinding = exports.TextBinding = function (_Binding2) { + _inherits(TextBinding, _Binding2); - var TextBinding = exports.TextBinding = (function (_Binding2) { - // Initializes a text binding for the specified view and text node. + // Initializes a text binding for the specified view and text node. - function TextBinding(view, el, type, keypath) { - var options = arguments[4] === undefined ? {} : arguments[4]; + function TextBinding(view, el, type, keypath) { + var options = arguments.length <= 4 || arguments[4] === undefined ? {} : arguments[4]; - _classCallCheck(this, TextBinding); + _classCallCheck(this, TextBinding); - this.view = view; - this.el = el; - this.type = type; - this.keypath = keypath; - this.options = options; - this.formatters = this.options.formatters || []; - this.dependencies = []; - this.formatterObservers = {}; + var _this11 = _possibleConstructorReturn(this, Object.getPrototypeOf(TextBinding).call(this, view, el, type)); - this.binder = { - routine: function (node, value) { - node.data = defined(value) ? value : ""; - } - }; + _this11.keypath = keypath; + _this11.options = options; + _this11.formatters = _this11.options.formatters || []; + _this11.dependencies = []; + _this11.formatterObservers = {}; - this.sync = this.sync.bind(this); - } + _this11.binder = { + routine: function routine(node, value) { + node.data = defined(value) ? value : ''; + } + }; - _inherits(TextBinding, _Binding2); + _this11.sync = _this11.sync.bind(_this11); + return _this11; + } - _createClass(TextBinding, { - sync: { + // Wrap the call to `sync` to avoid function context issues. - // Wrap the call to `sync` to avoid function context issues. + _createClass(TextBinding, [{ + key: 'sync', value: function sync() { - _get(Object.getPrototypeOf(TextBinding.prototype), "sync", this).call(this); + _get(Object.getPrototypeOf(TextBinding.prototype), 'sync', this).call(this); } - } - }); + }]); - return TextBinding; - })(Binding); + return TextBinding; + }(Binding); + }); /***/ }, /* 6 */ -/***/ function(module, exports) { - - - - // Parser and tokenizer for getting the type and value from a string. - "use strict"; - - exports.parseType = parseType; +/***/ function(module, exports, __webpack_require__) { - // Template parser and tokenizer for mustache-style text content bindings. - // Parses the template and returns a set of tokens, separating static portions - // of text from binding declarations. - exports.parseTemplate = parseTemplate; - Object.defineProperty(exports, "__esModule", { - value: true - }); - var PRIMITIVE = 0; - var KEYPATH = 1; - var TEXT = 0; - var BINDING = 1; - function parseType(string) { - var type = PRIMITIVE; - var value = string; - - if (/^'.*'$|^".*"$/.test(string)) { - value = string.slice(1, -1); - } else if (string === "true") { - value = true; - } else if (string === "false") { - value = false; - } else if (string === "null") { - value = null; - } else if (string === "undefined") { - value = undefined; - } else if (isNaN(Number(string)) === false) { - value = Number(string); + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [exports], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports !== "undefined") { + factory(exports); } else { - type = KEYPATH; + var mod = { + exports: {} + }; + factory(mod.exports); + global.parsers = mod.exports; } + })(this, function (exports) { + 'use strict'; - return { type: type, value: value }; - } + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.parseType = parseType; + exports.parseTemplate = parseTemplate; + var PRIMITIVE = 0; + var KEYPATH = 1; + var TEXT = 0; + var BINDING = 1; + + // Parser and tokenizer for getting the type and value from a string. + function parseType(string) { + var type = PRIMITIVE; + var value = string; + + if (/^'.*'$|^".*"$/.test(string)) { + value = string.slice(1, -1); + } else if (string === 'true') { + value = true; + } else if (string === 'false') { + value = false; + } else if (string === 'null') { + value = null; + } else if (string === 'undefined') { + value = undefined; + } else if (isNaN(Number(string)) === false) { + value = Number(string); + } else { + type = KEYPATH; + } - function parseTemplate(template, delimiters) { - var tokens = []; - var length = template.length; - var index = 0; - var lastIndex = 0; + return { type: type, value: value }; + } - while (lastIndex < length) { - index = template.indexOf(delimiters[0], lastIndex); + // Template parser and tokenizer for mustache-style text content bindings. + // Parses the template and returns a set of tokens, separating static portions + // of text from binding declarations. + function parseTemplate(template, delimiters) { + var tokens = []; + var length = template.length; + var index = 0; + var lastIndex = 0; - if (index < 0) { - tokens.push({ - type: TEXT, - value: template.slice(lastIndex) - }); + while (lastIndex < length) { + index = template.indexOf(delimiters[0], lastIndex); - break; - } else { - if (index > 0 && lastIndex < index) { + if (index < 0) { tokens.push({ type: TEXT, - value: template.slice(lastIndex, index) + value: template.slice(lastIndex) }); - } - - lastIndex = index + delimiters[0].length; - index = template.indexOf(delimiters[1], lastIndex); - - if (index < 0) { - var substring = template.slice(lastIndex - delimiters[1].length); - lastToken = tokens[tokens.length - 1]; - if (lastToken && lastToken.type === TEXT) { - lastToken.value += substring; - } else { + break; + } else { + if (index > 0 && lastIndex < index) { tokens.push({ type: TEXT, - value: substring + value: template.slice(lastIndex, index) }); } - break; - } + lastIndex = index + delimiters[0].length; + index = template.indexOf(delimiters[1], lastIndex); - var value = template.slice(lastIndex, index).trim(); + if (index < 0) { + var substring = template.slice(lastIndex - delimiters[1].length); + var lastToken = tokens[tokens.length - 1]; - tokens.push({ - type: BINDING, - value: value - }); + if (lastToken && lastToken.type === TEXT) { + lastToken.value += substring; + } else { + tokens.push({ + type: TEXT, + value: substring + }); + } - lastIndex = index + delimiters[1].length; - } - } + break; + } - return tokens; - } + var value = template.slice(lastIndex, index).trim(); -/***/ }, -/* 7 */ -/***/ function(module, exports) { + tokens.push({ + type: BINDING, + value: value + }); - "use strict"; + lastIndex = index + delimiters[1].length; + } + } - exports.bindEvent = bindEvent; - exports.unbindEvent = unbindEvent; - exports.getInputValue = getInputValue; - Object.defineProperty(exports, "__esModule", { - value: true + return tokens; + } }); - var $ = window.jQuery || window.$; - function bindEvent(el, event, handler) { - if ($) { - $(el).on(event, handler); - } else { - el.addEventListener(event, handler, false); - } - } +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { - function unbindEvent(el, event, handler) { - if ($) { - $(el).off(event, handler); + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [exports], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports !== "undefined") { + factory(exports); } else { - el.removeEventListener(event, handler, false); + var mod = { + exports: {} + }; + factory(mod.exports); + global.util = mod.exports; } - } + })(this, function (exports) { + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.bindEvent = bindEvent; + exports.unbindEvent = unbindEvent; + exports.getInputValue = getInputValue; + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; + } : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; + }; - function getInputValue(el) { - if ($) { - var $el = $(el); + var $ = window.jQuery || window.$; - if ($el.attr("type") === "checkbox") { - return $el.is(":checked"); + function bindEvent(el, event, handler) { + if ($) { + $(el).on(event, handler); } else { - return $el.val(); + el.addEventListener(event, handler, false); } - } else { - if (el.type === "checkbox") { - return el.checked; - } else if (el.type === "select-multiple") { - var _ret = (function () { - var results = []; - - el.options.forEach(function (option) { - if (option.selected) { - results.push(option.value); - } - }); + } - return { - v: results - }; - })(); + function unbindEvent(el, event, handler) { + if ($) { + $(el).off(event, handler); + } else { + el.removeEventListener(event, handler, false); + } + } + + function getInputValue(el) { + if ($) { + var $el = $(el); - if (typeof _ret === "object") { - return _ret.v; + if ($el.attr('type') === 'checkbox') { + return $el.is(':checked'); + } else { + return $el.val(); } } else { - return el.value; + if (el.type === 'checkbox') { + return el.checked; + } else if (el.type === 'select-multiple') { + var _ret = function () { + var results = []; + + el.options.forEach(function (option) { + if (option.selected) { + results.push(option.value); + } + }); + + return { + v: results + }; + }(); + + if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; + } else { + return el.value; + } } } - } + }); /***/ }, /* 8 */ -/***/ function(module, exports) { +/***/ function(module, exports, __webpack_require__) { - // The default `.` adapter thats comes with Rivets.js. Allows subscribing to - // properties on plain objects, implemented in ES5 natives using - // `Object.defineProperty`. + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, exports], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports !== "undefined") { + factory(module, exports); + } else { + var mod = { + exports: {} + }; + factory(mod, mod.exports); + global.adapter = mod.exports; + } + })(this, function (module, exports) { + 'use strict'; - "use strict"; + Object.defineProperty(exports, "__esModule", { + value: true + }); + // The default `.` adapter thats comes with Rivets.js. Allows subscribing to + // properties on plain objects, implemented in ES5 natives using + // `Object.defineProperty`. - var defined = function (value) { - return value !== undefined && value !== null; - }; + var defined = function defined(value) { + return value !== undefined && value !== null; + }; - var ARRAY_METHODS = ["push", "pop", "shift", "unshift", "sort", "reverse", "splice"]; + var ARRAY_METHODS = ['push', 'pop', 'shift', 'unshift', 'sort', 'reverse', 'splice']; - var adapter = { - id: "_rv", - counter: 0, - weakmap: {}, + var adapter = { + id: '_rv', + counter: 0, + weakmap: {}, - weakReference: function weakReference(obj) { - if (!obj.hasOwnProperty(this.id)) { - var id = this.counter++; + weakReference: function weakReference(obj) { + if (!obj.hasOwnProperty(this.id)) { + var id = this.counter++; - Object.defineProperty(obj, this.id, { - value: id - }); - } + Object.defineProperty(obj, this.id, { + value: id + }); + } - if (!this.weakmap[obj[this.id]]) { - this.weakmap[obj[this.id]] = { - callbacks: {} - }; - } + if (!this.weakmap[obj[this.id]]) { + this.weakmap[obj[this.id]] = { + callbacks: {} + }; + } - return this.weakmap[obj[this.id]]; - }, + return this.weakmap[obj[this.id]]; + }, - cleanupWeakReference: function cleanupWeakReference(ref, id) { - if (!Object.keys(ref.callbacks).length) { - if (!(ref.pointers && Object.keys(ref.pointers).length)) { - delete this.weakmap[id]; + cleanupWeakReference: function cleanupWeakReference(ref, id) { + if (!Object.keys(ref.callbacks).length) { + if (!(ref.pointers && Object.keys(ref.pointers).length)) { + delete this.weakmap[id]; + } } - } - }, + }, - stubFunction: function stubFunction(obj, fn) { - var original = obj[fn]; - var map = this.weakReference(obj); - var weakmap = this.weakmap; + stubFunction: function stubFunction(obj, fn) { + var original = obj[fn]; + var map = this.weakReference(obj); + var weakmap = this.weakmap; - obj[fn] = function () { - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } + obj[fn] = function () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } - var response = original.apply(obj, args); + var response = original.apply(obj, args); - Object.keys(map.pointers).forEach(function (r) { - var k = map.pointers[r]; + Object.keys(map.pointers).forEach(function (r) { + var k = map.pointers[r]; - if (defined(weakmap[r])) { - if (weakmap[r].callbacks[k] instanceof Array) { - weakmap[r].callbacks[k].forEach(function (callback) { - callback(); - }); + if (defined(weakmap[r])) { + if (weakmap[r].callbacks[k] instanceof Array) { + weakmap[r].callbacks[k].forEach(function (callback) { + callback(); + }); + } } - } - }); + }); - return response; - }; - }, + return response; + }; + }, - observeMutations: function observeMutations(obj, ref, keypath) { - var _this = this; + observeMutations: function observeMutations(obj, ref, keypath) { + var _this = this; - if (obj instanceof Array) { - var map = this.weakReference(obj); + if (obj instanceof Array) { + var map = this.weakReference(obj); - if (!defined(map.pointers)) { - map.pointers = {}; + if (!defined(map.pointers)) { + map.pointers = {}; - ARRAY_METHODS.forEach(function (fn) { - _this.stubFunction(obj, fn); - }); - } + ARRAY_METHODS.forEach(function (fn) { + _this.stubFunction(obj, fn); + }); + } - if (!defined(map.pointers[ref])) { - map.pointers[ref] = []; - } + if (!defined(map.pointers[ref])) { + map.pointers[ref] = []; + } - if (map.pointers[ref].indexOf(keypath) === -1) { - map.pointers[ref].push(keypath); + if (map.pointers[ref].indexOf(keypath) === -1) { + map.pointers[ref].push(keypath); + } } - } - }, + }, - unobserveMutations: function unobserveMutations(obj, ref, keypath) { - if (obj instanceof Array && defined(obj[this.id])) { - var map = this.weakmap[obj[this.id]]; + unobserveMutations: function unobserveMutations(obj, ref, keypath) { + if (obj instanceof Array && defined(obj[this.id])) { + var map = this.weakmap[obj[this.id]]; - if (map) { - var pointers = map.pointers[ref]; + if (map) { + var pointers = map.pointers[ref]; - if (pointers) { - var idx = pointers.indexOf(keypath); + if (pointers) { + var idx = pointers.indexOf(keypath); - if (idx > -1) { - pointers.splice(idx, 1); - } + if (idx > -1) { + pointers.splice(idx, 1); + } - if (!pointers.length) { - delete map.pointers[ref]; - } + if (!pointers.length) { + delete map.pointers[ref]; + } - this.cleanupWeakReference(map, obj[this.id]); + this.cleanupWeakReference(map, obj[this.id]); + } } } - } - }, + }, - observe: function observe(obj, keypath, callback) { - var _this = this; + observe: function observe(obj, keypath, callback) { + var _this2 = this; - var callbacks = this.weakReference(obj).callbacks; + var callbacks = this.weakReference(obj).callbacks; - if (!defined(callbacks[keypath])) { - callbacks[keypath] = []; - var desc = Object.getOwnPropertyDescriptor(obj, keypath); + if (!defined(callbacks[keypath])) { + callbacks[keypath] = []; + var desc = Object.getOwnPropertyDescriptor(obj, keypath); - if (!(desc && (desc.get || desc.set))) { - (function () { - var value = obj[keypath]; + if (!(desc && (desc.get || desc.set))) { + (function () { + var value = obj[keypath]; - Object.defineProperty(obj, keypath, { - enumerable: true, + Object.defineProperty(obj, keypath, { + enumerable: true, - get: function () { - return value; - }, + get: function get() { + return value; + }, - set: function (newValue) { - if (newValue !== value) { - _this.unobserveMutations(value, obj[_this.id], keypath); - value = newValue; - var map = _this.weakmap[obj[_this.id]]; + set: function set(newValue) { + if (newValue !== value) { + _this2.unobserveMutations(value, obj[_this2.id], keypath); + value = newValue; + var map = _this2.weakmap[obj[_this2.id]]; - if (map) { - (function () { - var callbacks = map.callbacks; + if (map) { + var _callbacks = map.callbacks; - if (callbacks[keypath]) { - callbacks[keypath].slice().forEach(function (callback) { - if (callbacks[keypath].indexOf(callback) > -1) { - callback(); - } + if (_callbacks[keypath]) { + _callbacks[keypath].forEach(function (cb) { + cb(); }); } - _this.observeMutations(newValue, obj[_this.id], keypath); - })(); + _this2.observeMutations(newValue, obj[_this2.id], keypath); + } } } - } - }); - })(); + }); + })(); + } } - } - if (callbacks[keypath].indexOf(callback) === -1) { - callbacks[keypath].push(callback); - } + if (callbacks[keypath].indexOf(callback) === -1) { + callbacks[keypath].push(callback); + } - this.observeMutations(obj[keypath], obj[this.id], keypath); - }, + this.observeMutations(obj[keypath], obj[this.id], keypath); + }, - unobserve: function unobserve(obj, keypath, callback) { - var map = this.weakmap[obj[this.id]]; + unobserve: function unobserve(obj, keypath, callback) { + var map = this.weakmap[obj[this.id]]; - if (map) { - var callbacks = map.callbacks[keypath]; + if (map) { + var callbacks = map.callbacks[keypath]; - if (callbacks) { - var idx = callbacks.indexOf(callback); + if (callbacks) { + var idx = callbacks.indexOf(callback); - if (idx > -1) { - callbacks.splice(idx, 1); + if (idx > -1) { + callbacks.splice(idx, 1); - if (!callbacks.length) { - delete map.callbacks[keypath]; + if (!callbacks.length) { + delete map.callbacks[keypath]; + } } - } - this.unobserveMutations(obj[keypath], obj[this.id], keypath); - this.cleanupWeakReference(map, obj[this.id]); + this.unobserveMutations(obj[keypath], obj[this.id], keypath); + this.cleanupWeakReference(map, obj[this.id]); + } } - } - }, + }, - get: function get(obj, keypath) { - return obj[keypath]; - }, + get: function get(obj, keypath) { + return obj[keypath]; + }, - set: function (obj, keypath, value) { - obj[keypath] = value; - } - }; + set: function set(obj, keypath, value) { + obj[keypath] = value; + } + }; - module.exports = adapter; + exports.default = adapter; + module.exports = exports['default']; + }); /***/ }, /* 9 */ /***/ function(module, exports, __webpack_require__) { - "use strict"; + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, exports, __webpack_require__(2), __webpack_require__(7)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports !== "undefined") { + factory(module, exports, require('./rivets'), require('./util')); + } else { + var mod = { + exports: {} + }; + factory(mod, mod.exports, global.rivets, global.util); + global.binders = mod.exports; + } + })(this, function (module, exports, _rivets, _util) { + 'use strict'; - var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; + Object.defineProperty(exports, "__esModule", { + value: true + }); - var _taggedTemplateLiteral = function (strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }; + var _rivets2 = _interopRequireDefault(_rivets); - var rivets = _interopRequire(__webpack_require__(2)); + function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + default: obj + }; + } - var _util = __webpack_require__(7); + var _templateObject = _taggedTemplateLiteral([' rivets: ', ' ', ' '], [' rivets: ', ' ', ' ']), + _templateObject2 = _taggedTemplateLiteral([' rivets: ', ' '], [' rivets: ', ' ']); - var bindEvent = _util.bindEvent; - var unbindEvent = _util.unbindEvent; + function _taggedTemplateLiteral(strings, raw) { + return Object.freeze(Object.defineProperties(strings, { + raw: { + value: Object.freeze(raw) + } + })); + } - var CHANGE_EVENT = "change"; + var CHANGE_EVENT = 'change'; - var defined = function (value) { - return value !== undefined && value !== null; - }; + var defined = function defined(value) { + return value !== undefined && value !== null; + }; - var getString = function (value) { - return defined(value) ? value.toString() : undefined; - }; + var getString = function getString(value) { + return defined(value) ? value.toString() : undefined; + }; - var times = function (n, cb) { - for (var i = 0; i < n; i++) { - cb(); - } - }; - - var binders = { - // Sets the element's text value. - text: function (el, value) { - el.textContent = defined(value) ? value : ""; - }, - - // Sets the element's HTML content. - html: function (el, value) { - el.innerHTML = defined(value) ? value : ""; - }, - - // Shows the element when value is true. - show: function (el, value) { - el.style.display = value ? "" : "none"; - }, - - // Hides the element when value is true (negated version of `show` binder). - hide: function (el, value) { - el.style.display = value ? "none" : ""; - }, - - // Enables the element when value is true. - enabled: function (el, value) { - el.disabled = !value; - }, - - // Disables the element when value is true (negated version of `enabled` binder). - disabled: function (el, value) { - el.disabled = !!value; - }, - - // Checks a checkbox or radio input when the value is true. Also sets the model - // property when the input is checked or unchecked (two-way binder). - checked: { - publishes: true, - priority: 2000, - - bind: function bind(el) { - bindEvent(el, CHANGE_EVENT, this.publish); + var times = function times(n, cb) { + for (var i = 0; i < n; i++) { + cb(); + } + }; + + var binders = { + // Sets the element's text value. + text: function text(el, value) { + el.textContent = defined(value) ? value : ''; }, - unbind: function unbind(el) { - unbindEvent(el, CHANGE_EVENT, this.publish); + // Sets the element's HTML content. + html: function html(el, value) { + el.innerHTML = defined(value) ? value : ''; }, - routine: function routine(el, value) { - if (el.type === "radio") { - el.checked = getString(el.value) === getString(value); - } else { - el.checked = !!value; - } - } - }, + // Shows the element when value is true. + show: function show(el, value) { + el.style.display = value ? '' : 'none'; + }, - // Unchecks a checkbox or radio input when the value is true (negated version of - // `checked` binder). Also sets the model property when the input is checked or - // unchecked (two-way binder). - unchecked: { - publishes: true, - priority: 2000, + // Hides the element when value is true (negated version of `show` binder). + hide: function hide(el, value) { + el.style.display = value ? 'none' : ''; + }, - bind: function bind(el) { - bindEvent(el, CHANGE_EVENT, this.publish); + // Enables the element when value is true. + enabled: function enabled(el, value) { + el.disabled = !value; }, - unbind: function unbind(el) { - unbindEvent(el, CHANGE_EVENT, this.publish); + // Disables the element when value is true (negated version of `enabled` binder). + disabled: function disabled(el, value) { + el.disabled = !!value; }, - routine: function routine(el, value) { - if (el.type === "radio") { - el.checked = getString(el.value) !== getString(value); - } else { - el.checked = !value; - } - } - }, + // Checks a checkbox or radio input when the value is true. Also sets the model + // property when the input is checked or unchecked (two-way binder). + checked: { + publishes: true, + priority: 2000, - // Sets the element's value. Also sets the model property when the input changes - // (two-way binder). - value: { - publishes: true, - priority: 3000, + bind: function bind(el) { + (0, _util.bindEvent)(el, CHANGE_EVENT, this.publish); + }, - bind: function bind(el) { - if (!(el.tagName === "INPUT" && el.type === "radio")) { - this.event = el.tagName === "SELECT" ? "change" : "input"; + unbind: function unbind(el) { + (0, _util.unbindEvent)(el, CHANGE_EVENT, this.publish); + }, - bindEvent(el, this.event, this.publish); + routine: function routine(el, value) { + if (el.type === 'radio') { + el.checked = getString(el.value) === getString(value); + } else { + el.checked = !!value; + } } }, - unbind: function unbind(el) { - if (!(el.tagName === "INPUT" && el.type === "radio")) { - unbindEvent(el, this.event, this.publish); - } - }, + // Unchecks a checkbox or radio input when the value is true (negated version of + // `checked` binder). Also sets the model property when the input is checked or + // unchecked (two-way binder). + unchecked: { + publishes: true, + priority: 2000, - routine: function routine(el, value) { - if (el.tagName === "INPUT" && el.type === "radio") { - el.setAttribute("value", value); - } else if (window.jQuery) { - el = jQuery(el); + bind: function bind(el) { + (0, _util.bindEvent)(el, CHANGE_EVENT, this.publish); + }, - if (getString(value) !== getString(el.val())) { - el.val(defined(value) ? value : ""); - } - } else { - if (el.type === "select-multiple") { - if (value instanceof Array) { - el.options.forEach(function (option) { - option.selected = value.indexOf(option.value) > -1; - }); - } - } else if (getString(value) !== getString(el.value)) { - el.value = defined(value) ? value : ""; + unbind: function unbind(el) { + (0, _util.unbindEvent)(el, CHANGE_EVENT, this.publish); + }, + + routine: function routine(el, value) { + if (el.type === 'radio') { + el.checked = getString(el.value) !== getString(value); + } else { + el.checked = !value; } } - } - }, + }, - // Inserts and binds the element and it's child nodes into the DOM when true. - "if": { - block: true, - priority: 4000, + // Sets the element's value. Also sets the model property when the input changes + // (two-way binder). + value: { + publishes: true, + priority: 3000, - bind: function bind(el) { - if (!defined(this.marker)) { - var attr = [this.view.prefix, this.type].join("-").replace("--", "-"); - var declaration = el.getAttribute(attr); + bind: function bind(el) { + if (!(el.tagName === 'INPUT' && el.type === 'radio')) { + this.event = el.tagName === 'SELECT' ? 'change' : 'input'; - this.marker = document.createComment(_taggedTemplateLiteral([" rivets: ", " ", " "], [" rivets: ", " ", " "]), this.type, declaration); - this.bound = false; + (0, _util.bindEvent)(el, this.event, this.publish); + } + }, - el.removeAttribute(attr); - el.parentNode.insertBefore(this.marker, el); - el.parentNode.removeChild(el); - } - }, + unbind: function unbind(el) { + if (!(el.tagName === 'INPUT' && el.type === 'radio')) { + (0, _util.unbindEvent)(el, this.event, this.publish); + } + }, + + routine: function routine(el, value) { + if (el.tagName === 'INPUT' && el.type === 'radio') { + el.setAttribute('value', value); + } else if (window.jQuery) { + el = jQuery(el); - unbind: function unbind() { - if (defined(this.nested)) { - this.nested.unbind(); + if (getString(value) !== getString(el.val())) { + el.val(defined(value) ? value : ''); + } + } else { + if (el.type === 'select-multiple') { + if (value instanceof Array) { + Array.from(el.options).forEach(function (option) { + option.selected = value.indexOf(option.value) > -1; + }); + } + } else if (getString(value) !== getString(el.value)) { + el.value = defined(value) ? value : ''; + } + } } }, - routine: function routine(el, value) { - var _this = this; - - if (!!value === !this.bound) { - if (value) { - (function () { - var models = {}; + // Inserts and binds the element and it's child nodes into the DOM when true. + if: { + block: true, + priority: 4000, - Object.keys(_this.view.models).forEach(function (key) { - models[key] = _this.view.models[key]; - }); + bind: function bind(el) { + if (!defined(this.marker)) { + var attr = [this.view.prefix, this.type].join('-').replace('--', '-'); + var declaration = el.getAttribute(attr); - if (defined(_this.nested)) { - _this.nested.bind(); - } else { - _this.nested = rivets.bind(el, models, _this.view.options()); - } + this.marker = document.createComment(_templateObject, this.type, declaration); + this.bound = false; - _this.marker.parentNode.insertBefore(el, _this.marker.nextSibling); - _this.bound = true; - })(); - } else { + el.removeAttribute(attr); + el.parentNode.insertBefore(this.marker, el); el.parentNode.removeChild(el); + } + }, + + unbind: function unbind() { + if (defined(this.nested)) { this.nested.unbind(); this.bound = false; } - } - }, + }, - update: function update(models) { - if (defined(this.nested)) { - this.nested.update(models); - } - } - }, + routine: function routine(el, value) { + var _this = this; - // Removes and unbinds the element and it's child nodes into the DOM when true - // (negated version of `if` binder). - unless: { - block: true, - priority: 4000, + if (!!value === !this.bound) { + if (value) { + (function () { + var models = {}; - bind: function bind(el) { - rivets.binders["if"].bind.call(this, el); - }, + Object.keys(_this.view.models).forEach(function (key) { + models[key] = _this.view.models[key]; + }); - unbind: function unbind() { - rivets.binders["if"].unbind.call(this); - }, + if (defined(_this.nested)) { + _this.nested.bind(); + } else { + _this.nested = _rivets2.default.bind(el, models, _this.view.options()); + } - routine: function routine(el, value) { - rivets.binders["if"].routine.call(this, el, !value); + _this.marker.parentNode.insertBefore(el, _this.marker.nextSibling); + _this.bound = true; + })(); + } else { + el.parentNode.removeChild(el); + this.nested.unbind(); + this.bound = false; + } + } + }, + + update: function update(models) { + if (defined(this.nested)) { + this.nested.update(models); + } + } }, - update: function update(models) { - rivets.binders["if"].update.call(this, models); - } - }, + // Removes and unbinds the element and it's child nodes into the DOM when true + // (negated version of `if` binder). + unless: { + block: true, + priority: 4000, - // Binds an event handler on the element. - "on-*": { - "function": true, - priority: 1000, + bind: function bind(el) { + _rivets2.default.binders.if.bind.call(this, el); + }, - unbind: function unbind(el) { - if (defined(this.handler)) { - unbindEvent(el, this.args[0], this.handler); - } - }, + unbind: function unbind() { + _rivets2.default.binders.if.unbind.call(this); + }, - routine: function routine(el, value) { - if (defined(this.handler)) { - unbindEvent(el, this.args[0], this.handler); - } + routine: function routine(el, value) { + _rivets2.default.binders.if.routine.call(this, el, !value); + }, - this.handler = this.eventHandler(value); - bindEvent(el, this.args[0], this.handler); - } - }, - - // Appends bound instances of the element in place for each item in the array. - "each-*": { - block: true, - priority: 4000, - - bind: function bind(el) { - if (!defined(this.marker)) { - var attr = [this.view.prefix, this.type].join("-").replace("--", "-"); - this.marker = document.createComment(_taggedTemplateLiteral([" rivets: ", " "], [" rivets: ", " "]), this.type); - this.iterated = []; - - el.removeAttribute(attr); - el.parentNode.insertBefore(this.marker, el); - el.parentNode.removeChild(el); - } else { - this.iterated.forEach(function (view) { - view.bind(); - }); + update: function update(models) { + _rivets2.default.binders.if.update.call(this, models); } }, - unbind: function unbind(el) { - if (defined(this.iterated)) { - this.iterated.forEach(function (view) { - view.unbind(); - }); + // Binds an event handler on the element. + 'on-*': { + function: true, + priority: 1000, + + unbind: function unbind(el) { + if (defined(this.handler)) { + (0, _util.unbindEvent)(el, this.args[0], this.handler); + } + }, + + routine: function routine(el, value) { + if (defined(this.handler)) { + (0, _util.unbindEvent)(el, this.args[0], this.handler); + } + + this.handler = this.eventHandler(value); + (0, _util.bindEvent)(el, this.args[0], this.handler); } }, - routine: function routine(el, collection) { - var _this = this; + // Appends bound instances of the element in place for each item in the array. + 'each-*': { + block: true, + priority: 4000, - var modelName = this.args[0]; - var collection = collection || []; + bind: function bind(el) { + if (!defined(this.marker)) { + var attr = [this.view.prefix, this.type].join('-').replace('--', '-'); + this.marker = document.createComment(_templateObject2, this.type); + this.iterated = []; - if (this.iterated.length > collection.length) { - times(this.iterated.length - collection.length, function () { - var view = _this.iterated.pop(); - view.unbind(); - _this.marker.parentNode.removeChild(view.els[0]); - }); - } + el.removeAttribute(attr); + el.parentNode.insertBefore(this.marker, el); + el.parentNode.removeChild(el); + } else { + this.iterated.forEach(function (view) { + view.bind(); + }); + } + }, - collection.forEach(function (model, index) { - var data = { index: index }; - data[modelName] = model; + unbind: function unbind(el) { + if (defined(this.iterated)) { + this.iterated.forEach(function (view) { + view.unbind(); + }); + } + }, - if (!defined(_this.iterated[index])) { - Object.keys(_this.view.models).forEach(function (key) { - if (!defined(data[key])) { - data[key] = _this.view.models[key]; - } + routine: function routine(el, _collection) { + var _this2 = this; + + var modelName = this.args[0]; + var collection = _collection || []; + + if (this.iterated.length > collection.length) { + times(this.iterated.length - collection.length, function () { + var view = _this2.iterated.pop(); + view.unbind(); + _this2.marker.parentNode.removeChild(view.els[0]); }); + } - var previous = _this.marker; + collection.forEach(function (model, index) { + var data = { index: index }; + data[_rivets2.default.iterationAlias(modelName)] = index; + data[modelName] = model; - if (_this.iterated.length) { - previous = _this.iterated[_this.iterated.length - 1].els[0]; - } + if (!defined(_this2.iterated[index])) { + Object.keys(_this2.view.models).forEach(function (key) { + if (!defined(data[key])) { + data[key] = _this2.view.models[key]; + } + }); + + var previous = _this2.marker; - var options = _this.view.options(); - options.preloadData = true; + if (_this2.iterated.length) { + previous = _this2.iterated[_this2.iterated.length - 1].els[0]; + } + + var options = _this2.view.options(); + options.preloadData = true; + + var template = el.cloneNode(true); + var view = _rivets2.default.bind(template, data, options); + _this2.iterated.push(view); + _this2.marker.parentNode.insertBefore(template, previous.nextSibling); + } else if (_this2.iterated[index].models[modelName] !== model) { + _this2.iterated[index].update(data); + } + }); - var template = el.cloneNode(true); - var view = rivets.bind(template, data, options); - _this.iterated.push(view); - _this.marker.parentNode.insertBefore(template, previous.nextSibling); - } else if (_this.iterated[index].models[modelName] !== model) { - _this.iterated[index].update(data); + if (el.nodeName === 'OPTION') { + this.view.bindings.forEach(function (binding) { + if (binding.el === _this2.marker.parentNode && binding.type === 'value') { + binding.sync(); + } + }); } - }); + }, + + update: function update(models) { + var _this3 = this; - if (el.nodeName === "OPTION") { - this.view.bindings.forEach(function (binding) { - if (binding.el === _this.marker.parentNode && binding.type === "value") { - binding.sync(); + var data = {}; + + Object.keys(models).forEach(function (key) { + if (key !== _this3.args[0]) { + data[key] = models[key]; } }); + + this.iterated.forEach(function (view) { + view.update(data); + }); } }, - update: function update(models) { - var _this = this; - - var data = {}; + // Adds or removes the class from the element when value is true or false. + 'class-*': function _class(el, value) { + var elClass = ' ' + el.className + ' '; - Object.keys(models).forEach(function (key) { - if (key !== _this.args[0]) { - data[key] = models[key]; + if (!value === elClass.indexOf(' ' + this.args[0] + ' ') > -1) { + if (value) { + el.className = el.className + ' ' + this.args[0]; + } else { + el.className = elClass.replace(' ' + this.args[0] + ' ', ' ').trim(); } - }); - - this.iterated.forEach(function (view) { - view.update(data); - }); - } - }, - - // Adds or removes the class from the element when value is true or false. - "class-*": function _class(el, value) { - var elClass = " " + el.className + " "; + } + }, - if (!value === elClass.indexOf(" " + this.args[0] + " ") > -1) { - if (value) { - el.className = "" + el.className + " " + this.args[0]; + // Sets the attribute on the element. If no binder above is matched it will fall + // back to using this binder. + '*': function _(el, value) { + if (defined(value)) { + el.setAttribute(this.type, value); } else { - el.className = elClass.replace(" " + this.args[0] + " ", " ").trim(); + el.removeAttribute(this.type); } } - }, - - // Sets the attribute on the element. If no binder above is matched it will fall - // back to using this binder. - "*": function _(el, value) { - if (defined(value)) { - el.setAttribute(this.type, value); - } else { - el.removeAttribute(this.type); - } - } - }; + }; - module.exports = binders; + exports.default = binders; + module.exports = exports['default']; + }); /***/ } /******/ ]) diff --git a/package.json b/package.json index abf46eddb..af4d73d09 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "mocha": "2.5.3", "should": "~4.0.4", "sinon": "~1.10.2", - "webpack": "^1.7.3", + "webpack": "1.13.1", + "babel-core": "6.9.1", "babel-cli": "^6.0.0", "mocha-phantomjs": "4.0.2", "babel-loader" : "6.2.4", @@ -37,6 +38,7 @@ "mocha-phantomjs-core" : "1.3.1", "babel-preset-es2015": "6.9.0", "babel-plugin-transform-es2015-modules-umd": "6.8.0", - "phantomjs": "2.1.7" + "phantomjs": "2.1.7", + "babel-plugin-add-module-exports" : "0.2.1" } } diff --git a/spec/lib/parsers.js b/spec/lib/parsers.js index 8019e250c..7bbdcae44 100644 --- a/spec/lib/parsers.js +++ b/spec/lib/parsers.js @@ -1,7 +1,7 @@ (function (global, factory) { - if (typeof define === 'function' && define.amd) { + if (typeof define === "function" && define.amd) { define(['exports'], factory); - } else if (typeof exports !== 'undefined') { + } else if (typeof exports !== "undefined") { factory(exports); } else { var mod = { @@ -13,7 +13,7 @@ })(this, function (exports) { 'use strict'; - Object.defineProperty(exports, '__esModule', { + Object.defineProperty(exports, "__esModule", { value: true }); exports.parseType = parseType; @@ -24,7 +24,6 @@ var BINDING = 1; // Parser and tokenizer for getting the type and value from a string. - function parseType(string) { var type = PRIMITIVE; var value = string; @@ -51,7 +50,6 @@ // Template parser and tokenizer for mustache-style text content bindings. // Parses the template and returns a set of tokens, separating static portions // of text from binding declarations. - function parseTemplate(template, delimiters) { var tokens = []; var length = template.length; @@ -81,7 +79,7 @@ if (index < 0) { var substring = template.slice(lastIndex - delimiters[1].length); - lastToken = tokens[tokens.length - 1]; + var lastToken = tokens[tokens.length - 1]; if (lastToken && lastToken.type === TEXT) { lastToken.value += substring; diff --git a/src/binders.js b/src/binders.js index fde0491f1..616377413 100644 --- a/src/binders.js +++ b/src/binders.js @@ -267,9 +267,9 @@ const binders = { } }, - routine: function(el, collection) { + routine: function(el, _collection) { let modelName = this.args[0] - let collection = collection || [] + let collection = _collection || [] if (this.iterated.length > collection.length) { times(this.iterated.length - collection.length, () => { diff --git a/src/bindings.js b/src/bindings.js index ee0f9eb72..e8888c268 100644 --- a/src/bindings.js +++ b/src/bindings.js @@ -266,31 +266,30 @@ export class ComponentBinding extends Binding { // Initializes a component binding for the specified view. The raw component // element is passed in along with the component type. Attributes and scope // inflections are determined based on the components defined attributes. - constructor(view, el, type) { - this.view = view - this.el = el - this.type = type - this.component = this.view.components[this.type] - this.static = {} - this.observers = {} - this.upstreamObservers = {} - - let bindingRegExp = view.bindingRegExp() - - if (this.el.attributes) { - this.el.attributes.forEach(attribute => { - if (!bindingRegExp.test(attribute.name)) { - let propertyName = this.camelCase(attribute.name) - let stat = this.component.static - - if (stat && stat.indexOf(propertyName) > -1) { - this.static[propertyName] = attribute.value - } else { - this.observers[propertyName] = attribute.value - } - } - }) - } + constructor(view, el, type) { + super(view, el, type); + + this.component = this.view.components[this.type] + this.static = {} + this.observers = {} + this.upstreamObservers = {} + + let bindingRegExp = view.bindingRegExp() + + if (this.el.attributes) { + this.el.attributes.forEach(attribute => { + if (!bindingRegExp.test(attribute.name)) { + let propertyName = this.camelCase(attribute.name) + let stat = this.component.static + + if (stat && stat.indexOf(propertyName) > -1) { + this.static[propertyName] = attribute.value + } else { + this.observers[propertyName] = attribute.value + } + } + }) + } } @@ -417,23 +416,22 @@ export class ComponentBinding extends Binding { // differences while avoiding it being overwritten. export class TextBinding extends Binding { // Initializes a text binding for the specified view and text node. - constructor(view, el, type, keypath, options = {}) { - this.view = view - this.el = el - this.type = type - this.keypath = keypath - this.options = options - this.formatters = this.options.formatters || [] - this.dependencies = [] - this.formatterObservers = {} - - this.binder = { - routine: (node, value) => { - node.data = defined(value) ? value : '' - } - } - - this.sync = this.sync.bind(this) + constructor(view, el, type, keypath, options = {}) { + super(view, el, type); + + this.keypath = keypath + this.options = options + this.formatters = this.options.formatters || [] + this.dependencies = [] + this.formatterObservers = {} + + this.binder = { + routine: (node, value) => { + node.data = defined(value) ? value : '' + } + } + + this.sync = this.sync.bind(this) } // Wrap the call to `sync` to avoid function context issues. diff --git a/src/export.js b/src/export.js index 0487433ef..484c35725 100644 --- a/src/export.js +++ b/src/export.js @@ -20,12 +20,12 @@ const factory = sightglass => { // Initializes a new instance of a component on the specified element and // returns a Rivets.View instance. - rivets.init = (component, el, data = {}) => { + rivets.init = (_component, el, data = {}) => { if (!el) { el = document.createElement('div') } - component = rivets.components[component] + let component = rivets.components[_component] el.innerHTML = component.template.call(rivets, el) let scope = component.initialize.call(rivets, el, data) diff --git a/webpack.config.js b/webpack.config.js index 31f77a304..eeb541523 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -16,7 +16,14 @@ module.exports = { { test: /\.js$/, exclude: '/node_modules/', - loader: 'babel-loader' + loader: 'babel', + query: { + presets: ['es2015'], + plugins: [ + 'add-module-exports', + 'transform-es2015-modules-umd' + ] + } } ] }, From 12abf28b4004d64d0087747b98f7ae85f9c192f9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 8 Aug 2016 14:29:05 +0100 Subject: [PATCH 37/43] fixed indentation --- src/bindings.js | 80 ++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/bindings.js b/src/bindings.js index e8888c268..a52014567 100644 --- a/src/bindings.js +++ b/src/bindings.js @@ -266,30 +266,30 @@ export class ComponentBinding extends Binding { // Initializes a component binding for the specified view. The raw component // element is passed in along with the component type. Attributes and scope // inflections are determined based on the components defined attributes. - constructor(view, el, type) { - super(view, el, type); - - this.component = this.view.components[this.type] - this.static = {} - this.observers = {} - this.upstreamObservers = {} - - let bindingRegExp = view.bindingRegExp() - - if (this.el.attributes) { - this.el.attributes.forEach(attribute => { - if (!bindingRegExp.test(attribute.name)) { - let propertyName = this.camelCase(attribute.name) - let stat = this.component.static - - if (stat && stat.indexOf(propertyName) > -1) { - this.static[propertyName] = attribute.value - } else { - this.observers[propertyName] = attribute.value - } - } - }) - } + constructor(view, el, type) { + super(view, el, type); + + this.component = this.view.components[this.type] + this.static = {} + this.observers = {} + this.upstreamObservers = {} + + let bindingRegExp = view.bindingRegExp() + + if (this.el.attributes) { + this.el.attributes.forEach(attribute => { + if (!bindingRegExp.test(attribute.name)) { + let propertyName = this.camelCase(attribute.name) + let stat = this.component.static + + if (stat && stat.indexOf(propertyName) > -1) { + this.static[propertyName] = attribute.value + } else { + this.observers[propertyName] = attribute.value + } + } + }) + } } @@ -416,22 +416,22 @@ export class ComponentBinding extends Binding { // differences while avoiding it being overwritten. export class TextBinding extends Binding { // Initializes a text binding for the specified view and text node. - constructor(view, el, type, keypath, options = {}) { - super(view, el, type); - - this.keypath = keypath - this.options = options - this.formatters = this.options.formatters || [] - this.dependencies = [] - this.formatterObservers = {} - - this.binder = { - routine: (node, value) => { - node.data = defined(value) ? value : '' - } - } - - this.sync = this.sync.bind(this) + constructor(view, el, type, keypath, options = {}) { + super(view, el, type); + + this.keypath = keypath + this.options = options + this.formatters = this.options.formatters || [] + this.dependencies = [] + this.formatterObservers = {} + + this.binder = { + routine: (node, value) => { + node.data = defined(value) ? value : '' + } + } + + this.sync = this.sync.bind(this) } // Wrap the call to `sync` to avoid function context issues. From 13250b44cbdc41778cae48754107a780be66e3a4 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 8 Aug 2016 14:44:36 +0100 Subject: [PATCH 38/43] remove dist files --- dist/rivets.bundled.js | 2308 ------------------------------------ dist/rivets.bundled.min.js | 6 - 2 files changed, 2314 deletions(-) delete mode 100644 dist/rivets.bundled.js delete mode 100755 dist/rivets.bundled.min.js diff --git a/dist/rivets.bundled.js b/dist/rivets.bundled.js deleted file mode 100644 index eba1edf26..000000000 --- a/dist/rivets.bundled.js +++ /dev/null @@ -1,2308 +0,0 @@ -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(); - else if(typeof define === 'function' && define.amd) - define([], factory); - else if(typeof exports === 'object') - exports["rivets"] = factory(); - else - root["rivets"] = factory(); -})(this, function() { -return /******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; - -/******/ // The require function -/******/ function __webpack_require__(moduleId) { - -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) -/******/ return installedModules[moduleId].exports; - -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; - -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - -/******/ // Flag the module as loaded -/******/ module.loaded = true; - -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } - - -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; - -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; - -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { - if (true) { - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, exports, __webpack_require__(1), __webpack_require__(2), __webpack_require__(4), __webpack_require__(8), __webpack_require__(9)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof exports !== "undefined") { - factory(module, exports, require('sightglass'), require('./rivets'), require('./view'), require('./adapter'), require('./binders')); - } else { - var mod = { - exports: {} - }; - factory(mod, mod.exports, global.sightglass, global.rivets, global.view, global.adapter, global.binders); - global._export = mod.exports; - } - })(this, function (module, exports, _sightglass, _rivets, _view, _adapter, _binders) { - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _sightglass2 = _interopRequireDefault(_sightglass); - - var _rivets2 = _interopRequireDefault(_rivets); - - var _view2 = _interopRequireDefault(_view); - - var _adapter2 = _interopRequireDefault(_adapter); - - var _binders2 = _interopRequireDefault(_binders); - - function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { - default: obj - }; - } - - // Module factory. Integrates sightglass and public API methods. Returns the - // public interface. - var factory = function factory(sightglass) { - _rivets2.default.sightglass = sightglass; - _rivets2.default.binders = _binders2.default; - _rivets2.default.adapters['.'] = _adapter2.default; - - // Binds some data to a template / element. Retuddrns a Rivets.View instance. - _rivets2.default.bind = function (el) { - var models = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - - var view = new _view2.default(el, models, options); - view.bind(); - return view; - }; - - // Initializes a new instance of a component on the specified element and - // returns a Rivets.View instance. - _rivets2.default.init = function (_component, el) { - var data = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - - if (!el) { - el = document.createElement('div'); - } - - var component = _rivets2.default.components[_component]; - el.innerHTML = component.template.call(_rivets2.default, el); - var scope = component.initialize.call(_rivets2.default, el, data); - - var view = new _view2.default(el, scope); - view.bind(); - return view; - }; - - return _rivets2.default; - }; - - exports.default = factory(_sightglass2.default); - module.exports = exports['default']; - }); - -/***/ }, -/* 1 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { - if (true) { - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof exports !== "undefined") { - factory(module); - } else { - var mod = { - exports: {} - }; - factory(mod); - global.index = mod.exports; - } - })(this, function (module) { - 'use strict'; - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; - }; - - (function () { - // Public sightglass interface. - function sightglass(obj, keypath, callback, options) { - return new Observer(obj, keypath, callback, options); - } - - // Batteries not included. - sightglass.adapters = {}; - - // Constructs a new keypath observer and kicks things off. - function Observer(obj, keypath, callback, options) { - this.options = options || {}; - this.options.adapters = this.options.adapters || {}; - this.obj = obj; - this.keypath = keypath; - this.callback = callback; - this.objectPath = []; - this.update = this.update.bind(this); - this.parse(); - - if (isObject(this.target = this.realize())) { - this.set(true, this.key, this.target, this.callback); - } - } - - // Tokenizes the provided keypath string into interface + path tokens for the - // observer to work with. - Observer.tokenize = function (keypath, interfaces, root) { - var tokens = []; - var current = { i: root, path: '' }; - var index, chr; - - for (index = 0; index < keypath.length; index++) { - chr = keypath.charAt(index); - - if (!! ~interfaces.indexOf(chr)) { - tokens.push(current); - current = { i: chr, path: '' }; - } else { - current.path += chr; - } - } - - tokens.push(current); - return tokens; - }; - - // Parses the keypath using the interfaces defined on the view. Sets variables - // for the tokenized keypath as well as the end key. - Observer.prototype.parse = function () { - var interfaces = this.interfaces(); - var root, path; - - if (!interfaces.length) { - error('Must define at least one adapter interface.'); - } - - if (!! ~interfaces.indexOf(this.keypath[0])) { - root = this.keypath[0]; - path = this.keypath.substr(1); - } else { - if (typeof (root = this.options.root || sightglass.root) === 'undefined') { - error('Must define a default root adapter.'); - } - - path = this.keypath; - } - - this.tokens = Observer.tokenize(path, interfaces, root); - this.key = this.tokens.pop(); - }; - - // Realizes the full keypath, attaching observers for every key and correcting - // old observers to any changed objects in the keypath. - Observer.prototype.realize = function () { - var current = this.obj; - var unreached = false; - var prev; - - this.tokens.forEach(function (token, index) { - if (isObject(current)) { - if (typeof this.objectPath[index] !== 'undefined') { - if (current !== (prev = this.objectPath[index])) { - this.set(false, token, prev, this.update); - this.set(true, token, current, this.update); - this.objectPath[index] = current; - } - } else { - this.set(true, token, current, this.update); - this.objectPath[index] = current; - } - - current = this.get(token, current); - } else { - if (unreached === false) { - unreached = index; - } - - if (prev = this.objectPath[index]) { - this.set(false, token, prev, this.update); - } - } - }, this); - - if (unreached !== false) { - this.objectPath.splice(unreached); - } - - return current; - }; - - // Updates the keypath. This is called when any intermediary key is changed. - Observer.prototype.update = function () { - var next, oldValue; - - if ((next = this.realize()) !== this.target) { - if (isObject(this.target)) { - this.set(false, this.key, this.target, this.callback); - } - - if (isObject(next)) { - this.set(true, this.key, next, this.callback); - } - - oldValue = this.value(); - this.target = next; - - if (this.value() !== oldValue) this.callback(); - } - }; - - // Reads the current end value of the observed keypath. Returns undefined if - // the full keypath is unreachable. - Observer.prototype.value = function () { - if (isObject(this.target)) { - return this.get(this.key, this.target); - } - }; - - // Sets the current end value of the observed keypath. Calling setValue when - // the full keypath is unreachable is a no-op. - Observer.prototype.setValue = function (value) { - if (isObject(this.target)) { - this.adapter(this.key).set(this.target, this.key.path, value); - } - }; - - // Gets the provided key on an object. - Observer.prototype.get = function (key, obj) { - return this.adapter(key).get(obj, key.path); - }; - - // Observes or unobserves a callback on the object using the provided key. - Observer.prototype.set = function (active, key, obj, callback) { - var action = active ? 'observe' : 'unobserve'; - this.adapter(key)[action](obj, key.path, callback); - }; - - // Returns an array of all unique adapter interfaces available. - Observer.prototype.interfaces = function () { - var interfaces = Object.keys(this.options.adapters); - - Object.keys(sightglass.adapters).forEach(function (i) { - if (! ~interfaces.indexOf(i)) { - interfaces.push(i); - } - }); - - return interfaces; - }; - - // Convenience function to grab the adapter for a specific key. - Observer.prototype.adapter = function (key) { - return this.options.adapters[key.i] || sightglass.adapters[key.i]; - }; - - // Unobserves the entire keypath. - Observer.prototype.unobserve = function () { - var obj; - - this.tokens.forEach(function (token, index) { - if (obj = this.objectPath[index]) { - this.set(false, token, obj, this.update); - } - }, this); - - if (isObject(this.target)) { - this.set(false, this.key, this.target, this.callback); - } - }; - - // Check if a value is an object than can be observed. - function isObject(obj) { - return (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' && obj !== null; - } - - // Error thrower. - function error(message) { - throw new Error('[sightglass] ' + message); - } - - // Export module for Node and the browser. - if (typeof module !== 'undefined' && module.exports) { - module.exports = sightglass; - } else if (true) { - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function () { - return this.sightglass = sightglass; - }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else { - this.sightglass = sightglass; - } - }).call(undefined); - }); - -/***/ }, -/* 2 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { - if (true) { - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, exports, __webpack_require__(3)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof exports !== "undefined") { - factory(module, exports, require('./constants')); - } else { - var mod = { - exports: {} - }; - factory(mod, mod.exports, global.constants); - global.rivets = mod.exports; - } - })(this, function (module, exports, _constants) { - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - - var rivets = { - // Global binders. - binders: {}, - - // Global components. - components: {}, - - // Global formatters. - formatters: {}, - - // Global sightglass adapters. - adapters: {}, - - // Default attribute prefix. - prefix: 'rv', - - // Default template delimiters. - templateDelimiters: ['{', '}'], - - // Default sightglass root interface. - rootInterface: '.', - - // Preload data by default. - preloadData: true, - - // Alias for index in rv-each binder - iterationAlias: function iterationAlias(modelName) { - return '%' + modelName + '%'; - }, - - // Default event handler. - handler: function handler(context, ev, binding) { - this.call(context, ev, binding.view.models); - }, - - // Merges an object literal into the corresponding global options. - configure: function configure() { - var _this = this; - - var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - - Object.keys(options).forEach(function (option) { - var value = options[option]; - - if (_constants.EXTENSIONS.indexOf(option) > -1) { - Object.keys(value).forEach(function (key) { - _this[option][key] = value[key]; - }); - } else { - _this[option] = value; - } - }); - } - }; - - exports.default = rivets; - module.exports = exports['default']; - }); - -/***/ }, -/* 3 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { - if (true) { - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [exports], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof exports !== "undefined") { - factory(exports); - } else { - var mod = { - exports: {} - }; - factory(mod.exports); - global.constants = mod.exports; - } - })(this, function (exports) { - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - var OPTIONS = exports.OPTIONS = ['prefix', 'templateDelimiters', 'rootInterface', 'preloadData', 'handler']; - - var EXTENSIONS = exports.EXTENSIONS = ['binders', 'formatters', 'components', 'adapters']; - }); - -/***/ }, -/* 4 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { - if (true) { - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, exports, __webpack_require__(2), __webpack_require__(3), __webpack_require__(5), __webpack_require__(6)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof exports !== "undefined") { - factory(module, exports, require('./rivets'), require('./constants'), require('./bindings'), require('./parsers')); - } else { - var mod = { - exports: {} - }; - factory(mod, mod.exports, global.rivets, global.constants, global.bindings, global.parsers); - global.view = mod.exports; - } - })(this, function (module, exports, _rivets, _constants, _bindings, _parsers) { - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _rivets2 = _interopRequireDefault(_rivets); - - function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { - default: obj - }; - } - - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } - - var _createClass = function () { - function defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } - } - - return function (Constructor, protoProps, staticProps) { - if (protoProps) defineProperties(Constructor.prototype, protoProps); - if (staticProps) defineProperties(Constructor, staticProps); - return Constructor; - }; - }(); - - var defined = function defined(value) { - return value !== undefined && value !== null; - }; - - // A collection of bindings built from a set of parent nodes. - - var View = function () { - // The DOM elements and the model objects for binding are passed into the - // constructor along with any local options that should be used throughout the - // context of the view and it's bindings. - - function View(els, models) { - var _this = this; - - var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - - _classCallCheck(this, View); - - if (els.jquery || els instanceof Array) { - this.els = els; - } else { - this.els = [els]; - } - - this.models = models; - - _constants.EXTENSIONS.forEach(function (extensionType) { - _this[extensionType] = {}; - - if (options[extensionType]) { - Object.keys(options[extensionType]).forEach(function (key) { - _this[extensionType][key] = options[extensionType][key]; - }); - } - - Object.keys(_rivets2.default[extensionType]).forEach(function (key) { - if (!defined(_this[extensionType][key])) { - _this[extensionType][key] = _rivets2.default[extensionType][key]; - } - }); - }); - - _constants.OPTIONS.forEach(function (option) { - _this[option] = defined(options[option]) ? options[option] : _rivets2.default[option]; - }); - - this.build(); - } - - _createClass(View, [{ - key: 'options', - value: function options() { - var _this2 = this; - - var options = {}; - - _constants.EXTENSIONS.concat(_constants.OPTIONS).forEach(function (option) { - options[option] = _this2[option]; - }); - - return options; - } - }, { - key: 'bindingRegExp', - value: function bindingRegExp() { - return new RegExp('^' + this.prefix + '-'); - } - }, { - key: 'buildBinding', - value: function buildBinding(binding, node, type, declaration) { - var pipes = declaration.match(/((?:'[^']*')*(?:(?:[^\|']+(?:'[^']*')*[^\|']*)+|[^\|]+))|^$/g).map(function (pipe) { - return pipe.trim(); - }); - - var context = pipes.shift().split('<').map(function (ctx) { - return ctx.trim(); - }); - - var keypath = context.shift(); - var dependencies = context.shift(); - var options = { formatters: pipes }; - - if (dependencies) { - options.dependencies = dependencies.split(/\s+/); - } - - this.bindings.push(new binding(this, node, type, keypath, options)); - } - }, { - key: 'build', - value: function build() { - var _this3 = this; - - this.bindings = []; - - var parse = function parse(node) { - var block = false; - - if (node.nodeType === 3) { - var delimiters = _this3.templateDelimiters; - - if (delimiters) { - var tokens = (0, _parsers.parseTemplate)(node.data, delimiters); - - if (tokens.length) { - if (!(tokens.length === 1 && tokens[0].type === 0)) { - tokens.forEach(function (token) { - var text = document.createTextNode(token.value); - node.parentNode.insertBefore(text, node); - - if (token.type === 1) { - _this3.buildBinding(_bindings.TextBinding, text, null, token.value); - } - }); - - node.parentNode.removeChild(node); - } - } - } - } else if (node.nodeType === 1) { - block = _this3.traverse(node); - } - - if (!block) { - Array.prototype.slice.call(node.childNodes).forEach(parse); - } - }; - - var elements = this.els, - i = void 0, - len = void 0; - for (i = 0, len = elements.length; i < len; i++) { - parse(elements[i]); - } - - this.bindings.sort(function (a, b) { - var aPriority = defined(a.binder) ? a.binder.priority || 0 : 0; - var bPriority = defined(b.binder) ? b.binder.priority || 0 : 0; - return bPriority - aPriority; - }); - } - }, { - key: 'traverse', - value: function traverse(node) { - var _this4 = this; - - var bindingRegExp = this.bindingRegExp(); - var block = node.nodeName === 'SCRIPT' || node.nodeName === 'STYLE'; - var attributes = null; - - Array.prototype.slice.call(node.attributes).forEach(function (attribute) { - if (bindingRegExp.test(attribute.name)) { - (function () { - var type = attribute.name.replace(bindingRegExp, ''); - var binder = _this4.binders[type]; - - if (!binder) { - Object.keys(_this4.binders).forEach(function (identifier) { - var value = _this4.binders[identifier]; - - if (identifier !== '*' && identifier.indexOf('*') > -1) { - var regexp = new RegExp('^' + identifier.replace(/\*/g, '.+') + '$'); - - if (regexp.test(type)) { - binder = value; - } - } - }); - } - - if (!defined(binder)) { - binder = _this4.binders['*']; - } - - if (binder.block) { - block = true; - attributes = [attribute]; - } - })(); - } - }); - - attributes = attributes || Array.prototype.slice.call(node.attributes); - - attributes.forEach(function (attribute) { - if (bindingRegExp.test(attribute.name)) { - var type = attribute.name.replace(bindingRegExp, ''); - _this4.buildBinding(_bindings.Binding, node, type, attribute.value); - } - }); - - if (!block) { - var type = node.nodeName.toLowerCase(); - - if (this.components[type] && !node._bound) { - this.bindings.push(new _bindings.ComponentBinding(this, node, type)); - block = true; - } - } - - return block; - } - }, { - key: 'select', - value: function select(fn) { - return this.bindings.filter(fn); - } - }, { - key: 'bind', - value: function bind() { - this.bindings.forEach(function (binding) { - binding.bind(); - }); - } - }, { - key: 'unbind', - value: function unbind() { - this.bindings.forEach(function (binding) { - binding.unbind(); - }); - } - }, { - key: 'sync', - value: function sync() { - this.bindings.forEach(function (binding) { - binding.sync(); - }); - } - }, { - key: 'publish', - value: function publish() { - var publishes = this.select(function (binding) { - if (defined(binding.binder)) { - return binding.binder.publishes; - } - }); - - publishes.forEach(function (binding) { - binding.publish(); - }); - } - }, { - key: 'update', - value: function update() { - var _this5 = this; - - var models = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - - Object.keys(models).forEach(function (key) { - _this5.models[key] = models[key]; - }); - - this.bindings.forEach(function (binding) { - if (defined(binding.update)) { - binding.update(models); - } - }); - } - }]); - - return View; - }(); - - exports.default = View; - module.exports = exports['default']; - }); - -/***/ }, -/* 5 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { - if (true) { - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [exports, __webpack_require__(2), __webpack_require__(6), __webpack_require__(7), __webpack_require__(3)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof exports !== "undefined") { - factory(exports, require('./rivets'), require('./parsers'), require('./util'), require('./constants')); - } else { - var mod = { - exports: {} - }; - factory(mod.exports, global.rivets, global.parsers, global.util, global.constants); - global.bindings = mod.exports; - } - })(this, function (exports, _rivets, _parsers, _util, _constants) { - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.TextBinding = exports.ComponentBinding = exports.Binding = undefined; - - var _rivets2 = _interopRequireDefault(_rivets); - - function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { - default: obj - }; - } - - var _get = function get(object, property, receiver) { - if (object === null) object = Function.prototype; - var desc = Object.getOwnPropertyDescriptor(object, property); - - if (desc === undefined) { - var parent = Object.getPrototypeOf(object); - - if (parent === null) { - return undefined; - } else { - return get(parent, property, receiver); - } - } else if ("value" in desc) { - return desc.value; - } else { - var getter = desc.get; - - if (getter === undefined) { - return undefined; - } - - return getter.call(receiver); - } - }; - - function _possibleConstructorReturn(self, call) { - if (!self) { - throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - } - - return call && (typeof call === "object" || typeof call === "function") ? call : self; - } - - function _inherits(subClass, superClass) { - if (typeof superClass !== "function" && superClass !== null) { - throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); - } - - subClass.prototype = Object.create(superClass && superClass.prototype, { - constructor: { - value: subClass, - enumerable: false, - writable: true, - configurable: true - } - }); - if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; - } - - function _toConsumableArray(arr) { - if (Array.isArray(arr)) { - for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { - arr2[i] = arr[i]; - } - - return arr2; - } else { - return Array.from(arr); - } - } - - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } - - var _createClass = function () { - function defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } - } - - return function (Constructor, protoProps, staticProps) { - if (protoProps) defineProperties(Constructor.prototype, protoProps); - if (staticProps) defineProperties(Constructor, staticProps); - return Constructor; - }; - }(); - - var defined = function defined(value) { - return value !== undefined && value !== null; - }; - - // A single binding between a model attribute and a DOM element. - - var Binding = exports.Binding = function () { - // All information about the binding is passed into the constructor; the - // containing view, the DOM node, the type of binding, the model object and the - // keypath at which to listen for changes. - - function Binding(view, el, type, keypath) { - var options = arguments.length <= 4 || arguments[4] === undefined ? {} : arguments[4]; - - _classCallCheck(this, Binding); - - this.view = view; - this.el = el; - this.type = type; - this.keypath = keypath; - this.options = options; - this.formatters = options.formatters || []; - this.dependencies = []; - this.formatterObservers = {}; - this.model = undefined; - this.setBinder(); - - this.bind = this.bind.bind(this); - this.unbind = this.unbind.bind(this); - this.sync = this.sync.bind(this); - this.publish = this.publish.bind(this); - } - - // Sets the binder to use when binding and syncing. - - - _createClass(Binding, [{ - key: 'setBinder', - value: function setBinder() { - var _this = this; - - this.binder = this.view.binders[this.type]; - - if (!this.binder) { - Object.keys(this.view.binders).forEach(function (identifier) { - var value = _this.view.binders[identifier]; - - if (identifier !== '*' && identifier.indexOf('*') > -1) { - var regexp = new RegExp('^' + identifier.replace(/\*/g, '.+') + '$'); - - if (regexp.test(_this.type)) { - _this.binder = value; - _this.args = new RegExp('^' + identifier.replace(/\*/g, '(.+)') + '$').exec(_this.type); - _this.args.shift(); - } - } - }); - } - - if (!defined(this.binder)) { - this.binder = this.view.binders['*']; - } - - if (this.binder instanceof Function) { - this.binder = { routine: this.binder }; - } - } - - // Observes the object keypath to run the provided callback. - - }, { - key: 'observe', - value: function observe(obj, keypath, callback) { - return _rivets2.default.sightglass(obj, keypath, callback, { - root: this.view.rootInterface, - adapters: this.view.adapters - }); - } - }, { - key: 'parseTarget', - value: function parseTarget() { - var token = (0, _parsers.parseType)(this.keypath); - - if (token.type === 0) { - this.value = token.value; - } else { - this.observer = this.observe(this.view.models, this.keypath, this.sync); - this.model = this.observer.target; - } - } - - // Applies all the current formatters to the supplied value and returns the - // formatted value. - - }, { - key: 'formattedValue', - value: function formattedValue(value) { - var _this2 = this; - - this.formatters.forEach(function (formatterStr, fi) { - var args = formatterStr.match(/[^\s']+|'([^']|'[^\s])*'|"([^"]|"[^\s])*"/g); - var id = args.shift(); - var formatter = _this2.view.formatters[id]; - var processedArgs = []; - - args = args.map(_parsers.parseType); - - args.forEach(function (arg, ai) { - if (arg.type === 0) { - processedArgs.push(arg.value); - } else { - if (!defined(_this2.formatterObservers[fi])) { - _this2.formatterObservers[fi] = {}; - } - - var observer = _this2.formatterObservers[fi][ai]; - - if (!observer) { - observer = _this2.observe(_this2.view.models, arg.value, _this2.sync); - _this2.formatterObservers[fi][ai] = observer; - } - - processedArgs.push(observer.value()); - } - }); - - if (formatter && formatter.read instanceof Function) { - value = formatter.read.apply(formatter, [value].concat(processedArgs)); - } else if (formatter instanceof Function) { - value = formatter.apply(undefined, [value].concat(processedArgs)); - } - }); - - return value; - } - - // Returns an event handler for the binding around the supplied function. - - }, { - key: 'eventHandler', - value: function eventHandler(fn) { - var binding = this; - var handler = binding.view.handler; - - return function (ev) { - handler.call(fn, this, ev, binding); - }; - } - - // Sets the value for the binding. This Basically just runs the binding routine - // with the suplied value formatted. - - }, { - key: 'set', - value: function set(value) { - if (value instanceof Function && !this.binder.function) { - value = this.formattedValue(value.call(this.model)); - } else { - value = this.formattedValue(value); - } - - if (this.binder.routine) { - this.binder.routine.call(this, this.el, value); - } - } - - // Syncs up the view binding with the model. - - }, { - key: 'sync', - value: function sync() { - var _this3 = this; - - if (this.observer) { - if (this.model !== this.observer.target) { - var deps = this.options.dependencies; - - this.dependencies.forEach(function (observer) { - observer.unobserve(); - }); - - this.dependencies = []; - this.model = this.observer.target; - - if (defined(this.model) && deps && deps.length) { - deps.forEach(function (dependency) { - var observer = _this3.observe(_this3.model, dependency, _this3.sync); - _this3.dependencies.push(observer); - }); - } - } - - this.set(this.observer.value()); - } else { - this.set(this.value); - } - } - - // Publishes the value currently set on the input element back to the model. - - }, { - key: 'publish', - value: function publish() { - var _this4 = this; - - if (this.observer) { - (function () { - var value = _this4.getValue(_this4.el); - - _this4.formatters.slice(0).reverse().forEach(function (formatter) { - var args = formatter.split(/\s+/); - var id = args.shift(); - var f = _this4.view.formatters[id]; - - if (defined(f) && f.publish) { - value = f.publish.apply(f, [value].concat(_toConsumableArray(args))); - } - }); - - _this4.observer.setValue(value); - })(); - } - } - - // Subscribes to the model for changes at the specified keypath. Bi-directional - // routines will also listen for changes on the element to propagate them back - // to the model. - - }, { - key: 'bind', - value: function bind() { - var _this5 = this; - - this.parseTarget(); - - if (defined(this.binder.bind)) { - this.binder.bind.call(this, this.el); - } - - if (defined(this.model) && defined(this.options.dependencies)) { - this.options.dependencies.forEach(function (dependency) { - var observer = _this5.observe(_this5.model, dependency, _this5.sync); - _this5.dependencies.push(observer); - }); - } - - if (this.view.preloadData) { - this.sync(); - } - } - - // Unsubscribes from the model and the element. - - }, { - key: 'unbind', - value: function unbind() { - var _this6 = this; - - if (defined(this.binder.unbind)) { - this.binder.unbind.call(this, this.el); - } - - if (defined(this.observer)) { - this.observer.unobserve(); - } - - this.dependencies.forEach(function (observer) { - observer.unobserve(); - }); - - this.dependencies = []; - - Object.keys(this.formatterObservers).forEach(function (fi) { - var args = _this6.formatterObservers[fi]; - - Object.keys(args).forEach(function (ai) { - args[ai].unobserve(); - }); - }); - - this.formatterObservers = {}; - } - - // Updates the binding's model from what is currently set on the view. Unbinds - // the old model first and then re-binds with the new model. - - }, { - key: 'update', - value: function update() { - var models = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - - if (defined(this.observer)) { - this.model = this.observer.target; - } - - if (defined(this.binder.update)) { - this.binder.update.call(this, models); - } - } - - // Returns elements value - - }, { - key: 'getValue', - value: function getValue(el) { - if (this.binder && defined(this.binder.getValue)) { - return this.binder.getValue.call(this, el); - } else { - return (0, _util.getInputValue)(el); - } - } - }]); - - return Binding; - }(); - - var ComponentBinding = exports.ComponentBinding = function (_Binding) { - _inherits(ComponentBinding, _Binding); - - // Initializes a component binding for the specified view. The raw component - // element is passed in along with the component type. Attributes and scope - // inflections are determined based on the components defined attributes. - - function ComponentBinding(view, el, type) { - _classCallCheck(this, ComponentBinding); - - var _this7 = _possibleConstructorReturn(this, Object.getPrototypeOf(ComponentBinding).call(this, view, el, type)); - - _this7.component = _this7.view.components[_this7.type]; - _this7.static = {}; - _this7.observers = {}; - _this7.upstreamObservers = {}; - - var bindingRegExp = view.bindingRegExp(); - - if (_this7.el.attributes) { - _this7.el.attributes.forEach(function (attribute) { - if (!bindingRegExp.test(attribute.name)) { - var propertyName = _this7.camelCase(attribute.name); - var stat = _this7.component.static; - - if (stat && stat.indexOf(propertyName) > -1) { - _this7.static[propertyName] = attribute.value; - } else { - _this7.observers[propertyName] = attribute.value; - } - } - }); - } - return _this7; - } - - // Intercepts `Rivets.Binding::sync` since component bindings are not bound to - // a particular model to update it's value. - - - _createClass(ComponentBinding, [{ - key: 'sync', - value: function sync() {} - - // Intercepts `Rivets.Binding::update` since component bindings are not bound - // to a particular model to update it's value. - - }, { - key: 'update', - value: function update() {} - - // Intercepts `Rivets.Binding::publish` since component bindings are not bound - // to a particular model to update it's value. - - }, { - key: 'publish', - value: function publish() {} - - // Returns an object map using the component's scope inflections. - - }, { - key: 'locals', - value: function locals() { - var _this8 = this; - - var result = {}; - - Object.keys(this.static).forEach(function (key) { - result[key] = _this8.static[key]; - }); - - Object.keys(this.observers).forEach(function (key) { - result[key] = _this8.observers[key].value(); - }); - - return result; - } - - // Returns a camel-cased version of the string. Used when translating an - // element's attribute name into a property name for the component's scope. - - }, { - key: 'camelCase', - value: function camelCase(string) { - return string.replace(/-([a-z])/g, function (grouped) { - grouped[1].toUpperCase(); - }); - } - - // Intercepts `Rivets.Binding::bind` to build `@componentView` with a localized - // map of models from the root view. Bind `@componentView` on subsequent calls. - - }, { - key: 'bind', - value: function bind() { - var _this9 = this; - - if (!this.bound) { - Object.keys(this.observers).forEach(function (key) { - var keypath = _this9.observers[key]; - - _this9.observers[key] = _this9.observe(_this9.view.models, keypath, function (key) { - return function () { - _this9.componentView.models[key] = _this9.observers[key].value(); - }; - }.call(_this9, key)); - }); - - this.bound = true; - } - - if (defined(this.componentView)) { - this.componentView.bind(); - } else { - (function () { - _this9.el.innerHTML = _this9.component.template.call(_this9); - var scope = _this9.component.initialize.call(_this9, _this9.el, _this9.locals()); - _this9.el._bound = true; - - var options = {}; - - _constants.EXTENSIONS.forEach(function (extensionType) { - options[extensionType] = {}; - - if (_this9.component[extensionType]) { - Object.keys(_this9.component[extensionType]).forEach(function (key) { - options[extensionType][key] = _this9.component[extensionType][key]; - }); - } - - Object.keys(_this9.view[extensionType]).forEach(function (key) { - if (!defined(options[extensionType][key])) { - options[extensionType][key] = _this9.view[extensionType][key]; - } - }); - }); - - _constants.OPTIONS.forEach(function (option) { - if (defined(_this9.component[option])) { - options[option] = _this9.component[option]; - } else { - options[option] = _this9.view[option]; - } - }); - - _this9.componentView = new View(_this9.el, scope, options); - _this9.componentView.bind(); - - Object.keys(_this9.observers).forEach(function (key) { - var observer = _this9.observers[key]; - var models = _this9.componentView.models; - - var upstream = _this9.observe(models, key, function (key, observer) { - return function () { - observer.setValue(_this9.componentView.models[key]); - }; - }.call(_this9, key, observer)); - - _this9.upstreamObservers[key] = upstream; - }); - })(); - } - } - - // Intercept `Rivets.Binding::unbind` to be called on `@componentView`. - - }, { - key: 'unbind', - value: function unbind() { - var _this10 = this; - - Object.keys(this.upstreamObservers).forEach(function (key) { - _this10.upstreamObservers[key].unobserve(); - }); - - Object.keys(this.observers).forEach(function (key) { - _this10.observers[key].unobserve(); - }); - - if (defined(this.componentView)) { - this.componentView.unbind.call(this); - } - } - }]); - - return ComponentBinding; - }(Binding); - - var TextBinding = exports.TextBinding = function (_Binding2) { - _inherits(TextBinding, _Binding2); - - // Initializes a text binding for the specified view and text node. - - function TextBinding(view, el, type, keypath) { - var options = arguments.length <= 4 || arguments[4] === undefined ? {} : arguments[4]; - - _classCallCheck(this, TextBinding); - - var _this11 = _possibleConstructorReturn(this, Object.getPrototypeOf(TextBinding).call(this, view, el, type)); - - _this11.keypath = keypath; - _this11.options = options; - _this11.formatters = _this11.options.formatters || []; - _this11.dependencies = []; - _this11.formatterObservers = {}; - - _this11.binder = { - routine: function routine(node, value) { - node.data = defined(value) ? value : ''; - } - }; - - _this11.sync = _this11.sync.bind(_this11); - return _this11; - } - - // Wrap the call to `sync` to avoid function context issues. - - - _createClass(TextBinding, [{ - key: 'sync', - value: function sync() { - _get(Object.getPrototypeOf(TextBinding.prototype), 'sync', this).call(this); - } - }]); - - return TextBinding; - }(Binding); - }); - -/***/ }, -/* 6 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { - if (true) { - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [exports], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof exports !== "undefined") { - factory(exports); - } else { - var mod = { - exports: {} - }; - factory(mod.exports); - global.parsers = mod.exports; - } - })(this, function (exports) { - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.parseType = parseType; - exports.parseTemplate = parseTemplate; - var PRIMITIVE = 0; - var KEYPATH = 1; - var TEXT = 0; - var BINDING = 1; - - // Parser and tokenizer for getting the type and value from a string. - function parseType(string) { - var type = PRIMITIVE; - var value = string; - - if (/^'.*'$|^".*"$/.test(string)) { - value = string.slice(1, -1); - } else if (string === 'true') { - value = true; - } else if (string === 'false') { - value = false; - } else if (string === 'null') { - value = null; - } else if (string === 'undefined') { - value = undefined; - } else if (isNaN(Number(string)) === false) { - value = Number(string); - } else { - type = KEYPATH; - } - - return { type: type, value: value }; - } - - // Template parser and tokenizer for mustache-style text content bindings. - // Parses the template and returns a set of tokens, separating static portions - // of text from binding declarations. - function parseTemplate(template, delimiters) { - var tokens = []; - var length = template.length; - var index = 0; - var lastIndex = 0; - - while (lastIndex < length) { - index = template.indexOf(delimiters[0], lastIndex); - - if (index < 0) { - tokens.push({ - type: TEXT, - value: template.slice(lastIndex) - }); - - break; - } else { - if (index > 0 && lastIndex < index) { - tokens.push({ - type: TEXT, - value: template.slice(lastIndex, index) - }); - } - - lastIndex = index + delimiters[0].length; - index = template.indexOf(delimiters[1], lastIndex); - - if (index < 0) { - var substring = template.slice(lastIndex - delimiters[1].length); - var lastToken = tokens[tokens.length - 1]; - - if (lastToken && lastToken.type === TEXT) { - lastToken.value += substring; - } else { - tokens.push({ - type: TEXT, - value: substring - }); - } - - break; - } - - var value = template.slice(lastIndex, index).trim(); - - tokens.push({ - type: BINDING, - value: value - }); - - lastIndex = index + delimiters[1].length; - } - } - - return tokens; - } - }); - -/***/ }, -/* 7 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { - if (true) { - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [exports], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof exports !== "undefined") { - factory(exports); - } else { - var mod = { - exports: {} - }; - factory(mod.exports); - global.util = mod.exports; - } - })(this, function (exports) { - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.bindEvent = bindEvent; - exports.unbindEvent = unbindEvent; - exports.getInputValue = getInputValue; - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; - }; - - var $ = window.jQuery || window.$; - - function bindEvent(el, event, handler) { - if ($) { - $(el).on(event, handler); - } else { - el.addEventListener(event, handler, false); - } - } - - function unbindEvent(el, event, handler) { - if ($) { - $(el).off(event, handler); - } else { - el.removeEventListener(event, handler, false); - } - } - - function getInputValue(el) { - if ($) { - var $el = $(el); - - if ($el.attr('type') === 'checkbox') { - return $el.is(':checked'); - } else { - return $el.val(); - } - } else { - if (el.type === 'checkbox') { - return el.checked; - } else if (el.type === 'select-multiple') { - var _ret = function () { - var results = []; - - el.options.forEach(function (option) { - if (option.selected) { - results.push(option.value); - } - }); - - return { - v: results - }; - }(); - - if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; - } else { - return el.value; - } - } - } - }); - -/***/ }, -/* 8 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { - if (true) { - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, exports], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof exports !== "undefined") { - factory(module, exports); - } else { - var mod = { - exports: {} - }; - factory(mod, mod.exports); - global.adapter = mod.exports; - } - })(this, function (module, exports) { - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - // The default `.` adapter thats comes with Rivets.js. Allows subscribing to - // properties on plain objects, implemented in ES5 natives using - // `Object.defineProperty`. - - var defined = function defined(value) { - return value !== undefined && value !== null; - }; - - var ARRAY_METHODS = ['push', 'pop', 'shift', 'unshift', 'sort', 'reverse', 'splice']; - - var adapter = { - id: '_rv', - counter: 0, - weakmap: {}, - - weakReference: function weakReference(obj) { - if (!obj.hasOwnProperty(this.id)) { - var id = this.counter++; - - Object.defineProperty(obj, this.id, { - value: id - }); - } - - if (!this.weakmap[obj[this.id]]) { - this.weakmap[obj[this.id]] = { - callbacks: {} - }; - } - - return this.weakmap[obj[this.id]]; - }, - - cleanupWeakReference: function cleanupWeakReference(ref, id) { - if (!Object.keys(ref.callbacks).length) { - if (!(ref.pointers && Object.keys(ref.pointers).length)) { - delete this.weakmap[id]; - } - } - }, - - stubFunction: function stubFunction(obj, fn) { - var original = obj[fn]; - var map = this.weakReference(obj); - var weakmap = this.weakmap; - - obj[fn] = function () { - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - var response = original.apply(obj, args); - - Object.keys(map.pointers).forEach(function (r) { - var k = map.pointers[r]; - - if (defined(weakmap[r])) { - if (weakmap[r].callbacks[k] instanceof Array) { - weakmap[r].callbacks[k].forEach(function (callback) { - callback(); - }); - } - } - }); - - return response; - }; - }, - - observeMutations: function observeMutations(obj, ref, keypath) { - var _this = this; - - if (obj instanceof Array) { - var map = this.weakReference(obj); - - if (!defined(map.pointers)) { - map.pointers = {}; - - ARRAY_METHODS.forEach(function (fn) { - _this.stubFunction(obj, fn); - }); - } - - if (!defined(map.pointers[ref])) { - map.pointers[ref] = []; - } - - if (map.pointers[ref].indexOf(keypath) === -1) { - map.pointers[ref].push(keypath); - } - } - }, - - unobserveMutations: function unobserveMutations(obj, ref, keypath) { - if (obj instanceof Array && defined(obj[this.id])) { - var map = this.weakmap[obj[this.id]]; - - if (map) { - var pointers = map.pointers[ref]; - - if (pointers) { - var idx = pointers.indexOf(keypath); - - if (idx > -1) { - pointers.splice(idx, 1); - } - - if (!pointers.length) { - delete map.pointers[ref]; - } - - this.cleanupWeakReference(map, obj[this.id]); - } - } - } - }, - - observe: function observe(obj, keypath, callback) { - var _this2 = this; - - var callbacks = this.weakReference(obj).callbacks; - - if (!defined(callbacks[keypath])) { - callbacks[keypath] = []; - var desc = Object.getOwnPropertyDescriptor(obj, keypath); - - if (!(desc && (desc.get || desc.set))) { - (function () { - var value = obj[keypath]; - - Object.defineProperty(obj, keypath, { - enumerable: true, - - get: function get() { - return value; - }, - - set: function set(newValue) { - if (newValue !== value) { - _this2.unobserveMutations(value, obj[_this2.id], keypath); - value = newValue; - var map = _this2.weakmap[obj[_this2.id]]; - - if (map) { - var _callbacks = map.callbacks; - - if (_callbacks[keypath]) { - _callbacks[keypath].forEach(function (cb) { - cb(); - }); - } - - _this2.observeMutations(newValue, obj[_this2.id], keypath); - } - } - } - }); - })(); - } - } - - if (callbacks[keypath].indexOf(callback) === -1) { - callbacks[keypath].push(callback); - } - - this.observeMutations(obj[keypath], obj[this.id], keypath); - }, - - unobserve: function unobserve(obj, keypath, callback) { - var map = this.weakmap[obj[this.id]]; - - if (map) { - var callbacks = map.callbacks[keypath]; - - if (callbacks) { - var idx = callbacks.indexOf(callback); - - if (idx > -1) { - callbacks.splice(idx, 1); - - if (!callbacks.length) { - delete map.callbacks[keypath]; - } - } - - this.unobserveMutations(obj[keypath], obj[this.id], keypath); - this.cleanupWeakReference(map, obj[this.id]); - } - } - }, - - get: function get(obj, keypath) { - return obj[keypath]; - }, - - set: function set(obj, keypath, value) { - obj[keypath] = value; - } - }; - - exports.default = adapter; - module.exports = exports['default']; - }); - -/***/ }, -/* 9 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { - if (true) { - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, exports, __webpack_require__(2), __webpack_require__(7)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else if (typeof exports !== "undefined") { - factory(module, exports, require('./rivets'), require('./util')); - } else { - var mod = { - exports: {} - }; - factory(mod, mod.exports, global.rivets, global.util); - global.binders = mod.exports; - } - })(this, function (module, exports, _rivets, _util) { - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _rivets2 = _interopRequireDefault(_rivets); - - function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { - default: obj - }; - } - - var _templateObject = _taggedTemplateLiteral([' rivets: ', ' ', ' '], [' rivets: ', ' ', ' ']), - _templateObject2 = _taggedTemplateLiteral([' rivets: ', ' '], [' rivets: ', ' ']); - - function _taggedTemplateLiteral(strings, raw) { - return Object.freeze(Object.defineProperties(strings, { - raw: { - value: Object.freeze(raw) - } - })); - } - - var CHANGE_EVENT = 'change'; - - var defined = function defined(value) { - return value !== undefined && value !== null; - }; - - var getString = function getString(value) { - return defined(value) ? value.toString() : undefined; - }; - - var times = function times(n, cb) { - for (var i = 0; i < n; i++) { - cb(); - } - }; - - var binders = { - // Sets the element's text value. - text: function text(el, value) { - el.textContent = defined(value) ? value : ''; - }, - - // Sets the element's HTML content. - html: function html(el, value) { - el.innerHTML = defined(value) ? value : ''; - }, - - // Shows the element when value is true. - show: function show(el, value) { - el.style.display = value ? '' : 'none'; - }, - - // Hides the element when value is true (negated version of `show` binder). - hide: function hide(el, value) { - el.style.display = value ? 'none' : ''; - }, - - // Enables the element when value is true. - enabled: function enabled(el, value) { - el.disabled = !value; - }, - - // Disables the element when value is true (negated version of `enabled` binder). - disabled: function disabled(el, value) { - el.disabled = !!value; - }, - - // Checks a checkbox or radio input when the value is true. Also sets the model - // property when the input is checked or unchecked (two-way binder). - checked: { - publishes: true, - priority: 2000, - - bind: function bind(el) { - (0, _util.bindEvent)(el, CHANGE_EVENT, this.publish); - }, - - unbind: function unbind(el) { - (0, _util.unbindEvent)(el, CHANGE_EVENT, this.publish); - }, - - routine: function routine(el, value) { - if (el.type === 'radio') { - el.checked = getString(el.value) === getString(value); - } else { - el.checked = !!value; - } - } - }, - - // Unchecks a checkbox or radio input when the value is true (negated version of - // `checked` binder). Also sets the model property when the input is checked or - // unchecked (two-way binder). - unchecked: { - publishes: true, - priority: 2000, - - bind: function bind(el) { - (0, _util.bindEvent)(el, CHANGE_EVENT, this.publish); - }, - - unbind: function unbind(el) { - (0, _util.unbindEvent)(el, CHANGE_EVENT, this.publish); - }, - - routine: function routine(el, value) { - if (el.type === 'radio') { - el.checked = getString(el.value) !== getString(value); - } else { - el.checked = !value; - } - } - }, - - // Sets the element's value. Also sets the model property when the input changes - // (two-way binder). - value: { - publishes: true, - priority: 3000, - - bind: function bind(el) { - if (!(el.tagName === 'INPUT' && el.type === 'radio')) { - this.event = el.tagName === 'SELECT' ? 'change' : 'input'; - - (0, _util.bindEvent)(el, this.event, this.publish); - } - }, - - unbind: function unbind(el) { - if (!(el.tagName === 'INPUT' && el.type === 'radio')) { - (0, _util.unbindEvent)(el, this.event, this.publish); - } - }, - - routine: function routine(el, value) { - if (el.tagName === 'INPUT' && el.type === 'radio') { - el.setAttribute('value', value); - } else if (window.jQuery) { - el = jQuery(el); - - if (getString(value) !== getString(el.val())) { - el.val(defined(value) ? value : ''); - } - } else { - if (el.type === 'select-multiple') { - if (value instanceof Array) { - Array.from(el.options).forEach(function (option) { - option.selected = value.indexOf(option.value) > -1; - }); - } - } else if (getString(value) !== getString(el.value)) { - el.value = defined(value) ? value : ''; - } - } - } - }, - - // Inserts and binds the element and it's child nodes into the DOM when true. - if: { - block: true, - priority: 4000, - - bind: function bind(el) { - if (!defined(this.marker)) { - var attr = [this.view.prefix, this.type].join('-').replace('--', '-'); - var declaration = el.getAttribute(attr); - - this.marker = document.createComment(_templateObject, this.type, declaration); - this.bound = false; - - el.removeAttribute(attr); - el.parentNode.insertBefore(this.marker, el); - el.parentNode.removeChild(el); - } - }, - - unbind: function unbind() { - if (defined(this.nested)) { - this.nested.unbind(); - this.bound = false; - } - }, - - routine: function routine(el, value) { - var _this = this; - - if (!!value === !this.bound) { - if (value) { - (function () { - var models = {}; - - Object.keys(_this.view.models).forEach(function (key) { - models[key] = _this.view.models[key]; - }); - - if (defined(_this.nested)) { - _this.nested.bind(); - } else { - _this.nested = _rivets2.default.bind(el, models, _this.view.options()); - } - - _this.marker.parentNode.insertBefore(el, _this.marker.nextSibling); - _this.bound = true; - })(); - } else { - el.parentNode.removeChild(el); - this.nested.unbind(); - this.bound = false; - } - } - }, - - update: function update(models) { - if (defined(this.nested)) { - this.nested.update(models); - } - } - }, - - // Removes and unbinds the element and it's child nodes into the DOM when true - // (negated version of `if` binder). - unless: { - block: true, - priority: 4000, - - bind: function bind(el) { - _rivets2.default.binders.if.bind.call(this, el); - }, - - unbind: function unbind() { - _rivets2.default.binders.if.unbind.call(this); - }, - - routine: function routine(el, value) { - _rivets2.default.binders.if.routine.call(this, el, !value); - }, - - update: function update(models) { - _rivets2.default.binders.if.update.call(this, models); - } - }, - - // Binds an event handler on the element. - 'on-*': { - function: true, - priority: 1000, - - unbind: function unbind(el) { - if (defined(this.handler)) { - (0, _util.unbindEvent)(el, this.args[0], this.handler); - } - }, - - routine: function routine(el, value) { - if (defined(this.handler)) { - (0, _util.unbindEvent)(el, this.args[0], this.handler); - } - - this.handler = this.eventHandler(value); - (0, _util.bindEvent)(el, this.args[0], this.handler); - } - }, - - // Appends bound instances of the element in place for each item in the array. - 'each-*': { - block: true, - priority: 4000, - - bind: function bind(el) { - if (!defined(this.marker)) { - var attr = [this.view.prefix, this.type].join('-').replace('--', '-'); - this.marker = document.createComment(_templateObject2, this.type); - this.iterated = []; - - el.removeAttribute(attr); - el.parentNode.insertBefore(this.marker, el); - el.parentNode.removeChild(el); - } else { - this.iterated.forEach(function (view) { - view.bind(); - }); - } - }, - - unbind: function unbind(el) { - if (defined(this.iterated)) { - this.iterated.forEach(function (view) { - view.unbind(); - }); - } - }, - - routine: function routine(el, _collection) { - var _this2 = this; - - var modelName = this.args[0]; - var collection = _collection || []; - - if (this.iterated.length > collection.length) { - times(this.iterated.length - collection.length, function () { - var view = _this2.iterated.pop(); - view.unbind(); - _this2.marker.parentNode.removeChild(view.els[0]); - }); - } - - collection.forEach(function (model, index) { - var data = { index: index }; - data[_rivets2.default.iterationAlias(modelName)] = index; - data[modelName] = model; - - if (!defined(_this2.iterated[index])) { - Object.keys(_this2.view.models).forEach(function (key) { - if (!defined(data[key])) { - data[key] = _this2.view.models[key]; - } - }); - - var previous = _this2.marker; - - if (_this2.iterated.length) { - previous = _this2.iterated[_this2.iterated.length - 1].els[0]; - } - - var options = _this2.view.options(); - options.preloadData = true; - - var template = el.cloneNode(true); - var view = _rivets2.default.bind(template, data, options); - _this2.iterated.push(view); - _this2.marker.parentNode.insertBefore(template, previous.nextSibling); - } else if (_this2.iterated[index].models[modelName] !== model) { - _this2.iterated[index].update(data); - } - }); - - if (el.nodeName === 'OPTION') { - this.view.bindings.forEach(function (binding) { - if (binding.el === _this2.marker.parentNode && binding.type === 'value') { - binding.sync(); - } - }); - } - }, - - update: function update(models) { - var _this3 = this; - - var data = {}; - - Object.keys(models).forEach(function (key) { - if (key !== _this3.args[0]) { - data[key] = models[key]; - } - }); - - this.iterated.forEach(function (view) { - view.update(data); - }); - } - }, - - // Adds or removes the class from the element when value is true or false. - 'class-*': function _class(el, value) { - var elClass = ' ' + el.className + ' '; - - if (!value === elClass.indexOf(' ' + this.args[0] + ' ') > -1) { - if (value) { - el.className = el.className + ' ' + this.args[0]; - } else { - el.className = elClass.replace(' ' + this.args[0] + ' ', ' ').trim(); - } - } - }, - - // Sets the attribute on the element. If no binder above is matched it will fall - // back to using this binder. - '*': function _(el, value) { - if (defined(value)) { - el.setAttribute(this.type, value); - } else { - el.removeAttribute(this.type); - } - } - }; - - exports.default = binders; - module.exports = exports['default']; - }); - -/***/ } -/******/ ]) -}); -; \ No newline at end of file diff --git a/dist/rivets.bundled.min.js b/dist/rivets.bundled.min.js deleted file mode 100755 index 8365daa29..000000000 --- a/dist/rivets.bundled.min.js +++ /dev/null @@ -1,6 +0,0 @@ -// Rivets.js + Sightglass.js -// version: 0.8.1 -// author: Michael Richards -// license: MIT -(function(){function t(t,i,s,h){return new e(t,i,s,h)}function e(t,e,s,h){this.options=h||{},this.options.adapters=this.options.adapters||{},this.obj=t,this.keypath=e,this.callback=s,this.objectPath=[],this.parse(),i(this.target=this.realize())&&this.set(!0,this.key,this.target,this.callback)}function i(t){return"object"==typeof t&&null!==t}function s(t){throw new Error("[sightglass] "+t)}t.adapters={},e.tokenize=function(t,e,i){var s,h,a=[],o={i:i,path:""};for(s=0;se;e++)if(e in this&&this[e]===t)return e;return-1};t={options:["prefix","templateDelimiters","rootInterface","preloadData","handler"],extensions:["binders","formatters","components","adapters"],"public":{binders:{},components:{},formatters:{},adapters:{},prefix:"rv",templateDelimiters:["{","}"],rootInterface:".",preloadData:!0,handler:function(t,e,i){return this.call(t,e,i.view.models)},configure:function(e){var i,n,r,s;null==e&&(e={});for(r in e)if(s=e[r],"binders"===r||"components"===r||"formatters"===r||"adapters"===r)for(n in s)i=s[n],t[r][n]=i;else t["public"][r]=s},bind:function(e,i,n){var r;return null==i&&(i={}),null==n&&(n={}),r=new t.View(e,i,n),r.bind(),r},init:function(e,i,n){var r,s;return null==n&&(n={}),null==i&&(i=document.createElement("div")),e=t["public"].components[e],i.innerHTML=e.template.call(this,i),r=e.initialize.call(this,i,n),s=new t.View(i,r),s.bind(),s}}},window.jQuery||window.$?(n="on"in jQuery.prototype?["on","off"]:["bind","unbind"],e=n[0],i=n[1],t.Util={bindEvent:function(t,i,n){return jQuery(t)[e](i,n)},unbindEvent:function(t,e,n){return jQuery(t)[i](e,n)},getInputValue:function(t){var e;return e=jQuery(t),"checkbox"===e.attr("type")?e.is(":checked"):e.val()}}):t.Util={bindEvent:function(){return"addEventListener"in window?function(t,e,i){return t.addEventListener(e,i,!1)}:function(t,e,i){return t.attachEvent("on"+e,i)}}(),unbindEvent:function(){return"removeEventListener"in window?function(t,e,i){return t.removeEventListener(e,i,!1)}:function(t,e,i){return t.detachEvent("on"+e,i)}}(),getInputValue:function(t){var e,i,n,r;if("checkbox"===t.type)return t.checked;if("select-multiple"===t.type){for(r=[],i=0,n=t.length;n>i;i++)e=t[i],e.selected&&r.push(e.value);return r}return t.value}},t.TypeParser=function(){function t(){}return t.types={primitive:0,keypath:1},t.parse=function(t){return/^'.*'$|^".*"$/.test(t)?{type:this.types.primitive,value:t.slice(1,-1)}:"true"===t?{type:this.types.primitive,value:!0}:"false"===t?{type:this.types.primitive,value:!1}:"null"===t?{type:this.types.primitive,value:null}:"undefined"===t?{type:this.types.primitive,value:void 0}:isNaN(Number(t))===!1?{type:this.types.primitive,value:Number(t)}:{type:this.types.keypath,value:t}},t}(),t.TextTemplateParser=function(){function t(){}return t.types={text:0,binding:1},t.parse=function(t,e){var i,n,r,s,o,u,l;for(u=[],s=t.length,i=0,n=0;s>n;){if(i=t.indexOf(e[0],n),0>i){u.push({type:this.types.text,value:t.slice(n)});break}if(i>0&&i>n&&u.push({type:this.types.text,value:t.slice(n,i)}),n=i+e[0].length,i=t.indexOf(e[1],n),0>i){o=t.slice(n-e[1].length),r=u[u.length-1],(null!=r?r.type:void 0)===this.types.text?r.value+=o:u.push({type:this.types.text,value:o});break}l=t.slice(n,i).trim(),u.push({type:this.types.binding,value:l}),n=i+e[1].length}return u},t}(),t.View=function(){function e(e,i,n){var s,o,u,l,h,a,p,d,c,f,b,v,y;for(this.els=e,this.models=i,null==n&&(n={}),this.update=r(this.update,this),this.publish=r(this.publish,this),this.sync=r(this.sync,this),this.unbind=r(this.unbind,this),this.bind=r(this.bind,this),this.select=r(this.select,this),this.traverse=r(this.traverse,this),this.build=r(this.build,this),this.buildBinding=r(this.buildBinding,this),this.bindingRegExp=r(this.bindingRegExp,this),this.options=r(this.options,this),this.els.jquery||this.els instanceof Array||(this.els=[this.els]),c=t.extensions,h=0,p=c.length;p>h;h++){if(o=c[h],this[o]={},n[o]){f=n[o];for(s in f)u=f[s],this[o][s]=u}b=t["public"][o];for(s in b)u=b[s],null==(l=this[o])[s]&&(l[s]=u)}for(v=t.options,a=0,d=v.length;d>a;a++)o=v[a],this[o]=null!=(y=n[o])?y:t["public"][o];this.build()}return e.prototype.options=function(){var e,i,n,r,s;for(i={},s=t.extensions.concat(t.options),n=0,r=s.length;r>n;n++)e=s[n],i[e]=this[e];return i},e.prototype.bindingRegExp=function(){return new RegExp("^"+this.prefix+"-")},e.prototype.buildBinding=function(e,i,n,r){var s,o,u,l,h,a,p;return h={},p=function(){var t,e,i,n;for(i=r.split("|"),n=[],t=0,e=i.length;e>t;t++)a=i[t],n.push(a.trim());return n}(),s=function(){var t,e,i,n;for(i=p.shift().split("<"),n=[],t=0,e=i.length;e>t;t++)o=i[t],n.push(o.trim());return n}(),l=s.shift(),h.formatters=p,(u=s.shift())&&(h.dependencies=u.split(/\s+/)),this.bindings.push(new t[e](this,i,n,l,h))},e.prototype.build=function(){var e,i,n,r,s;for(this.bindings=[],i=function(e){return function(n){var r,s,o,u,l,h,a,p,d,c,f,b,v,y;if(3===n.nodeType){if(l=t.TextTemplateParser,(o=e.templateDelimiters)&&(p=l.parse(n.data,o)).length&&(1!==p.length||p[0].type!==l.types.text)){for(d=0,f=p.length;f>d;d++)a=p[d],h=document.createTextNode(a.value),n.parentNode.insertBefore(h,n),1===a.type&&e.buildBinding("TextBinding",h,null,a.value);n.parentNode.removeChild(n)}}else 1===n.nodeType&&(r=e.traverse(n));if(!r){for(v=function(){var t,e,i,r;for(i=n.childNodes,r=[],t=0,e=i.length;e>t;t++)u=i[t],r.push(u);return r}(),y=[],c=0,b=v.length;b>c;c++)s=v[c],y.push(i(s));return y}}}(this),s=this.els,n=0,r=s.length;r>n;n++)e=s[n],i(e);this.bindings.sort(function(t,e){var i,n;return((null!=(i=e.binder)?i.priority:void 0)||0)-((null!=(n=t.binder)?n.priority:void 0)||0)})},e.prototype.traverse=function(e){var i,n,r,s,o,u,l,h,a,p,d,c,f,b,v,y;for(s=this.bindingRegExp(),o="SCRIPT"===e.nodeName||"STYLE"===e.nodeName,b=e.attributes,p=0,c=b.length;c>p;p++)if(i=b[p],s.test(i.name)){if(h=i.name.replace(s,""),!(r=this.binders[h])){v=this.binders;for(u in v)a=v[u],"*"!==u&&-1!==u.indexOf("*")&&(l=new RegExp("^"+u.replace(/\*/g,".+")+"$"),l.test(h)&&(r=a))}r||(r=this.binders["*"]),r.block&&(o=!0,n=[i])}for(y=n||e.attributes,d=0,f=y.length;f>d;d++)i=y[d],s.test(i.name)&&(h=i.name.replace(s,""),this.buildBinding("Binding",e,h,i.value));return o||(h=e.nodeName.toLowerCase(),this.components[h]&&!e._bound&&(this.bindings.push(new t.ComponentBinding(this,e,h)),o=!0)),o},e.prototype.select=function(t){var e,i,n,r,s;for(r=this.bindings,s=[],i=0,n=r.length;n>i;i++)e=r[i],t(e)&&s.push(e);return s},e.prototype.bind=function(){var t,e,i,n,r;for(n=this.bindings,r=[],e=0,i=n.length;i>e;e++)t=n[e],r.push(t.bind());return r},e.prototype.unbind=function(){var t,e,i,n,r;for(n=this.bindings,r=[],e=0,i=n.length;i>e;e++)t=n[e],r.push(t.unbind());return r},e.prototype.sync=function(){var t,e,i,n,r;for(n=this.bindings,r=[],e=0,i=n.length;i>e;e++)t=n[e],r.push("function"==typeof t.sync?t.sync():void 0);return r},e.prototype.publish=function(){var t,e,i,n,r;for(n=this.select(function(t){var e;return null!=(e=t.binder)?e.publishes:void 0}),r=[],e=0,i=n.length;i>e;e++)t=n[e],r.push(t.publish());return r},e.prototype.update=function(t){var e,i,n,r,s,o,u;null==t&&(t={});for(i in t)n=t[i],this.models[i]=n;for(o=this.bindings,u=[],r=0,s=o.length;s>r;r++)e=o[r],u.push("function"==typeof e.update?e.update(t):void 0);return u},e}(),t.Binding=function(){function e(t,e,i,n,s){this.view=t,this.el=e,this.type=i,this.keypath=n,this.options=null!=s?s:{},this.getValue=r(this.getValue,this),this.update=r(this.update,this),this.unbind=r(this.unbind,this),this.bind=r(this.bind,this),this.publish=r(this.publish,this),this.sync=r(this.sync,this),this.set=r(this.set,this),this.eventHandler=r(this.eventHandler,this),this.formattedValue=r(this.formattedValue,this),this.parseTarget=r(this.parseTarget,this),this.observe=r(this.observe,this),this.setBinder=r(this.setBinder,this),this.formatters=this.options.formatters||[],this.dependencies=[],this.formatterObservers={},this.model=void 0,this.setBinder()}return e.prototype.setBinder=function(){var t,e,i,n;if(!(this.binder=this.view.binders[this.type])){n=this.view.binders;for(t in n)i=n[t],"*"!==t&&-1!==t.indexOf("*")&&(e=new RegExp("^"+t.replace(/\*/g,".+")+"$"),e.test(this.type)&&(this.binder=i,this.args=new RegExp("^"+t.replace(/\*/g,"(.+)")+"$").exec(this.type),this.args.shift()))}return this.binder||(this.binder=this.view.binders["*"]),this.binder instanceof Function?this.binder={routine:this.binder}:void 0},e.prototype.observe=function(e,i,n){return t.sightglass(e,i,n,{root:this.view.rootInterface,adapters:this.view.adapters})},e.prototype.parseTarget=function(){var e;return e=t.TypeParser.parse(this.keypath),0===e.type?this.value=e.value:(this.observer=this.observe(this.view.models,this.keypath,this.sync),this.model=this.observer.target)},e.prototype.formattedValue=function(e){var i,n,r,o,u,l,h,a,p,d,c,f,b,v;for(v=this.formatters,o=d=0,f=v.length;f>d;o=++d){for(u=v[o],r=u.match(/[^\s']+|'([^']|'[^\s])*'|"([^"]|"[^\s])*"/g),l=r.shift(),u=this.view.formatters[l],r=function(){var e,i,s;for(s=[],e=0,i=r.length;i>e;e++)n=r[e],s.push(t.TypeParser.parse(n));return s}(),a=[],i=c=0,b=r.length;b>c;i=++c)n=r[i],a.push(0===n.type?n.value:((p=this.formatterObservers)[o]||(p[o]={}),(h=this.formatterObservers[o][i])?void 0:(h=this.observe(this.view.models,n.value,this.sync),this.formatterObservers[o][i]=h),h.value()));(null!=u?u.read:void 0)instanceof Function?e=u.read.apply(u,[e].concat(s.call(a))):u instanceof Function&&(e=u.apply(null,[e].concat(s.call(a))))}return e},e.prototype.eventHandler=function(t){var e,i;return i=(e=this).view.handler,function(n){return i.call(t,this,n,e)}},e.prototype.set=function(t){var e;return t=t instanceof Function&&!this.binder["function"]?this.formattedValue(t.call(this.model)):this.formattedValue(t),null!=(e=this.binder.routine)?e.call(this,this.el,t):void 0},e.prototype.sync=function(){var t,e;return this.set(function(){var i,n,r,s,o,u,l;if(this.observer){if(this.model!==this.observer.target){for(o=this.dependencies,i=0,r=o.length;r>i;i++)e=o[i],e.unobserve();if(this.dependencies=[],null!=(this.model=this.observer.target)&&(null!=(u=this.options.dependencies)?u.length:void 0))for(l=this.options.dependencies,n=0,s=l.length;s>n;n++)t=l[n],e=this.observe(this.model,t,this.sync),this.dependencies.push(e)}return this.observer.value()}return this.value}.call(this))},e.prototype.publish=function(){var t,e,i,n,r,o,u,l,h;if(this.observer){for(n=this.getValue(this.el),u=this.formatters.slice(0).reverse(),r=0,o=u.length;o>r;r++)e=u[r],t=e.split(/\s+/),i=t.shift(),(null!=(l=this.view.formatters[i])?l.publish:void 0)&&(n=(h=this.view.formatters[i]).publish.apply(h,[n].concat(s.call(t))));return this.observer.setValue(n)}},e.prototype.bind=function(){var t,e,i,n,r,s,o;if(this.parseTarget(),null!=(r=this.binder.bind)&&r.call(this,this.el),null!=this.model&&(null!=(s=this.options.dependencies)?s.length:void 0))for(o=this.options.dependencies,i=0,n=o.length;n>i;i++)t=o[i],e=this.observe(this.model,t,this.sync),this.dependencies.push(e);return this.view.preloadData?this.sync():void 0},e.prototype.unbind=function(){var t,e,i,n,r,s,o,u,l,h;for(null!=(o=this.binder.unbind)&&o.call(this,this.el),null!=(u=this.observer)&&u.unobserve(),l=this.dependencies,r=0,s=l.length;s>r;r++)n=l[r],n.unobserve();this.dependencies=[],h=this.formatterObservers;for(i in h){e=h[i];for(t in e)n=e[t],n.unobserve()}return this.formatterObservers={}},e.prototype.update=function(t){var e,i;return null==t&&(t={}),this.model=null!=(e=this.observer)?e.target:void 0,null!=(i=this.binder.update)?i.call(this,t):void 0},e.prototype.getValue=function(e){return this.binder&&null!=this.binder.getValue?this.binder.getValue.call(this,e):t.Util.getInputValue(e)},e}(),t.ComponentBinding=function(e){function i(t,e,i){var n,s,o,u,h,a,p;for(this.view=t,this.el=e,this.type=i,this.unbind=r(this.unbind,this),this.bind=r(this.bind,this),this.locals=r(this.locals,this),this.component=this.view.components[this.type],this["static"]={},this.observers={},this.upstreamObservers={},s=t.bindingRegExp(),a=this.el.attributes||[],u=0,h=a.length;h>u;u++)n=a[u],s.test(n.name)||(o=this.camelCase(n.name),l.call(null!=(p=this.component["static"])?p:[],o)>=0?this["static"][o]=n.value:this.observers[o]=n.value)}return u(i,e),i.prototype.sync=function(){},i.prototype.update=function(){},i.prototype.publish=function(){},i.prototype.locals=function(){var t,e,i,n,r,s;i={},r=this["static"];for(t in r)n=r[t],i[t]=n;s=this.observers;for(t in s)e=s[t],i[t]=e.value();return i},i.prototype.camelCase=function(t){return t.replace(/-([a-z])/g,function(t){return t[1].toUpperCase()})},i.prototype.bind=function(){var e,i,n,r,s,o,u,l,h,a,p,d,c,f,b,v,y,g,m,w,k;if(!this.bound){f=this.observers;for(i in f)n=f[i],this.observers[i]=this.observe(this.view.models,n,function(t){return function(e){return function(){return t.componentView.models[e]=t.observers[e].value()}}}(this).call(this,i));this.bound=!0}if(null!=this.componentView)return this.componentView.bind();for(this.el.innerHTML=this.component.template.call(this),u=this.component.initialize.call(this,this.el,this.locals()),this.el._bound=!0,o={},b=t.extensions,a=0,d=b.length;d>a;a++){if(s=b[a],o[s]={},this.component[s]){v=this.component[s];for(e in v)l=v[e],o[s][e]=l}y=this.view[s];for(e in y)l=y[e],null==(h=o[s])[e]&&(h[e]=l)}for(g=t.options,p=0,c=g.length;c>p;p++)s=g[p],o[s]=null!=(m=this.component[s])?m:this.view[s];this.componentView=new t.View(this.el,u,o),this.componentView.bind(),w=this.observers,k=[];for(i in w)r=w[i],k.push(this.upstreamObservers[i]=this.observe(this.componentView.models,i,function(t){return function(e,i){return function(){return i.setValue(t.componentView.models[e])}}}(this).call(this,i,r)));return k},i.prototype.unbind=function(){var t,e,i,n,r;i=this.upstreamObservers;for(t in i)e=i[t],e.unobserve();n=this.observers;for(t in n)e=n[t],e.unobserve();return null!=(r=this.componentView)?r.unbind.call(this):void 0},i}(t.Binding),t.TextBinding=function(t){function e(t,e,i,n,s){this.view=t,this.el=e,this.type=i,this.keypath=n,this.options=null!=s?s:{},this.sync=r(this.sync,this),this.formatters=this.options.formatters||[],this.dependencies=[],this.formatterObservers={}}return u(e,t),e.prototype.binder={routine:function(t,e){return t.data=null!=e?e:""}},e.prototype.sync=function(){return e.__super__.sync.apply(this,arguments)},e}(t.Binding),t["public"].binders.text=function(t,e){return null!=t.textContent?t.textContent=null!=e?e:"":t.innerText=null!=e?e:""},t["public"].binders.html=function(t,e){return t.innerHTML=null!=e?e:""},t["public"].binders.show=function(t,e){return t.style.display=e?"":"none"},t["public"].binders.hide=function(t,e){return t.style.display=e?"none":""},t["public"].binders.enabled=function(t,e){return t.disabled=!e},t["public"].binders.disabled=function(t,e){return t.disabled=!!e},t["public"].binders.checked={publishes:!0,priority:2e3,bind:function(e){return t.Util.bindEvent(e,"change",this.publish)},unbind:function(e){return t.Util.unbindEvent(e,"change",this.publish)},routine:function(t,e){var i;return t.checked="radio"===t.type?(null!=(i=t.value)?i.toString():void 0)===(null!=e?e.toString():void 0):!!e}},t["public"].binders.unchecked={publishes:!0,priority:2e3,bind:function(e){return t.Util.bindEvent(e,"change",this.publish)},unbind:function(e){return t.Util.unbindEvent(e,"change",this.publish)},routine:function(t,e){var i;return t.checked="radio"===t.type?(null!=(i=t.value)?i.toString():void 0)!==(null!=e?e.toString():void 0):!e}},t["public"].binders.value={publishes:!0,priority:3e3,bind:function(e){return"INPUT"!==e.tagName||"radio"!==e.type?(this.event="SELECT"===e.tagName?"change":"input",t.Util.bindEvent(e,this.event,this.publish)):void 0},unbind:function(e){return"INPUT"!==e.tagName||"radio"!==e.type?t.Util.unbindEvent(e,this.event,this.publish):void 0},routine:function(t,e){var i,n,r,s,o,u,h;if("INPUT"===t.tagName&&"radio"===t.type)return t.setAttribute("value",e);if(null!=window.jQuery){if(t=jQuery(t),(null!=e?e.toString():void 0)!==(null!=(s=t.val())?s.toString():void 0))return t.val(null!=e?e:"")}else if("select-multiple"===t.type){if(null!=e){for(h=[],n=0,r=t.length;r>n;n++)i=t[n],h.push(i.selected=(o=i.value,l.call(e,o)>=0));return h}}else if((null!=e?e.toString():void 0)!==(null!=(u=t.value)?u.toString():void 0))return t.value=null!=e?e:""}},t["public"].binders["if"]={block:!0,priority:4e3,bind:function(t){var e,i;return null==this.marker?(e=[this.view.prefix,this.type].join("-").replace("--","-"),i=t.getAttribute(e),this.marker=document.createComment(" rivets: "+this.type+" "+i+" "),this.bound=!1,t.removeAttribute(e),t.parentNode.insertBefore(this.marker,t),t.parentNode.removeChild(t)):void 0},unbind:function(){var t;return null!=(t=this.nested)?t.unbind():void 0},routine:function(e,i){var n,r,s,o;if(!!i==!this.bound){if(i){s={},o=this.view.models;for(n in o)r=o[n],s[n]=r;return(this.nested||(this.nested=new t.View(e,s,this.view.options()))).bind(),this.marker.parentNode.insertBefore(e,this.marker.nextSibling),this.bound=!0}return e.parentNode.removeChild(e),this.nested.unbind(),this.bound=!1}},update:function(t){var e;return null!=(e=this.nested)?e.update(t):void 0}},t["public"].binders.unless={block:!0,priority:4e3,bind:function(e){return t["public"].binders["if"].bind.call(this,e)},unbind:function(){return t["public"].binders["if"].unbind.call(this)},routine:function(e,i){return t["public"].binders["if"].routine.call(this,e,!i)},update:function(e){return t["public"].binders["if"].update.call(this,e)}},t["public"].binders["on-*"]={"function":!0,priority:1e3,unbind:function(e){return this.handler?t.Util.unbindEvent(e,this.args[0],this.handler):void 0},routine:function(e,i){return this.handler&&t.Util.unbindEvent(e,this.args[0],this.handler),t.Util.bindEvent(e,this.args[0],this.handler=this.eventHandler(i))}},t["public"].binders["each-*"]={block:!0,priority:4e3,bind:function(t){var e,i,n,r,s;if(null==this.marker)e=[this.view.prefix,this.type].join("-").replace("--","-"),this.marker=document.createComment(" rivets: "+this.type+" "),this.iterated=[],t.removeAttribute(e),t.parentNode.insertBefore(this.marker,t),t.parentNode.removeChild(t);else for(s=this.iterated,n=0,r=s.length;r>n;n++)i=s[n],i.bind()},unbind:function(){var t,e,i,n,r;if(null!=this.iterated){for(n=this.iterated,r=[],e=0,i=n.length;i>e;e++)t=n[e],r.push(t.unbind());return r}},routine:function(e,i){var n,r,s,o,u,l,h,a,p,d,c,f,b,v,y,g,m,w,k,x,N;if(h=this.args[0],i=i||[],this.iterated.length>i.length)for(w=Array(this.iterated.length-i.length),f=0,y=w.length;y>f;f++)s=w[f],c=this.iterated.pop(),c.unbind(),this.marker.parentNode.removeChild(c.els[0]);for(o=b=0,g=i.length;g>b;o=++b)if(l=i[o],r={index:o},r[h]=l,null==this.iterated[o]){k=this.view.models;for(u in k)l=k[u],null==r[u]&&(r[u]=l);p=this.iterated.length?this.iterated[this.iterated.length-1].els[0]:this.marker,a=this.view.options(),a.preloadData=!0,d=e.cloneNode(!0),c=new t.View(d,r,a),c.bind(),this.iterated.push(c),this.marker.parentNode.insertBefore(d,p.nextSibling)}else this.iterated[o].models[h]!==l&&this.iterated[o].update(r);if("OPTION"===e.nodeName){for(x=this.view.bindings,N=[],v=0,m=x.length;m>v;v++)n=x[v],n.el===this.marker.parentNode&&"value"===n.type?N.push(n.sync()):N.push(void 0);return N}},update:function(t){var e,i,n,r,s,o,u,l;e={};for(i in t)n=t[i],i!==this.args[0]&&(e[i]=n);for(u=this.iterated,l=[],s=0,o=u.length;o>s;s++)r=u[s],l.push(r.update(e));return l}},t["public"].binders["class-*"]=function(t,e){var i;return i=" "+t.className+" ",!e==(-1!==i.indexOf(" "+this.args[0]+" "))?t.className=e?""+t.className+" "+this.args[0]:i.replace(" "+this.args[0]+" "," ").trim():void 0},t["public"].binders["*"]=function(t,e){return null!=e?t.setAttribute(this.type,e):t.removeAttribute(this.type)},t["public"].adapters["."]={id:"_rv",counter:0,weakmap:{},weakReference:function(t){var e,i,n;return t.hasOwnProperty(this.id)||(e=this.counter++,Object.defineProperty(t,this.id,{value:e})),(i=this.weakmap)[n=t[this.id]]||(i[n]={callbacks:{}})},cleanupWeakReference:function(t,e){return Object.keys(t.callbacks).length||t.pointers&&Object.keys(t.pointers).length?void 0:delete this.weakmap[e]},stubFunction:function(t,e){var i,n,r;return n=t[e],i=this.weakReference(t),r=this.weakmap,t[e]=function(){var e,s,o,u,l,h,a,p,d,c;u=n.apply(t,arguments),a=i.pointers;for(o in a)for(s=a[o],c=null!=(p=null!=(d=r[o])?d.callbacks[s]:void 0)?p:[],l=0,h=c.length;h>l;l++)e=c[l],e();return u}},observeMutations:function(t,e,i){var n,r,s,o,u,h;if(Array.isArray(t)){if(s=this.weakReference(t),null==s.pointers)for(s.pointers={},r=["push","pop","shift","unshift","sort","reverse","splice"],u=0,h=r.length;h>u;u++)n=r[u],this.stubFunction(t,n);if(null==(o=s.pointers)[e]&&(o[e]=[]),l.call(s.pointers[e],i)<0)return s.pointers[e].push(i)}},unobserveMutations:function(t,e,i){var n,r,s;return Array.isArray(t)&&null!=t[this.id]&&(r=this.weakmap[t[this.id]])&&(s=r.pointers[e])?((n=s.indexOf(i))>=0&&s.splice(n,1),s.length||delete r.pointers[e],this.cleanupWeakReference(r,t[this.id])):void 0},observe:function(t,e,i){var n,r,s;return n=this.weakReference(t).callbacks,null==n[e]&&(n[e]=[],r=Object.getOwnPropertyDescriptor(t,e),(null!=r?r.get:void 0)||(null!=r?r.set:void 0)||(s=t[e],Object.defineProperty(t,e,{enumerable:!0,get:function(){return s},set:function(r){return function(o){var u,h,a,p;if(o!==s&&(r.unobserveMutations(s,t[r.id],e),s=o,u=r.weakmap[t[r.id]])){if(n=u.callbacks,n[e])for(p=n[e].slice(),h=0,a=p.length;a>h;h++)i=p[h],l.call(n[e],i)>=0&&i();return r.observeMutations(o,t[r.id],e)}}}(this)}))),l.call(n[e],i)<0&&n[e].push(i),this.observeMutations(t[e],t[this.id],e)},unobserve:function(t,e,i){var n,r,s;return(s=this.weakmap[t[this.id]])&&(n=s.callbacks[e])?((r=n.indexOf(i))>=0&&(n.splice(r,1),n.length||delete s.callbacks[e]),this.unobserveMutations(t[e],t[this.id],e),this.cleanupWeakReference(s,t[this.id])):void 0},get:function(t,e){return t[e]},set:function(t,e,i){return t[e]=i}},t.factory=function(e){return t.sightglass=e,t["public"]._=t,t["public"]},"object"==typeof("undefined"!=typeof module&&null!==module?module.exports:void 0)?module.exports=t.factory(require("sightglass")):"function"==typeof define&&define.amd?define(["sightglass"],function(e){return this.rivets=t.factory(e)}):this.rivets=t.factory(sightglass)}).call(this); \ No newline at end of file From 1b7bbc7b9327d78827f056ef6175b13c643aead9 Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Mon, 8 Aug 2016 16:19:07 +0100 Subject: [PATCH 39/43] added test and report fix from #655 --- spec/rivets/binding.js | 36 ++++++++++++++++++++++++++- src/bindings.js | 56 ++++++++++++++++++++++-------------------- src/parsers.js | 4 +-- 3 files changed, 67 insertions(+), 29 deletions(-) diff --git a/spec/rivets/binding.js b/spec/rivets/binding.js index 4cb196783..a4f48a72c 100644 --- a/spec/rivets/binding.js +++ b/spec/rivets/binding.js @@ -94,7 +94,7 @@ describe('Rivets.Binding', function() { describe('with a binder.unbind defined', function() { beforeEach(function() { binding.binder.unbind = function(){} - + }) it('should not throw an error', function() { @@ -205,6 +205,40 @@ describe('Rivets.Binding', function() { binding.binder.routine.calledWith(valueInput, 'fred is awesome').should.be.true }) + it("should resolve formatter arguments to their values", function() { + rivets.formatters.withArguments = { + publish: function(value, arg1, arg2) { + return value + ':' + arg1 + ':' + arg2 + }, + read: function(value, arg1, arg2) { + return value + '-' + arg1 + '-' + arg2 + } + } + + valueInput = document.createElement('input') + valueInput.setAttribute('type', 'text') + valueInput.setAttribute('data-value', "obj.name | withArguments config.age 'male'") + + view = rivets.bind(valueInput, { + obj: { + name: 'nothing' + }, + config: { + age: 50 + } + }) + + binding = view.bindings[0] + model = binding.model + + valueInput.value = 'bobby' + binding.publish({target: valueInput}) + adapter.set.calledWith(model, 'name', 'bobby:50:male').should.be.true + + binding.set('andy') + binding.binder.routine.calledWith(valueInput, 'andy-50-male').should.be.true + }) + it("should not fail or format if the specified binding function doesn't exist", function() { rivets.formatters.awesome = { } valueInput = document.createElement('input') diff --git a/src/bindings.js b/src/bindings.js index a52014567..bd999149e 100644 --- a/src/bindings.js +++ b/src/bindings.js @@ -1,5 +1,5 @@ import rivets from './rivets' -import {parseType} from './parsers' +import {parseType, PRIMITIVE} from './parsers' import {getInputValue} from './util' import {EXTENSIONS, OPTIONS} from './constants' @@ -70,7 +70,7 @@ export class Binding { parseTarget() { let token = parseType(this.keypath) - if (token.type === 0) { + if (token.type === PRIMITIVE) { this.value = token.value } else { this.observer = this.observe(this.view.models, this.keypath, this.sync) @@ -78,36 +78,38 @@ export class Binding { } } - // Applies all the current formatters to the supplied value and returns the - // formatted value. - formattedValue(value) { - this.formatters.forEach((formatterStr, fi) => { - let args = formatterStr.match(/[^\s']+|'([^']|'[^\s])*'|"([^"]|"[^\s])*"/g) - let id = args.shift() - let formatter = this.view.formatters[id] - let processedArgs = [] - - args = args.map(parseType) - - args.forEach((arg, ai) => { - if (arg.type === 0) { - processedArgs.push(arg.value) + parseFormatterArguments(args, formatterIndex) { + return args + .map(parseType) + .map(({type, value}, ai) => { + if (type === PRIMITIVE) { + return value } else { - if (!defined(this.formatterObservers[fi])) { - this.formatterObservers[fi] = {} + if (!defined(this.formatterObservers[formatterIndex])) { + this.formatterObservers[formatterIndex] = {} } - let observer = this.formatterObservers[fi][ai] + let observer = this.formatterObservers[formatterIndex][ai] if (!observer) { - observer = this.observe(this.view.models, arg.value, this.sync) - this.formatterObservers[fi][ai] = observer + observer = this.observe(this.view.models, value, this.sync) + this.formatterObservers[formatterIndex][ai] = observer } - processedArgs.push(observer.value()) + return observer.value() } }) + } + // Applies all the current formatters to the supplied value and returns the + // formatted value. + formattedValue(value) { + this.formatters.forEach((formatterStr, fi) => { + let args = formatterStr.match(/[^\s']+|'([^']|'[^\s])*'|"([^"]|"[^\s])*"/g) + let id = args.shift() + let formatter = this.view.formatters[id] + + const processedArgs = this.parseFormatterArguments(args, fi) if (formatter && (formatter.read instanceof Function)) { value = formatter.read(value, ...processedArgs) @@ -175,13 +177,15 @@ export class Binding { if (this.observer) { let value = this.getValue(this.el) - this.formatters.slice(0).reverse().forEach(formatter => { + this.formatters.slice(0).reverse().forEach((formatter, fi) => { let args = formatter.split(/\s+/) let id = args.shift() let f = this.view.formatters[id] + const processedArgs = this.parseFormatterArguments(args, fi) + if (defined(f) && f.publish) { - value = f.publish(value, ...args) + value = f.publish(value, ...processedArgs) } }) @@ -281,7 +285,7 @@ export class ComponentBinding extends Binding { if (!bindingRegExp.test(attribute.name)) { let propertyName = this.camelCase(attribute.name) let stat = this.component.static - + if (stat && stat.indexOf(propertyName) > -1) { this.static[propertyName] = attribute.value } else { @@ -418,7 +422,7 @@ export class TextBinding extends Binding { // Initializes a text binding for the specified view and text node. constructor(view, el, type, keypath, options = {}) { super(view, el, type); - + this.keypath = keypath this.options = options this.formatters = this.options.formatters || [] diff --git a/src/parsers.js b/src/parsers.js index 8146c8b6c..239e10205 100644 --- a/src/parsers.js +++ b/src/parsers.js @@ -1,4 +1,4 @@ -const PRIMITIVE = 0 +export const PRIMITIVE = 0 const KEYPATH = 1 const TEXT = 0 const BINDING = 1 @@ -24,7 +24,7 @@ export function parseType(string) { type = KEYPATH } - return {type: type, value: value} + return {type, value} } // Template parser and tokenizer for mustache-style text content bindings. From a5997a082797eb324562ec01ee12b2255ce4abd4 Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Tue, 9 Aug 2016 13:28:01 +0100 Subject: [PATCH 40/43] fix formatter order, improve tests --- spec/rivets/binding.js | 61 +++++++++++++++++++++++++++++++++++++++--- src/bindings.js | 11 ++++---- 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/spec/rivets/binding.js b/spec/rivets/binding.js index a4f48a72c..04e839042 100644 --- a/spec/rivets/binding.js +++ b/spec/rivets/binding.js @@ -211,7 +211,7 @@ describe('Rivets.Binding', function() { return value + ':' + arg1 + ':' + arg2 }, read: function(value, arg1, arg2) { - return value + '-' + arg1 + '-' + arg2 + return value.replace(':' + arg1 + ':' + arg2, '') } } @@ -235,8 +235,61 @@ describe('Rivets.Binding', function() { binding.publish({target: valueInput}) adapter.set.calledWith(model, 'name', 'bobby:50:male').should.be.true - binding.set('andy') - binding.binder.routine.calledWith(valueInput, 'andy-50-male').should.be.true + valueInput.value.should.equal('bobby') + + binding.set('andy:50:male') + binding.binder.routine.calledWith(valueInput, 'andy').should.be.true + }) + + it("should resolve formatter arguments correctly with multiple formatters", function() { + rivets.formatters.wrap = { + publish: function(value, arg1, arg2) { + return arg1 + value + arg2 + }, + read: function(value, arg1, arg2) { + return value.replace(arg1, '').replace(arg2, '') + } + } + + rivets.formatters.saveAsCase = { + publish: function(value, typeCase) { + return value['to' + typeCase + 'Case']() + }, + read: function(value, typeCase) { + return value[typeCase === 'Upper' ? 'toLowerCase' : 'toUpperCase']() + } + } + + valueInput = document.createElement('input') + valueInput.setAttribute('type', 'text') + valueInput.setAttribute( + 'data-value', + "obj.name | saveAsCase config.typeCase | wrap config.curly '}' | wrap config.square ']' | wrap config.paren ')'" + ) + + view = rivets.bind(valueInput, { + obj: { + name: 'nothing' + }, + config: { + paren: '(', + square: '[', + curly: '{', + typeCase: 'Upper' + } + }) + + binding = view.bindings[0] + model = binding.model + + valueInput.value = 'bobby' + binding.publish({target: valueInput}) + adapter.set.calledWith(model, 'name', '{[(BOBBY)]}').should.be.true + + valueInput.value.should.equal('bobby') + + binding.set('{[(ANDY)]}') + binding.binder.routine.calledWith(valueInput, 'andy').should.be.true }) it("should not fail or format if the specified binding function doesn't exist", function() { @@ -257,7 +310,7 @@ describe('Rivets.Binding', function() { binding.binder.routine.calledWith(valueInput, 'fred').should.be.true }) - it("should apply read binders left to right, and write binders right to left", function() { + it("should apply read binders left to right, and publish binders right to left", function() { rivets.formatters.totally = { publish: function(value) { return value + ' totally' }, read: function(value) { return value + ' totally' } diff --git a/src/bindings.js b/src/bindings.js index bd999149e..e1b6895c0 100644 --- a/src/bindings.js +++ b/src/bindings.js @@ -176,12 +176,13 @@ export class Binding { publish() { if (this.observer) { let value = this.getValue(this.el) + const lastformatterIndex = this.formatters.length - 1 - this.formatters.slice(0).reverse().forEach((formatter, fi) => { - let args = formatter.split(/\s+/) - let id = args.shift() - let f = this.view.formatters[id] - + this.formatters.slice(0).reverse().forEach((formatter, fiReversed) => { + const fi = lastformatterIndex - fiReversed + const args = formatter.split(/\s+/) + const id = args.shift() + const f = this.view.formatters[id] const processedArgs = this.parseFormatterArguments(args, fi) if (defined(f) && f.publish) { From 9d7d5f6d0c6d214f4cf0973185682f2d9c82083d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Cazeaux Date: Mon, 10 Oct 2016 21:28:57 +0200 Subject: [PATCH 41/43] Report fix for #644 on ES6. Only the test has been reported, fix seems to be already effective on ES6 branch --- spec/rivets/binders.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/spec/rivets/binders.js b/spec/rivets/binders.js index 05e3a7183..3b61e0a9b 100644 --- a/spec/rivets/binders.js +++ b/spec/rivets/binders.js @@ -217,18 +217,36 @@ describe("Rivets.binders", function() { nestedEl.setAttribute("rv-if", "data.showNested"); nestedEl.innerHTML = "{ data.countNested }"; el.appendChild(nestedEl); - + var view = rivets.bind(fragment, model); - + model.data.countNested = "1"; model.data.showNested = true; Should(nestedEl.innerHTML).be.exactly("1"); model.data.show = false; model.data.show = true; model.data.countNested = "42"; - + Should(nestedEl.innerHTML).be.exactly("42"); }); + + it("does not throw when root scope is reset", function () { + el.setAttribute('rv-if', 'scope.error.errors'); + el.innerHTML = '
      {scope.error.errors.email}
      '; + model = { + scope: { + error: { + errors: { + email: 'not a valid address' + } + } + } + }; + var view = rivets.bind(el, model); + (function(){ + model.scope.error = {}; + }).should.not.throw(); + }) }); describe("Custom binder with no attribute value", function() { From 88eec5a9eac755b80fa6d847bddc97243e0a1aee Mon Sep 17 00:00:00 2001 From: Jean-Christophe Cazeaux Date: Mon, 10 Oct 2016 21:40:06 +0200 Subject: [PATCH 42/43] Report fix for #650 --- spec/rivets/binders.js | 59 +++++++++++++++++++++++++++++++++++++++--- src/adapter.js | 3 ++- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/spec/rivets/binders.js b/spec/rivets/binders.js index 05e3a7183..b49c0c43d 100644 --- a/spec/rivets/binders.js +++ b/spec/rivets/binders.js @@ -217,16 +217,16 @@ describe("Rivets.binders", function() { nestedEl.setAttribute("rv-if", "data.showNested"); nestedEl.innerHTML = "{ data.countNested }"; el.appendChild(nestedEl); - + var view = rivets.bind(fragment, model); - + model.data.countNested = "1"; model.data.showNested = true; Should(nestedEl.innerHTML).be.exactly("1"); model.data.show = false; model.data.show = true; model.data.countNested = "42"; - + Should(nestedEl.innerHTML).be.exactly("42"); }); }); @@ -251,4 +251,57 @@ describe("Rivets.binders", function() { Should(el.children[0].innerHTML).be.exactly('received undefined'); }); }); + describe('Array observe and unobserve', function() { + var fragment; + var el1; + var elEach; + var el2; + var el3; + var model; + + beforeEach(function() { + /* + DOM for test +
      +
      +
      +
      {item.data}
      +
      +
      +
      +
      {item.data}
      +
      +
      + */ + fragment = document.createElement("div"); + el1 = document.createElement("div"); + el1.setAttribute("rv-if", "scope.visible"); + el2 = document.createElement("div"); + elEach = document.createElement("div"); + elEach.setAttribute('rv-each-item', 'scope.items'); + elEach.innerHTML = '{item.data}'; + el2.appendChild(elEach); + el1.appendChild(el2); + el3 = document.createElement("div"); + elEach = document.createElement("div"); + elEach.setAttribute('rv-each-item', 'scope.items'); + elEach.innerHTML = '{item.data}'; + el3.appendChild(elEach); + fragment.appendChild(el1); + fragment.appendChild(el3); + + model = { scope: {items: [], visible:true }}; + }); + + it('observes array changes after another array binding is unbound', function() { + var view = rivets.bind(fragment, model); + model.scope.items.push({data:"data"}); + Should(el3.childNodes.length).be.exactly(2); + model.scope.items.push({data:"data"}); + Should(el3.childNodes.length).be.exactly(3); + model.scope.visible = false; + model.scope.items.push({data:"data"}); + Should(el3.childNodes.length).be.exactly(4); + }); + }); }); diff --git a/src/adapter.js b/src/adapter.js index 084d9f2ae..40334b40b 100644 --- a/src/adapter.js +++ b/src/adapter.js @@ -178,10 +178,11 @@ const adapter = { if (!callbacks.length) { delete map.callbacks[keypath] + this.unobserveMutations(obj[keypath], obj[this.id], keypath) } } - this.unobserveMutations(obj[keypath], obj[this.id], keypath) + this.cleanupWeakReference(map, obj[this.id]) } } From fd2d5c7eba804c5812776f8916b49e147dd8dca1 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Cazeaux Date: Mon, 10 Oct 2016 21:49:19 +0200 Subject: [PATCH 43/43] Report #619. It's only a test, actual fix is on sightglass 0.2.6 --- spec/rivets/binding.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/spec/rivets/binding.js b/spec/rivets/binding.js index 04e839042..9bf0e98cc 100644 --- a/spec/rivets/binding.js +++ b/spec/rivets/binding.js @@ -133,6 +133,35 @@ describe('Rivets.Binding', function() { }) }) + describe('prototype functions', function() { + it('does call routine if observed value is a function', function() { + rivets.configure({ + executeFunctions:true + }) + var Employee = function(name) { + this.name = name + } + Employee.prototype.getName = function() { + return this.name; + } + var model = {employee: new Employee("John")} + + el = document.createElement('div') + el.setAttribute('data-text', 'employee.getName') + + view = rivets.bind(el, model) + binding = view.bindings[0] + + sinon.spy(binding.binder, 'routine') + model.employee = new Employee("Peter") + binding.binder.routine.calledWith(el, "Peter").should.be.true + // Back to default !! + rivets.configure({ + executeFunctions:false + }) + }) + }) + describe('publish()', function() { it("should publish the value of a number input", function() { numberInput = document.createElement('input')