├── LICENSE ├── README.md ├── repeatable-test.htm ├── repeatable.jquery.json └── repeatable.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 c-smile 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | repeatable 2 | ========== 3 | 4 | Repeatable "template-less" jQuery plugin 5 | 6 | # Introduction 7 | 8 | The Repeatable is template-less mechanism for populating various list, tables, etc. by arrays containing objects. 9 | Temlate-less here means that you don't need to use templates for your repeteables anywere except of markup itself. 10 | 11 | # Example 12 | 13 | Let's assume that you would want to populate (render) some list with some data stored as objects in some array: 14 | 15 | ```javascript 16 | var data = [ 17 | { name: "Olga", age: 20, email: "aaa@example.com" }, 18 | { name: "Peter", age: 30, email: "bbb@example.com" }, 19 | { name: "Ivan", age: 15, email: "ccc@example.com" }, 20 | ]; 21 | ``` 22 | 23 | Then with the Repeatable you can define your list in markup as: 24 | 25 | ```html 26 | 30 | ``` 31 | 32 | First `
  • ` above is so called record template - it will appear in the DOM for each element in the data array. Second `
  • ` element will appear once and only if you will feed the repeatable by empty array. 33 | 34 | Having all these the Repeatable makes list population extremely simple: 35 | 36 | ```javascript 37 | var list = $("ul#people").repeatable(); // declaring the repeatable 38 | list.value = data; // that's data population, sic! 39 | ``` 40 | 41 | Note `list.value = data;` above, by assigning array data to the value property of the repetable you are populating the list by the data. 42 | 43 | 44 | Live demo: http://terrainformatica.com/widgets.js/repeatable/repeatable-test.htm 45 | 46 | # Record template microformat 47 | 48 | Any text or attribute value in the repeatable section may contain expressions enclosed in "mustache" brackets `{{ ...expr ...}}`. 49 | 50 | While populating records each such expression will be executed and it's return value (string) will be put in the DOM replacing `{{...}}` placeholder as a whole. 51 | 52 | ## Environment variables 53 | 54 | In repeatable expressions following variables have special meaning: 55 | 56 | * `this` - refers to the record (object) being processed (rendered); 57 | * `$index` - number, index of current record; 58 | * `$first` - `true` if this is the first record; 59 | * `$last` - `true` if this is the last record; 60 | * `$length` - number, total number of records in the array. 61 | 62 | ## Conditionals 63 | 64 | Any element inside repeatable section can be declared as conditional by using `if="...expr..."` attribute. Expression defined by the attribute will be evaluated and if its result is "truthy" the element will be rendered, otherwise - removed from the DOM. 65 | 66 | -------------------------------------------------------------------------------- /repeatable-test.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 32 | 33 | 34 | 35 | 36 |

    List population demo

    37 | 38 | 39 | 43 |
    44 | 45 |

    Horizontal list

    46 | 47 | 48 | 49 |

    50 | Friends of yours: 51 | 52 | {{this.name}} {{ $last? '' : ',' }} 53 | No friends found! 54 | 55 |

    56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /repeatable.jquery.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "repeatable", 3 | "title": "jQuery Repeatable", 4 | "description": "jQuery Repeatable 'template-less' plugin for lists and tables population by data arrays.", 5 | "keywords": [ 6 | "repeatable", 7 | "population" 8 | ], 9 | "version": "1.0.2", 10 | "author": { 11 | "name": "Andrew Fedoniouk @ terrainformatica.com", 12 | "url": "http://terrainformatica.com" 13 | }, 14 | "maintainers": [ 15 | { 16 | "name": "Andrew Fedoniouk", 17 | "email": "andrew.fedoniouk@gmail.com", 18 | "url": "http://stackoverflow.com/users/421163/c-smile" 19 | } 20 | ], 21 | "licenses": [ 22 | { 23 | "type": "MIT", 24 | "url": "https://github.com/c-smile/repeatable/blob/master/LICENSE" 25 | } 26 | ], 27 | "bugs": "https://github.com/c-smile/repeatable/issues", 28 | "homepage": "https://github.com/c-smile/repeatable", 29 | "docs": "https://github.com/c-smile/repeatable", 30 | "download": "https://github.com/c-smile/repeatable", 31 | "dependencies": { "jquery": ">=1.8" }, 32 | "demo": "http://terrainformatica.com/widgets.js/repeatable/repeatable-test.htm" 33 | } -------------------------------------------------------------------------------- /repeatable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Andrew Fedoniouk 3 | * @name jQuery repeatable() 4 | * @license MIT 5 | * @purpose template-less population of repeatables (lists) 6 | 7 | If you have this markup: 8 | 9 | 12 | 13 | And this initialization code: 14 | 15 | var rep = $("ul").repeatable(); 16 | 17 | And this data: 18 | 19 | var data = [ 20 | { name: "Olga", age: 20, email: "aaa@example.com" }, 21 | { name: "Peter", age: 30, email: "bbb@example.com" }, 22 | { name: "Ivan", age: 15, email: "ccc@example.com" }, 23 | ]; 24 | 25 | Then after assigning array value to the repeteable : 26 | 27 | rep.value = data; 28 | 29 | You will get this markup populated in the list: 30 | 31 | 36 | 37 | */ 38 | 39 | (function ($) { 40 | 41 | function repeatable(el) { 42 | 43 | if(typeof el.getValue == "function") 44 | return el; // seems like already set 45 | 46 | var $el = $(el); 47 | var template = $el.find(">*").remove(); 48 | var nrTemplate = template.length > 1 ? $(template[1]) : null; // "no records" template 49 | template = $(template[0]); 50 | 51 | var compiled = {}; // compiled expressions 52 | var vector = null; // data 53 | var index = 0; // current index being processed 54 | 55 | function compiledExpr(str) { 56 | var expr = compiled[str]; 57 | if( !expr ) 58 | compiled[str] = expr = new Function("$index","$first","$last","$total", "return (" + str + ")"); 59 | return expr; 60 | } 61 | 62 | function replace(text, data) { 63 | function subst(a, b) { 64 | var expr = compiledExpr(b); 65 | var s = expr.call(data, index, index==0,index==vector.length - 1, vector.length); return s === undefined ? "" : s; 66 | } 67 | return text.replace(/{{(.*)}}/g, subst); 68 | } 69 | 70 | function instantiate(el, data) { 71 | var attributes = el.attributes; 72 | for (var i = 0; i < attributes.length; ++i) { 73 | var attribute = attributes[i]; 74 | if (attribute.name == "if") { 75 | var str = attribute.value; 76 | var expr = compiledExpr(str); 77 | var tokeep = expr.call(data, index, index == 0, index == vector.length - 1, vector.length); 78 | if (!tokeep) { el.parentElement.removeChild(el); return; } 79 | } 80 | else if (attribute.value.indexOf("{{") >= 0) 81 | attribute.value = replace(attribute.value, data); 82 | } 83 | for (var nn, n = el.firstChild; n; n = nn) { 84 | var nn = n.nextSibling; 85 | if (n.nodeType == 1) // element 86 | instantiate(n, data); 87 | else if (n.nodeType == 3) // text 88 | { 89 | var t = n.textContent; 90 | if (t.indexOf("{{") >= 0) 91 | n.textContent = replace(t, data); 92 | } 93 | } 94 | } 95 | 96 | function getValue() { return vector; } 97 | 98 | function setValue(newValue) { 99 | vector = newValue; 100 | var t = template[0]; 101 | if( !vector || vector.length == 0 ) { 102 | $el.empty(); 103 | if(nrTemplate) 104 | $el.append(nrTemplate); // no records 105 | } 106 | else { 107 | var fragment = document.createDocumentFragment(); 108 | for (index = 0; index < vector.length; ++index) { 109 | var nel = t.cloneNode(true); 110 | instantiate(nel, vector[index]); 111 | fragment.appendChild(nel); 112 | } 113 | $el.empty(); 114 | $el.append(fragment); 115 | } 116 | } 117 | 118 | el.getValue = getValue; el.setValue = setValue; 119 | // redefine its 'value' property, setting value to some array will cause popupaltion of the repeatable by that data. 120 | try { Object.defineProperty(el, "value", { get: getValue, set: setValue, enumerable: true, configurable: true }); } catch(e) {} 121 | 122 | return el; 123 | } 124 | 125 | $.fn.repeatable = function () { 126 | var el = null; 127 | this.each(function () { el = repeatable(this); }); 128 | return el; // NOTE: returns last matched element! 129 | }; 130 | 131 | })(jQuery); 132 | 133 | --------------------------------------------------------------------------------