Skip to content
This repository
Browse code

Initial commit.

  • Loading branch information...
commit a8c387d39d00eb07fae6af20f686d95eddb32d45 0 parents
Michael Richards authored April 16, 2012
1  .gitignore
... ...
@@ -0,0 +1 @@
  1
+.DS_Store
20  LICENSE
... ...
@@ -0,0 +1,20 @@
  1
+Copyright (c) 2011 Michael Richards
  2
+
  3
+Permission is hereby granted, free of charge, to any person obtaining
  4
+a copy of this software and associated documentation files (the
  5
+"Software"), to deal in the Software without restriction, including
  6
+without limitation the rights to use, copy, modify, merge, publish,
  7
+distribute, sublicense, and/or sell copies of the Software, and to
  8
+permit persons to whom the Software is furnished to do so, subject to
  9
+the following conditions:
  10
+
  11
+The above copyright notice and this permission notice shall be
  12
+included in all copies or substantial portions of the Software.
  13
+
  14
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
64  README.md
Source Rendered
... ...
@@ -0,0 +1,64 @@
  1
+# Rivets.js
  2
+
  3
+Rivets.js is a declarative, observer-based DOM-binding facility that plays well with existing frameworks and supports multiple contexts. It aims to be lightweight, extensible, and configurable to work with any event-driven model.
  4
+
  5
+## Disclaimer
  6
+
  7
+Rivets.js is alpha software. While it should work well enough for prototyping and weekend projects, it is still undergoing major development. APIs are subject to change.
  8
+
  9
+## Usage
  10
+
  11
+No contrived example here yet, but the `rivets` module is simple. It exposes a single `bind` function that takes three arguements; the parent DOM element that you wish to bind to, an adapter interface, and a set of context objects.
  12
+
  13
+    rivets.bind(el, adapter, {user: currentUser, item: item});
  14
+
  15
+#### Available bindings:
  16
+
  17
+- **data-text**: one-way binding that sets the node's text.
  18
+- **data-value**: two-way binding that sets the node's value.
  19
+- **data-show**: one-way binding that sets the node's display state.
  20
+- **data-hide**: one-way inverse binding that sets the node's display state.
  21
+- **data-enabled**: one-way binding that sets the node's enabled state.
  22
+- **data-disabled**: one-way inverse binding that sets the node's enabled state.
  23
+- **data-checked**: two-way binding that sets the node's checked state.
  24
+- **data-unchecked**: two-way inverse binding that sets the node's checked state.
  25
+- **data-selected**: two-way binding that sets the node's selected state.
  26
+- **data-unselected**: two-way inverse binding that sets the node's checked state.
  27
+- **data-[attribute]**: one-way binding that sets the node's attribute value (currently only for a few select attributes).
  28
+
  29
+## Adapters
  30
+
  31
+Rivets.js is model interface-agnostic, meaning that it can work with any event-driven model by way of defining an adapter. The following standard adapter observes `change:[attribute]` style events and works with frameworks like [Backbone.js](http://documentcloud.github.com/backbone/), [Spine.js](http://spinejs.com/) and [Stapes.js](http://hay.github.com/stapes/) with minimal alterations for each. If your model's events API differs, it's trivial to write an adapter that will work with your own model objects.
  32
+
  33
+An adapter is an object that responds to `subscribe`, `read` and `publish`.
  34
+
  35
+    backboneAdapter = {
  36
+      subscribe: function(obj, keypath, callback) {
  37
+        obj.on('change:' + keypath, function(m, v) { callback(v) });
  38
+      },
  39
+      
  40
+      read: function(obj, keypath) {
  41
+        obj.get(keypath);
  42
+      },
  43
+      
  44
+      publish: function(obj, keypath, value) {
  45
+        obj.set(keypath, data);
  46
+      }
  47
+    }
  48
+
  49
+#### subscribe(obj, keypath, callback)
  50
+
  51
+- **obj**: The model object that we want to subscribe to for attribute changes. These are what get passed in as context objects when calling `rivets.bind`.
  52
+- **keypath**: The attribute name that we want to scope to when subscribing to the model's attribute changes. This would most commonly be a single key or a nested keypath (support for nested keypaths depends on if your model object publishes events on nested attribute changes). You may define and intercept this value in any format, for example if your model object uses `author[profile][bio]` instead of `author.profile.bio`.
  53
+- **callback**: The function that performs the binding routine. Call this function with the new attribute value.
  54
+
  55
+#### read(obj, keypath)
  56
+
  57
+- **obj**: The model object that we want to read the attribute from.
  58
+- **keypath**: The attribute name that we want to read from the model object.
  59
+
  60
+#### publish(obj, keypath, value)
  61
+
  62
+- **obj**: The model object that we want to set the new attribute value on.
  63
+- **keypath**: The attribute name that we want to set on the model object.
  64
+- **value**: The new attribute value that we want to set on the model.
101  lib/rivets.js
... ...
@@ -0,0 +1,101 @@
  1
+
  2
+  window.rivets = (function() {
  3
+    var attr, bindableAttributes, bindings, getInputValue, registerBinding, setAttribute, _fn, _i, _len;
  4
+    registerBinding = function(el, interface, contexts, type, callback) {
  5
+      return $("*[data-" + type + "]", el).each(function() {
  6
+        var context, inputValue, keypath, path;
  7
+        var _this = this;
  8
+        path = $(this).attr("data-" + type).split('.');
  9
+        context = path.shift();
  10
+        keypath = path.join('.');
  11
+        callback(this, interface.read(contexts[context], keypath));
  12
+        interface.subscribe(contexts[context], keypath, function(value) {
  13
+          return callback(_this, value);
  14
+        });
  15
+        if (inputValue = getInputValue(this)) {
  16
+          return interface.publish(contexts[context], keypath, inputValue);
  17
+        }
  18
+      });
  19
+    };
  20
+    setAttribute = function(el, attr, value, mirrored) {
  21
+      if (mirrored == null) mirrored = false;
  22
+      if (value) {
  23
+        return $(el).attr(attr, mirrored ? attr : value);
  24
+      } else {
  25
+        return $(el).removeAttr(attr);
  26
+      }
  27
+    };
  28
+    getInputValue = function(el) {
  29
+      switch ($(el).attr('type')) {
  30
+        case 'text':
  31
+        case 'textarea':
  32
+        case 'password':
  33
+        case 'select-one':
  34
+          return $(this).val();
  35
+        case 'checkbox':
  36
+          return $(this).is(':checked');
  37
+      }
  38
+    };
  39
+    bindings = {
  40
+      show: function(el, value) {
  41
+        if (value) {
  42
+          return $(el).show();
  43
+        } else {
  44
+          return $(el).hide();
  45
+        }
  46
+      },
  47
+      hide: function(el, value) {
  48
+        if (value) {
  49
+          return $(el).hide();
  50
+        } else {
  51
+          return $(el).show();
  52
+        }
  53
+      },
  54
+      enabled: function(el, value) {
  55
+        return setAttribute(el, 'disabled', !value, true);
  56
+      },
  57
+      disabled: function(el, value) {
  58
+        return setAttribute(el, 'disabled', value, true);
  59
+      },
  60
+      checked: function(el, value) {
  61
+        return setAttribute(el, 'checked', value, true);
  62
+      },
  63
+      unchecked: function(el, value) {
  64
+        return setAttribute(el, 'checked', !value, true);
  65
+      },
  66
+      selected: function(el, value) {
  67
+        return setAttribute(el, 'selected', value, true);
  68
+      },
  69
+      unselected: function(el, value) {
  70
+        return setAttribute(el, 'checked', !value, true);
  71
+      },
  72
+      text: function(el, value) {
  73
+        return $(el).text(value || '');
  74
+      },
  75
+      value: function(el, value) {
  76
+        return $(el).val(value);
  77
+      }
  78
+    };
  79
+    bindableAttributes = ['id', 'class', 'name', 'src', 'href', 'alt', 'title', 'placeholder'];
  80
+    _fn = function(attr) {
  81
+      return bindings[attr] = function(el, value) {
  82
+        return setAttribute(el, attr, value);
  83
+      };
  84
+    };
  85
+    for (_i = 0, _len = bindableAttributes.length; _i < _len; _i++) {
  86
+      attr = bindableAttributes[_i];
  87
+      _fn(attr);
  88
+    }
  89
+    return {
  90
+      bind: function(el, interface, contexts) {
  91
+        var callback, type, _results;
  92
+        if (contexts == null) contexts = {};
  93
+        _results = [];
  94
+        for (type in bindings) {
  95
+          callback = bindings[type];
  96
+          _results.push(registerBinding(el, interface, contexts, type, callback));
  97
+        }
  98
+        return _results;
  99
+      }
  100
+    };
  101
+  })();
18  package.json
... ...
@@ -0,0 +1,18 @@
  1
+{
  2
+  "name" : "rivets",
  3
+  "description" : "Declarative DOM-binding facility.",
  4
+  "version" : "0.1.0",
  5
+  "author" : "Michael Richards",
  6
+  "main" : "./lib/rivets.js",
  7
+  "licenses" : [{
  8
+    "type" : "MIT",
  9
+    "url" : "https://github.com/mikeric/rivets/blob/master/LICENSE"
  10
+  }],
  11
+  "repository" : {
  12
+    "type" : "git",
  13
+    "url" : "https://github.com/mikeric/rivets.git"
  14
+  },
  15
+  "dependencies" : {
  16
+    "jquery" : "1.5.0"
  17
+  }
  18
+}
63  src/rivets.coffee
... ...
@@ -0,0 +1,63 @@
  1
+# rivets.js
  2
+# version : 0.1.0
  3
+# author : Michael Richards
  4
+# license : MIT
  5
+
  6
+window.rivets = do ->
  7
+  registerBinding = (el, interface, contexts, type, callback) ->
  8
+    $("*[data-#{type}]", el).each ->
  9
+      path = $(this).attr("data-#{type}").split '.'
  10
+      context = path.shift()
  11
+      keypath = path.join '.'
  12
+
  13
+      callback this, interface.read contexts[context], keypath
  14
+
  15
+      interface.subscribe contexts[context], keypath, (value) =>
  16
+        callback this, value
  17
+
  18
+      if inputValue = getInputValue this
  19
+        interface.publish contexts[context], keypath, inputValue
  20
+
  21
+  setAttribute = (el, attr, value, mirrored=false) ->
  22
+    if value
  23
+      $(el).attr attr, if mirrored then attr else value
  24
+    else
  25
+      $(el).removeAttr attr
  26
+
  27
+  getInputValue = (el) ->
  28
+    switch $(el).attr 'type'
  29
+      when 'text', 'textarea', 'password', 'select-one' then $(this).val()
  30
+      when 'checkbox' then $(this).is ':checked'
  31
+
  32
+  bindings =
  33
+    show: (el, value) ->
  34
+      if value then $(el).show() else $(el).hide()
  35
+    hide: (el, value) ->
  36
+      if value then $(el).hide() else $(el).show()
  37
+    enabled: (el, value) ->
  38
+      setAttribute el, 'disabled', !value, true
  39
+    disabled: (el, value) ->
  40
+      setAttribute el, 'disabled', value, true
  41
+    checked: (el, value) ->
  42
+      setAttribute el, 'checked', value, true
  43
+    unchecked: (el, value) ->
  44
+      setAttribute el, 'checked', !value, true
  45
+    selected: (el, value) ->
  46
+      setAttribute el, 'selected', value, true
  47
+    unselected: (el, value) ->
  48
+      setAttribute el, 'checked', !value, true
  49
+    text: (el, value) ->
  50
+      $(el).text value or ''
  51
+    value: (el, value) ->
  52
+      $(el).val value
  53
+
  54
+  bindableAttributes = ['id', 'class', 'name', 'src', 'href', 'alt', 'title', 'placeholder']
  55
+
  56
+  for attr in bindableAttributes
  57
+    do (attr) ->
  58
+      bindings[attr] = (el, value) ->
  59
+        setAttribute el, attr, value
  60
+
  61
+  bind: (el, interface, contexts={}) ->
  62
+    for type, callback of bindings
  63
+      registerBinding el, interface, contexts, type, callback

0 notes on commit a8c387d

Please sign in to comment.
Something went wrong with that request. Please try again.