├── examples ├── 1 │ └── index.html ├── 2 │ └── index.html ├── 3 │ └── index.html ├── 4 │ └── index.html ├── 5 │ └── index.html ├── 6 │ └── index.html ├── todomvc │ ├── css │ │ └── app.css │ ├── node_modules │ │ ├── todomvc-common │ │ │ ├── base.css │ │ │ └── base.js │ │ └── todomvc-app-css │ │ │ └── index.css │ ├── js │ │ ├── z_handlers.js │ │ └── z.js │ └── index.html ├── index.html └── css │ └── main.css ├── LICENSE ├── CHANGELOG.md └── src ├── ext └── z_handlers.js └── core └── z.js /examples/todomvc/css/app.css: -------------------------------------------------------------------------------- 1 | [filter="active"] li.completed, 2 | [filter="completed"] li:not(.completed), 3 | .all-active .clear-completed 4 | { 5 | display: none; 6 | } 7 | .todo-list li label 8 | { 9 | white-space: initial; 10 | } -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Z :: Примеры 5 | 6 | 7 | 8 |

Примеры

9 |
10 | 21 |
22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /examples/1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Z :: Простейший пример 5 | 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | « 16 |

Простейший пример

17 | 18 |
19 |

20 | red 21 | Этот текст должен быть красным 22 |

23 | 24 | 25 | 26 |
27 | 28 | <p> 29 | <!-- 30 | Устанавливаем z-обработчик тегу <p> 31 | По событию "makeTextRed" необходимо вызвать "addClass" 32 | и в качестве параметра передать имя класса "red" 33 | --> 34 | <e on="makeTextRed" do="addClass">red</e> 35 | Этот текст должен быть красным 36 | </p> 37 | <!-- После загрузки страницы выполняем операции --> 38 | <exec> 39 | <!-- 40 | Запускаем событие "makeTextRed" 41 | начиная от родительского элемена тега <exec> 42 | --> 43 | <dispatch e="makeTextRed"></dispatch> 44 | </exec> 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Версия 0.8.5 2 | 3 | * Косметические изменения в коде 4 | 5 | Версия 0.8.4 6 | 7 | * Исправлена ошибка с циклическими запусками директивы `exec` 8 | * Добавлена возможность не блокировать DOM-события (`stopPropagation` и `preventDefault`) 9 | * Добавлена возможность не запускать события, если на странице выделен контент 10 | * Можно указывать `target` для `` для навешивания событий на `window` 11 | * Исправлена ошибка с пропусками при обработке директивы `` 12 | * Оригинальное событие браузера теперь передается и в глобальные события 13 | * `z.dispatchById` возвращает объект `zEvent` 14 | * Если `` удален из DOM-дерева, он перестает вызываться 15 | * Заменена функция миксования объектов данных на более функциональную 16 | * Добавлен параметр `before` в функцию `template` для вставки контента в начало контейнера 17 | * Директива `` вставляет значение в `textarea` как `value` вместо текстовой ноды 18 | * Исправлена ошибка с неправильным переносом контента директивы `` 19 | * Добавлена директива `dom` для прямого доступа к DOM-методам контейнера 20 | * Исправлена ошибка с наследованием у директивы `` и `` 21 | * Директива `datetime` теперь может выводить даты в UTC формате 22 | 23 | Версия 0.6 24 | 25 | * Добавлены атрибуты `freeze` и `freeze` директиве `` 26 | * Исправлена ошибка с многократным навешиванием обработчиков через `` 27 | * Теперь объект `zEvent` формируется каждый раз наново при срабатывании обработчика DOM-события. 28 | * Добавлена возможность обращаться к удаленному диспатчеру по `name` 29 | * Исправлена ошибка с неправильным срабатыванием единоразового срабатывания процесса темплейтирования 30 | * Исправлены ошибки зон видимости данных в командах темплейтирования 31 | * Добавлены новые инструкции темплейтирования `inner_html`, `datetime` 32 | -------------------------------------------------------------------------------- /examples/2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Z :: Запуск события по нажатию на ссылку 5 | 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | « 16 |

Запуск события по нажатию на ссылку

17 | 18 |
19 |

20 | red 21 | Этот текст должен стать красным после нажатия на ссылку 22 |

23 |

24 | 25 | 26 | 27 | 28 | Сделать текст красным 29 | 30 |

31 |
32 | 33 | <p> 34 | <!-- 35 | Устанавливаем z-обработчик тегу <p> 36 | По событию "makeTextRed" необходимо вызвать "addClass" 37 | и в качестве параметра передать имя класса "red" 38 | --> 39 | <e on="makeTextRed" do="addClass">red</e> 40 | Этот текст должен стать красным после нажатия на ссылку 41 | </p> 42 | <p> 43 | <a href="#"> 44 | <!-- Тегу <a> устанавливаем DOM-обработчик "click" --> 45 | <handler on="click"> 46 | <!-- 47 | Запускаем событие "makeTextRed" 48 | начиная от <body> (f[rom]="body") 49 | (иначе событие распространялость бы от тега <a>) 50 | --> 51 | <dispatch e="makeTextRed" f="body"></dispatch> 52 | </handler> 53 | Сделать текст красным 54 | </a> 55 | </p> 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /examples/todomvc/node_modules/todomvc-common/base.css: -------------------------------------------------------------------------------- 1 | hr { 2 | margin: 20px 0; 3 | border: 0; 4 | border-top: 1px dashed #c5c5c5; 5 | border-bottom: 1px dashed #f7f7f7; 6 | } 7 | 8 | .learn a { 9 | font-weight: normal; 10 | text-decoration: none; 11 | color: #b83f45; 12 | } 13 | 14 | .learn a:hover { 15 | text-decoration: underline; 16 | color: #787e7e; 17 | } 18 | 19 | .learn h3, 20 | .learn h4, 21 | .learn h5 { 22 | margin: 10px 0; 23 | font-weight: 500; 24 | line-height: 1.2; 25 | color: #000; 26 | } 27 | 28 | .learn h3 { 29 | font-size: 24px; 30 | } 31 | 32 | .learn h4 { 33 | font-size: 18px; 34 | } 35 | 36 | .learn h5 { 37 | margin-bottom: 0; 38 | font-size: 14px; 39 | } 40 | 41 | .learn ul { 42 | padding: 0; 43 | margin: 0 0 30px 25px; 44 | } 45 | 46 | .learn li { 47 | line-height: 20px; 48 | } 49 | 50 | .learn p { 51 | font-size: 15px; 52 | font-weight: 300; 53 | line-height: 1.3; 54 | margin-top: 0; 55 | margin-bottom: 0; 56 | } 57 | 58 | #issue-count { 59 | display: none; 60 | } 61 | 62 | .quote { 63 | border: none; 64 | margin: 20px 0 60px 0; 65 | } 66 | 67 | .quote p { 68 | font-style: italic; 69 | } 70 | 71 | .quote p:before { 72 | content: '“'; 73 | font-size: 50px; 74 | opacity: .15; 75 | position: absolute; 76 | top: -20px; 77 | left: 3px; 78 | } 79 | 80 | .quote p:after { 81 | content: '”'; 82 | font-size: 50px; 83 | opacity: .15; 84 | position: absolute; 85 | bottom: -42px; 86 | right: 3px; 87 | } 88 | 89 | .quote footer { 90 | position: absolute; 91 | bottom: -40px; 92 | right: 0; 93 | } 94 | 95 | .quote footer img { 96 | border-radius: 3px; 97 | } 98 | 99 | .quote footer a { 100 | margin-left: 5px; 101 | vertical-align: middle; 102 | } 103 | 104 | .speech-bubble { 105 | position: relative; 106 | padding: 10px; 107 | background: rgba(0, 0, 0, .04); 108 | border-radius: 5px; 109 | } 110 | 111 | .speech-bubble:after { 112 | content: ''; 113 | position: absolute; 114 | top: 100%; 115 | right: 30px; 116 | border: 13px solid transparent; 117 | border-top-color: rgba(0, 0, 0, .04); 118 | } 119 | 120 | .learn-bar > .learn { 121 | position: absolute; 122 | width: 272px; 123 | top: 8px; 124 | left: -300px; 125 | padding: 10px; 126 | border-radius: 5px; 127 | background-color: rgba(255, 255, 255, .6); 128 | transition-property: left; 129 | transition-duration: 500ms; 130 | } 131 | 132 | @media (min-width: 899px) { 133 | .learn-bar { 134 | width: auto; 135 | padding-left: 300px; 136 | } 137 | 138 | .learn-bar > .learn { 139 | left: 8px; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /examples/css/main.css: -------------------------------------------------------------------------------- 1 | body 2 | { 3 | background: #2c3e50; 4 | color: #ecf0f1; 5 | font: normal 12pt Arial; 6 | padding: 30px 40px; 7 | } 8 | body::after 9 | { 10 | content: "z"; 11 | position: fixed; 12 | left: 0px; 13 | top: 0px; 14 | font-size: 200px; 15 | z-index: -1; 16 | color: rgba(255,255,255,0.1); 17 | line-height: 85px; 18 | } 19 | h1 20 | { 21 | font-weight: normal; 22 | } 23 | h1::before 24 | { 25 | content: ""; 26 | padding: 0.2em 0.75em; 27 | margin-right: 0.5em; 28 | width: 0px; 29 | height: 0px; 30 | background: #1abc9c; 31 | border-radius: 10em; 32 | } 33 | a 34 | { 35 | color: #1abc9c; 36 | text-decoration: none; 37 | } 38 | a:hover, a:active 39 | { 40 | text-decoration: underline; 41 | } 42 | a:visited 43 | { 44 | color: #16a085; 45 | } 46 | .content 47 | { 48 | padding-left: 65px; 49 | } 50 | .backLink:link, .backLink:visited 51 | { 52 | font: normal 20pt Arial; 53 | position: absolute; 54 | color: #ecf0f1; 55 | text-decoration: none; 56 | padding: 0.3em 0.65em; 57 | margin: 0.55em 0px; 58 | } 59 | li 60 | { 61 | margin: 10px 0px; 62 | } 63 | code 64 | { 65 | display: block; 66 | white-space: pre; 67 | margin: 40px 20px 20px 65px; 68 | padding: 20px; 69 | background: #34495e; 70 | overflow: auto; 71 | } 72 | code.html::before 73 | { 74 | font-family: Arial; 75 | content: "HTML"; 76 | display: block; 77 | background: #7f8c8d; 78 | padding: 8px 20px; 79 | margin: -20px -20px 20px; 80 | position: relative; 81 | } 82 | code .tag 83 | { 84 | color: #d35400; 85 | } 86 | code .zTag 87 | { 88 | color: #3498db; 89 | } 90 | code .txt 91 | { 92 | color: #ecf0f1; 93 | } 94 | code .attrVal 95 | { 96 | color: #1abc9c; 97 | } 98 | code .comment 99 | { 100 | color: #7f8c8d; 101 | } 102 | code .zHandler 103 | { 104 | color: #000; 105 | } 106 | code .dispatcher 107 | { 108 | color: #fff; 109 | text-shadow: 2px 1px 0px rgba(0,0,0,0.2); 110 | } 111 | code .hl 112 | { 113 | background: #000; 114 | border-radius: 5px; 115 | padding: 0px 5px; 116 | margin: 0px -5px; 117 | } 118 | code .global 119 | { 120 | color: #f1c40f; 121 | } 122 | code .zTag:hover .hoverUS 123 | { 124 | border-bottom: 1px dotted #7f8c8d; 125 | cursor: pointer; 126 | } 127 | code .hoverUS:hover 128 | { 129 | color: #f1c40f; 130 | } 131 | 132 | .pMarker::before, 133 | .bMarker::before, 134 | .liMarker::before 135 | { 136 | content: "#"; 137 | display: inline-block; 138 | color: #2980b9; 139 | background: #34495e; 140 | border-radius: 3px; 141 | font: normal 9pt monospace; 142 | padding: 2px; 143 | vertical-align: top; 144 | } 145 | .pMarker::before { content: "

" } 146 | .bMarker::before { content: "" } 147 | .liMarker::before { content: "

  • " } 148 | -------------------------------------------------------------------------------- /src/ext/z_handlers.js: -------------------------------------------------------------------------------- 1 | /* Code is available under the Piblic Domain (Unlicense) */ 2 | 3 | z.addHandler( "addClass", function ( e, data ) { 4 | if ( data[0] ) this.classList.add( data[0] ); 5 | }); 6 | z.addHandler( "removeClass", function ( e, data ) { 7 | if ( data[0] ) this.classList.remove( data[0] ); 8 | }); 9 | z.addHandler( "swapClass", function ( e, data ) { 10 | if ( data[0] ) this.classList.toggle( data[0] ); 11 | }); 12 | 13 | z.addHandler( "addClassIfMatch", function ( e, data ) { 14 | var 15 | className = data[0], 16 | property = data[1], 17 | constant = data[2], 18 | action = (className && property && e.data[property] && e.data[property] == constant)? "add" : "remove" 19 | ; 20 | 21 | this.classList[action]( className ); 22 | }); 23 | z.addHandler( "removeClassIfMatch", function ( e, data ) { 24 | var 25 | className = data[0], 26 | property = data[1], 27 | constant = data[2], 28 | action = (className && property && e.data[property] && e.data[property] != constant)? "add" : "remove" 29 | ; 30 | 31 | this.classList[action]( className ); 32 | }); 33 | 34 | z.addHandler( "addClassIfAttrMatch", function ( e, data ) { 35 | var 36 | className = data[0], 37 | attrName = data[1], 38 | key = this.getAttribute(attrName), 39 | action = (className && key && e.data[key] !== undefined )? "add" : "remove" 40 | ; 41 | 42 | this.classList[action]( className ); 43 | }); 44 | 45 | z.addHandler( "execMethodIfMatch", function ( e, data ) { 46 | var 47 | property = data[0], 48 | constant = data[1], 49 | method = data[2], 50 | mArr = method.split("."), 51 | instance = (mArr.length > 1)? window[mArr[0]] : window, 52 | method = (mArr.length > 1)? mArr[1] : mArr[0], 53 | methodArgs = data.slice(3) 54 | ; 55 | 56 | instance[method].call( window, methodArgs ); 57 | 58 | }); 59 | 60 | z.addHandler( "postMessage", function ( e, data ) { 61 | 62 | rm.postMessage( e.data ); 63 | 64 | }); 65 | z.addHandler( "postMessageIfMatch", function ( e, data ) { 66 | var 67 | property = data[0], 68 | constant = data[1] 69 | ; 70 | 71 | if ( e.data[property] == constant ) rm.postMessage( e.data ); 72 | 73 | }); 74 | z.addHandler( "collectPostMessage", function ( e, data ) { 75 | 76 | var 77 | action = data[0], 78 | propagation = (data[1])? data.slice(1) : "*", 79 | tmpData = e.data 80 | ; 81 | 82 | tmpData.action = action; 83 | if ( !( tmpData.data instanceof Object) ) tmpData.data = {}; 84 | 85 | z.dispatch( { e: "collectData", f: this, p: propagation, data: tmpData.data } ); 86 | 87 | }); 88 | z.addHandler( "collectPostMixin", function ( e, data ) { 89 | 90 | var 91 | propagation = (data[0])? data : "*", 92 | tmpData = e.data 93 | ; 94 | 95 | if ( !( tmpData.data instanceof Object) ) tmpData.data = {}; 96 | 97 | z.dispatch( { e: "collectData", f: this, p: propagation, data: tmpData.data } ); 98 | 99 | }); 100 | 101 | z.addHandler( "fillInData", function ( e, data ) { /* Experimental */ 102 | var 103 | property = data[0] 104 | ; 105 | 106 | if ( e.data[property] !== undefined && e.data[property] !== null ) { 107 | this.innerHTML = e.data[property]; 108 | } 109 | 110 | }); 111 | 112 | z.addHandler( "collectAsArr", function ( e, data ) { 113 | var 114 | property = data[0] 115 | ; 116 | 117 | if ( e.data[property] === undefined ) e.data[property] = []; 118 | 119 | var dataArr = e.data[property]; 120 | 121 | var nextItem = dataArr.length; 122 | 123 | z.dispatch( { e: "collectAsArr", f: this, p: "parent", data: dataArr, __index__: nextItem } ); 124 | 125 | dataArr.reverse(); 126 | 127 | }); 128 | z.addHandler( "collectAsObj", function ( e, data ) { 129 | var 130 | propagation = ( data[0] ) ? data[0] : "parent"; 131 | ; 132 | 133 | if ( e.__index__ === undefined ) return; 134 | 135 | var tmpObj = e.data[ e.__index__ ] = { }; 136 | 137 | z.dispatch( { e: "collectAsObj", f: this, p: propagation, data: tmpObj } ); 138 | 139 | }); 140 | 141 | z.addHandler( "collectData", function ( e, data ) {/* Experimental */ 142 | 143 | try { 144 | 145 | var 146 | name = this.name || this.id || data[0], 147 | nodeName = this.nodeName, 148 | value = "", 149 | eData = e.data 150 | ; 151 | 152 | if ( name === undefined ) return; 153 | 154 | switch ( nodeName ) { 155 | case "SELECT": 156 | if ( this.multiple ) { 157 | value = []; 158 | for ( var i=this.options.length; i--; ) { 159 | var option = this.options[i]; 160 | if ( option.selected ) value.unshift( option.value ); 161 | } 162 | } 163 | else 164 | { 165 | value = this.value; 166 | } 167 | 168 | break; 169 | case "INPUT": 170 | if ( this.type === "checkbox" || this.type === "radio" ) { 171 | value = ( eData[name] instanceof Array )? eData[name] : []; 172 | value.unshift( (this.checked)? this.value : "" ); 173 | } 174 | else 175 | { 176 | value = this.value; 177 | } 178 | break; 179 | default: 180 | value = this.textContent.trim(); 181 | } 182 | eData[name] = value; 183 | 184 | } catch(e){ } 185 | 186 | }); 187 | 188 | z.addHandler( "restoreDefaultValue", function ( e, data ) { 189 | 190 | var 191 | nodeName = this.nodeName 192 | ; 193 | 194 | if ( nodeName === "INPUT" && ( this.type === "checkbox" || this.type === "radio" ) ) { 195 | this.checked = this.defaultChecked; 196 | return; 197 | } 198 | 199 | if ( nodeName === "SELECT" ) { 200 | for( var i=this.options.length; i--; ) { 201 | var option = this.options[i]; 202 | option.selected = option.defaultSelected; 203 | } 204 | } 205 | 206 | if ( this.defaultValue !== undefined ) { 207 | this.value = this.defaultValue; 208 | } 209 | }); 210 | 211 | z.addHandler( "removeChild", function ( e, data ) { 212 | 213 | this.parentNode.removeChild(this); 214 | 215 | }); 216 | 217 | z.addHandler( "clearContent", function ( e, data ) { 218 | 219 | this.innerHTML = ""; 220 | 221 | }); 222 | 223 | z.addHandler( "clearAttr", function ( e, data ) { 224 | 225 | for ( var i=0, l=data.length; i 2 | 3 | 4 | Z :: Формы множеств 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | « 15 |

    Формы множеств

    16 | 17 |
    18 | listTpl 19 | 20 | 21 | { 22 | "kids": 23 | [ 24 | { 25 | "name": "Маша", 26 | "sex": 0, 27 | "eats": 28 | { 29 | "apples": 11 30 | } 31 | }, 32 | { 33 | "name": "Настя", 34 | "sex": 0, 35 | "eats": 36 | { 37 | "apples": 5, 38 | "bananas": 2 39 | } 40 | }, 41 | { 42 | "name": "Тарас", 43 | "sex": 1, 44 | "eats": 45 | { 46 | "borsch": 1 47 | } 48 | } 49 | ] 50 | } 51 | 52 | 53 |
    54 | 55 | 85 | 86 | <div class="content"> 87 | <!-- Темплейтируем данные --> 88 | <e on="init" do="template">listTpl</e> 89 | <exec> 90 | <dispatch e="init"> 91 | { 92 | "kids": 93 | [ 94 | { 95 | "name": "Маша", 96 | "sex": 0, 97 | "eats": 98 | { 99 | "apples": 11 100 | } 101 | }, 102 | { 103 | "name": "Настя", 104 | "sex": 0, 105 | "eats": 106 | { 107 | "apples": 5, 108 | "bananas": 2 109 | } 110 | }, 111 | { 112 | "name": "Тарас", 113 | "sex": 1, 114 | "eats": 115 | { 116 | "borsch": 1 117 | } 118 | } 119 | ] 120 | } 121 | </dispatch> 122 | </exec> 123 | </div> 124 | <template id="zTemplates"> 125 | <template id="listTpl"> 126 | <ul> 127 | <!-- Перебираем элементы массива "kids"--> 128 | <foreach from="kids" item="kid"> 129 | <li> 130 | <!-- Выводим имя --> 131 | <value>kid.name</value> 132 | <!-- 133 | Тут мы хитрим. Мы будем использовать формы множеств 134 | английского языка для определения подходящего глагола 135 | для разных полов. 136 | Первый параметр ("eats") будет ключом в таблице форм 137 | множеств, второй - количеством, третий указывает на 138 | правило, которое будет использовано для нахождения 139 | нужной формы множеств. 140 | --> 141 | <value>"eats"^kid.sex^1</value> 142 | <!-- Перебираем массив "eats" --> 143 | <foreach from="kid.eats" item="amount" key="food"> 144 | <!-- 145 | Запоминаем контент в переменную "txt". 146 | Эта переменная локальна исключительно для тега <li> 147 | --> 148 | <capture to="txt"> 149 | <!-- 150 | Выводим количество и правильную форму множеств. 151 | Ключи форм множеств берутся из названия ключа объекта 152 | с данными 153 | --> 154 | <span class="hl"><value>amount</value></span> <value>food^amount^7</value> 155 | </capture> 156 | </foreach> 157 | <!-- Вставляем контент, который мы запоминали раннее --> 158 | <flush>txt.join(", ")</flush> 159 | </li> 160 | </foreach> 161 | </ul> 162 | </template> 163 | <!-- 164 | Описываем ключи и значения для обычного контента 165 | --> 166 | <plurals rule="7"> 167 | { 168 | "apples": [ "яблоко", "яблока", "яблок" ], 169 | "bananas": [ "банан", "банана", "бананов" ], 170 | "borsch": [ "тарелку борща", "тарелки борща", "тарелок борща" ] 171 | } 172 | </plurals> 173 | <!-- 174 | и для хитрого варианта с определением пола 175 | --> 176 | <plurals rule="1"> 177 | { 178 | "eats": [ "съел", "съела" ] 179 | } 180 | </plurals> 181 | </template> 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /examples/3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Z :: Локализация распространения событий 5 | 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | « 16 |

    Локализация распространения событий

    17 | 18 |
    19 |

    20 | red 21 | red 22 | Параграф 23 | 24 | red 25 | red 26 | 1 27 | 28 |

    29 |

    30 | red 31 | red 32 | Параграф 33 | 34 | red 35 | red 36 | 2 37 | 38 |

    39 |
      40 |
    • 41 | red 42 | red 43 | Элемент списка 44 | 45 | red 46 | red 47 | 1 48 | 49 |
    • 50 |
    • 51 | red 52 | red 53 | Элемент списка 54 | 55 | red 56 | red 57 | 2 58 | 59 |
    • 60 |
    61 |

    62 | Раскрасить 63 | 64 | 65 | 66 | 67 | <b> 68 | 69 | или 70 | 71 | 72 | 73 | 74 | <li> 75 | 76 | или 77 | 78 | 79 | 80 | 81 | <p> 82 | 83 | или 84 | 85 | 86 | 87 | 88 | отменить раскраску 89 | 90 |

    91 |
    92 | 93 | <p> 94 | <!-- 95 | Устанавливаем 2 z-обработчика 96 | По событию "makeTextRed" установим элементу класс "red" 97 | а по "clear" - удалим его 98 | --> 99 | <e on="makeTextRed" do="addClass">red</e> 100 | <e on="clear" do="removeClass">red</e> 101 | Параграф 102 | <b> 103 | <!-- Z-обработчики для всех тегов будут одинаковыми --> 104 | <e on="makeTextRed" do="addClass">red</e> 105 | <e on="clear" do="removeClass">red</e> 106 | 1 107 | </b> 108 | </p> 109 | <p> 110 | <e on="makeTextRed" do="addClass">red</e> 111 | <e on="clear" do="removeClass">red</e> 112 | Параграф 113 | <b> 114 | <e on="makeTextRed" do="addClass">red</e> 115 | <e on="clear" do="removeClass">red</e> 116 | 2 117 | </b> 118 | </p> 119 | <ul> 120 | <li> 121 | <e on="makeTextRed" do="addClass">red</e> 122 | <e on="clear" do="removeClass">red</e> 123 | Элемент списка 124 | <b> 125 | <e on="makeTextRed" do="addClass">red</e> 126 | <e on="clear" do="removeClass">red</e> 127 | 1 128 | </b> 129 | </li> 130 | <li> 131 | <e on="makeTextRed" do="addClass">red</e> 132 | <e on="clear" do="removeClass">red</e> 133 | Элемент списка 134 | <b> 135 | <e on="makeTextRed" do="addClass">red</e> 136 | <e on="clear" do="removeClass">red</e> 137 | 2 138 | </b> 139 | </li> 140 | </ul> 141 | <p> 142 | Раскрасить 143 | <a href="#"> 144 | <handler on="click"> 145 | <!-- 146 | Запускаем событие "makeTextRed" 147 | начиная от <body> 148 | и распространяем его только на <b> 149 | --> 150 | <dispatch e="makeTextRed" f="body" p="b"></dispatch> 151 | </handler> 152 | <b> 153 | </a> 154 | или 155 | <a href="#"> 156 | <handler on="click"> 157 | <!-- Распространяем событие только на <li> --> 158 | <dispatch e="makeTextRed" f="body" p="li"></dispatch> 159 | </handler> 160 | <li> 161 | </a> 162 | или 163 | <a href="#"> 164 | <handler on="click"> 165 | <!-- И только на <p> --> 166 | <dispatch e="makeTextRed" f="body" p="p"></dispatch> 167 | </handler> 168 | <p> 169 | </a> 170 | или 171 | <a href="#"> 172 | <handler on="click"> 173 | <!-- 174 | Запускаем событие очистки "clear" 175 | для всех элементов сразу 176 | --> 177 | <dispatch e="clear" f="body"></dispatch> 178 | </handler> 179 | отменить раскраску 180 | </a> 181 | </p> 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /examples/todomvc/node_modules/todomvc-common/base.js: -------------------------------------------------------------------------------- 1 | /* global _ */ 2 | (function () { 3 | 'use strict'; 4 | 5 | /* jshint ignore:start */ 6 | // Underscore's Template Module 7 | // Courtesy of underscorejs.org 8 | var _ = (function (_) { 9 | _.defaults = function (object) { 10 | if (!object) { 11 | return object; 12 | } 13 | for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { 14 | var iterable = arguments[argsIndex]; 15 | if (iterable) { 16 | for (var key in iterable) { 17 | if (object[key] == null) { 18 | object[key] = iterable[key]; 19 | } 20 | } 21 | } 22 | } 23 | return object; 24 | } 25 | 26 | // By default, Underscore uses ERB-style template delimiters, change the 27 | // following template settings to use alternative delimiters. 28 | _.templateSettings = { 29 | evaluate : /<%([\s\S]+?)%>/g, 30 | interpolate : /<%=([\s\S]+?)%>/g, 31 | escape : /<%-([\s\S]+?)%>/g 32 | }; 33 | 34 | // When customizing `templateSettings`, if you don't want to define an 35 | // interpolation, evaluation or escaping regex, we need one that is 36 | // guaranteed not to match. 37 | var noMatch = /(.)^/; 38 | 39 | // Certain characters need to be escaped so that they can be put into a 40 | // string literal. 41 | var escapes = { 42 | "'": "'", 43 | '\\': '\\', 44 | '\r': 'r', 45 | '\n': 'n', 46 | '\t': 't', 47 | '\u2028': 'u2028', 48 | '\u2029': 'u2029' 49 | }; 50 | 51 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; 52 | 53 | // JavaScript micro-templating, similar to John Resig's implementation. 54 | // Underscore templating handles arbitrary delimiters, preserves whitespace, 55 | // and correctly escapes quotes within interpolated code. 56 | _.template = function(text, data, settings) { 57 | var render; 58 | settings = _.defaults({}, settings, _.templateSettings); 59 | 60 | // Combine delimiters into one regular expression via alternation. 61 | var matcher = new RegExp([ 62 | (settings.escape || noMatch).source, 63 | (settings.interpolate || noMatch).source, 64 | (settings.evaluate || noMatch).source 65 | ].join('|') + '|$', 'g'); 66 | 67 | // Compile the template source, escaping string literals appropriately. 68 | var index = 0; 69 | var source = "__p+='"; 70 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { 71 | source += text.slice(index, offset) 72 | .replace(escaper, function(match) { return '\\' + escapes[match]; }); 73 | 74 | if (escape) { 75 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 76 | } 77 | if (interpolate) { 78 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 79 | } 80 | if (evaluate) { 81 | source += "';\n" + evaluate + "\n__p+='"; 82 | } 83 | index = offset + match.length; 84 | return match; 85 | }); 86 | source += "';\n"; 87 | 88 | // If a variable is not specified, place data values in local scope. 89 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 90 | 91 | source = "var __t,__p='',__j=Array.prototype.join," + 92 | "print=function(){__p+=__j.call(arguments,'');};\n" + 93 | source + "return __p;\n"; 94 | 95 | try { 96 | render = new Function(settings.variable || 'obj', '_', source); 97 | } catch (e) { 98 | e.source = source; 99 | throw e; 100 | } 101 | 102 | if (data) return render(data, _); 103 | var template = function(data) { 104 | return render.call(this, data, _); 105 | }; 106 | 107 | // Provide the compiled function source as a convenience for precompilation. 108 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; 109 | 110 | return template; 111 | }; 112 | 113 | return _; 114 | })({}); 115 | 116 | if (location.hostname === 'todomvc.com') { 117 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 118 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 119 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 120 | })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); 121 | ga('create', 'UA-31081062-1', 'auto'); 122 | ga('send', 'pageview'); 123 | } 124 | /* jshint ignore:end */ 125 | 126 | function redirect() { 127 | if (location.hostname === 'tastejs.github.io') { 128 | location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); 129 | } 130 | } 131 | 132 | function findRoot() { 133 | var base = location.href.indexOf('examples/'); 134 | return location.href.substr(0, base); 135 | } 136 | 137 | function getFile(file, callback) { 138 | if (!location.host) { 139 | return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); 140 | } 141 | 142 | var xhr = new XMLHttpRequest(); 143 | 144 | xhr.open('GET', findRoot() + file, true); 145 | xhr.send(); 146 | 147 | xhr.onload = function () { 148 | if (xhr.status === 200 && callback) { 149 | callback(xhr.responseText); 150 | } 151 | }; 152 | } 153 | 154 | function Learn(learnJSON, config) { 155 | if (!(this instanceof Learn)) { 156 | return new Learn(learnJSON, config); 157 | } 158 | 159 | var template, framework; 160 | 161 | if (typeof learnJSON !== 'object') { 162 | try { 163 | learnJSON = JSON.parse(learnJSON); 164 | } catch (e) { 165 | return; 166 | } 167 | } 168 | 169 | if (config) { 170 | template = config.template; 171 | framework = config.framework; 172 | } 173 | 174 | if (!template && learnJSON.templates) { 175 | template = learnJSON.templates.todomvc; 176 | } 177 | 178 | if (!framework && document.querySelector('[data-framework]')) { 179 | framework = document.querySelector('[data-framework]').dataset.framework; 180 | } 181 | 182 | this.template = template; 183 | 184 | if (learnJSON.backend) { 185 | this.frameworkJSON = learnJSON.backend; 186 | this.frameworkJSON.issueLabel = framework; 187 | this.append({ 188 | backend: true 189 | }); 190 | } else if (learnJSON[framework]) { 191 | this.frameworkJSON = learnJSON[framework]; 192 | this.frameworkJSON.issueLabel = framework; 193 | this.append(); 194 | } 195 | 196 | this.fetchIssueCount(); 197 | } 198 | 199 | Learn.prototype.append = function (opts) { 200 | var aside = document.createElement('aside'); 201 | aside.innerHTML = _.template(this.template, this.frameworkJSON); 202 | aside.className = 'learn'; 203 | 204 | if (opts && opts.backend) { 205 | // Remove demo link 206 | var sourceLinks = aside.querySelector('.source-links'); 207 | var heading = sourceLinks.firstElementChild; 208 | var sourceLink = sourceLinks.lastElementChild; 209 | // Correct link path 210 | var href = sourceLink.getAttribute('href'); 211 | sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); 212 | sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; 213 | } else { 214 | // Localize demo links 215 | var demoLinks = aside.querySelectorAll('.demo-link'); 216 | Array.prototype.forEach.call(demoLinks, function (demoLink) { 217 | if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { 218 | demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); 219 | } 220 | }); 221 | } 222 | 223 | document.body.className = (document.body.className + ' learn-bar').trim(); 224 | document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); 225 | }; 226 | 227 | Learn.prototype.fetchIssueCount = function () { 228 | var issueLink = document.getElementById('issue-count-link'); 229 | if (issueLink) { 230 | var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos'); 231 | var xhr = new XMLHttpRequest(); 232 | xhr.open('GET', url, true); 233 | xhr.onload = function (e) { 234 | var parsedResponse = JSON.parse(e.target.responseText); 235 | if (parsedResponse instanceof Array) { 236 | var count = parsedResponse.length; 237 | if (count !== 0) { 238 | issueLink.innerHTML = 'This app has ' + count + ' open issues'; 239 | document.getElementById('issue-count').style.display = 'inline'; 240 | } 241 | } 242 | }; 243 | xhr.send(); 244 | } 245 | }; 246 | 247 | redirect(); 248 | getFile('learn.json', Learn); 249 | })(); 250 | -------------------------------------------------------------------------------- /examples/todomvc/node_modules/todomvc-app-css/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | button { 8 | margin: 0; 9 | padding: 0; 10 | border: 0; 11 | background: none; 12 | font-size: 100%; 13 | vertical-align: baseline; 14 | font-family: inherit; 15 | font-weight: inherit; 16 | color: inherit; 17 | -webkit-appearance: none; 18 | appearance: none; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-font-smoothing: antialiased; 21 | font-smoothing: antialiased; 22 | } 23 | 24 | body { 25 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 26 | line-height: 1.4em; 27 | background: #f5f5f5; 28 | color: #4d4d4d; 29 | min-width: 230px; 30 | max-width: 550px; 31 | margin: 0 auto; 32 | -webkit-font-smoothing: antialiased; 33 | -moz-font-smoothing: antialiased; 34 | font-smoothing: antialiased; 35 | font-weight: 300; 36 | } 37 | 38 | button, 39 | input[type="checkbox"] { 40 | outline: none; 41 | } 42 | 43 | .hidden { 44 | display: none; 45 | } 46 | 47 | .todoapp { 48 | background: #fff; 49 | margin: 130px 0 40px 0; 50 | position: relative; 51 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 52 | 0 25px 50px 0 rgba(0, 0, 0, 0.1); 53 | } 54 | 55 | .todoapp input::-webkit-input-placeholder { 56 | font-style: italic; 57 | font-weight: 300; 58 | color: #e6e6e6; 59 | } 60 | 61 | .todoapp input::-moz-placeholder { 62 | font-style: italic; 63 | font-weight: 300; 64 | color: #e6e6e6; 65 | } 66 | 67 | .todoapp input::input-placeholder { 68 | font-style: italic; 69 | font-weight: 300; 70 | color: #e6e6e6; 71 | } 72 | 73 | .todoapp h1 { 74 | position: absolute; 75 | top: -155px; 76 | width: 100%; 77 | font-size: 100px; 78 | font-weight: 100; 79 | text-align: center; 80 | color: rgba(175, 47, 47, 0.15); 81 | -webkit-text-rendering: optimizeLegibility; 82 | -moz-text-rendering: optimizeLegibility; 83 | text-rendering: optimizeLegibility; 84 | } 85 | 86 | .new-todo, 87 | .edit { 88 | position: relative; 89 | margin: 0; 90 | width: 100%; 91 | font-size: 24px; 92 | font-family: inherit; 93 | font-weight: inherit; 94 | line-height: 1.4em; 95 | border: 0; 96 | outline: none; 97 | color: inherit; 98 | padding: 6px; 99 | border: 1px solid #999; 100 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 101 | box-sizing: border-box; 102 | -webkit-font-smoothing: antialiased; 103 | -moz-font-smoothing: antialiased; 104 | font-smoothing: antialiased; 105 | } 106 | 107 | .new-todo { 108 | padding: 16px 16px 16px 60px; 109 | border: none; 110 | background: rgba(0, 0, 0, 0.003); 111 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 112 | } 113 | 114 | .main { 115 | position: relative; 116 | z-index: 2; 117 | border-top: 1px solid #e6e6e6; 118 | } 119 | 120 | label[for='toggle-all'] { 121 | display: none; 122 | } 123 | 124 | .toggle-all { 125 | position: absolute; 126 | top: -55px; 127 | left: -12px; 128 | width: 60px; 129 | height: 34px; 130 | text-align: center; 131 | border: none; /* Mobile Safari */ 132 | } 133 | 134 | .toggle-all:before { 135 | content: '❯'; 136 | font-size: 22px; 137 | color: #e6e6e6; 138 | padding: 10px 27px 10px 27px; 139 | } 140 | 141 | .toggle-all:checked:before { 142 | color: #737373; 143 | } 144 | 145 | .todo-list { 146 | margin: 0; 147 | padding: 0; 148 | list-style: none; 149 | } 150 | 151 | .todo-list li { 152 | position: relative; 153 | font-size: 24px; 154 | border-bottom: 1px solid #ededed; 155 | } 156 | 157 | .todo-list li:last-child { 158 | border-bottom: none; 159 | } 160 | 161 | .todo-list li.editing { 162 | border-bottom: none; 163 | padding: 0; 164 | } 165 | 166 | .todo-list li.editing .edit { 167 | display: block; 168 | width: 506px; 169 | padding: 13px 17px 12px 17px; 170 | margin: 0 0 0 43px; 171 | } 172 | 173 | .todo-list li.editing .view { 174 | display: none; 175 | } 176 | 177 | .todo-list li .toggle { 178 | text-align: center; 179 | width: 40px; 180 | /* auto, since non-WebKit browsers doesn't support input styling */ 181 | height: auto; 182 | position: absolute; 183 | top: 0; 184 | bottom: 0; 185 | margin: auto 0; 186 | border: none; /* Mobile Safari */ 187 | -webkit-appearance: none; 188 | appearance: none; 189 | } 190 | 191 | .todo-list li .toggle:after { 192 | content: url('data:image/svg+xml;utf8,'); 193 | } 194 | 195 | .todo-list li .toggle:checked:after { 196 | content: url('data:image/svg+xml;utf8,'); 197 | } 198 | 199 | .todo-list li label { 200 | white-space: pre-line; 201 | word-break: break-all; 202 | padding: 15px 60px 15px 15px; 203 | margin-left: 45px; 204 | display: block; 205 | line-height: 1.2; 206 | transition: color 0.4s; 207 | } 208 | 209 | .todo-list li.completed label { 210 | color: #d9d9d9; 211 | text-decoration: line-through; 212 | } 213 | 214 | .todo-list li .destroy { 215 | display: none; 216 | position: absolute; 217 | top: 0; 218 | right: 10px; 219 | bottom: 0; 220 | width: 40px; 221 | height: 40px; 222 | margin: auto 0; 223 | font-size: 30px; 224 | color: #cc9a9a; 225 | margin-bottom: 11px; 226 | transition: color 0.2s ease-out; 227 | } 228 | 229 | .todo-list li .destroy:hover { 230 | color: #af5b5e; 231 | } 232 | 233 | .todo-list li .destroy:after { 234 | content: '×'; 235 | } 236 | 237 | .todo-list li:hover .destroy { 238 | display: block; 239 | } 240 | 241 | .todo-list li .edit { 242 | display: none; 243 | } 244 | 245 | .todo-list li.editing:last-child { 246 | margin-bottom: -1px; 247 | } 248 | 249 | .footer { 250 | color: #777; 251 | padding: 10px 15px; 252 | height: 20px; 253 | text-align: center; 254 | border-top: 1px solid #e6e6e6; 255 | } 256 | 257 | .footer:before { 258 | content: ''; 259 | position: absolute; 260 | right: 0; 261 | bottom: 0; 262 | left: 0; 263 | height: 50px; 264 | overflow: hidden; 265 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 266 | 0 8px 0 -3px #f6f6f6, 267 | 0 9px 1px -3px rgba(0, 0, 0, 0.2), 268 | 0 16px 0 -6px #f6f6f6, 269 | 0 17px 2px -6px rgba(0, 0, 0, 0.2); 270 | } 271 | 272 | .todo-count { 273 | float: left; 274 | text-align: left; 275 | } 276 | 277 | .todo-count strong { 278 | font-weight: 300; 279 | } 280 | 281 | .filters { 282 | margin: 0; 283 | padding: 0; 284 | list-style: none; 285 | position: absolute; 286 | right: 0; 287 | left: 0; 288 | } 289 | 290 | .filters li { 291 | display: inline; 292 | } 293 | 294 | .filters li a { 295 | color: inherit; 296 | margin: 3px; 297 | padding: 3px 7px; 298 | text-decoration: none; 299 | border: 1px solid transparent; 300 | border-radius: 3px; 301 | } 302 | 303 | .filters li a.selected, 304 | .filters li a:hover { 305 | border-color: rgba(175, 47, 47, 0.1); 306 | } 307 | 308 | .filters li a.selected { 309 | border-color: rgba(175, 47, 47, 0.2); 310 | } 311 | 312 | .clear-completed, 313 | html .clear-completed:active { 314 | float: right; 315 | position: relative; 316 | line-height: 20px; 317 | text-decoration: none; 318 | cursor: pointer; 319 | position: relative; 320 | } 321 | 322 | .clear-completed:hover { 323 | text-decoration: underline; 324 | } 325 | 326 | .info { 327 | margin: 65px auto 0; 328 | color: #bfbfbf; 329 | font-size: 10px; 330 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 331 | text-align: center; 332 | } 333 | 334 | .info p { 335 | line-height: 1; 336 | } 337 | 338 | .info a { 339 | color: inherit; 340 | text-decoration: none; 341 | font-weight: 400; 342 | } 343 | 344 | .info a:hover { 345 | text-decoration: underline; 346 | } 347 | 348 | /* 349 | Hack to remove background from Mobile Safari. 350 | Can't use it globally since it destroys checkboxes in Firefox 351 | */ 352 | @media screen and (-webkit-min-device-pixel-ratio:0) { 353 | .toggle-all, 354 | .todo-list li .toggle { 355 | background: none; 356 | } 357 | 358 | .todo-list li .toggle { 359 | height: 40px; 360 | } 361 | 362 | .toggle-all { 363 | -webkit-transform: rotate(90deg); 364 | transform: rotate(90deg); 365 | -webkit-appearance: none; 366 | appearance: none; 367 | } 368 | } 369 | 370 | @media (max-width: 430px) { 371 | .footer { 372 | height: 50px; 373 | } 374 | 375 | .filters { 376 | bottom: 10px; 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /examples/todomvc/js/z_handlers.js: -------------------------------------------------------------------------------- 1 | /* Code is available under the Piblic Domain (Unlicense) */ 2 | 3 | z.addHandler( "debug", function ( e, data ) { 4 | 5 | console.log(this, e, data); 6 | 7 | }); 8 | 9 | z.addHandler( "addClass", function ( e, data ) { 10 | if ( data[0] ) this.classList.add( data[0] ); 11 | }); 12 | z.addHandler( "removeClass", function ( e, data ) { 13 | if ( data[0] ) this.classList.remove( data[0] ); 14 | }); 15 | z.addHandler( "swapClass", function ( e, data ) { 16 | if ( data[0] ) this.classList.toggle( data[0] ); 17 | }); 18 | 19 | z.addHandler( "clearValue", function ( e, data ) { 20 | this.value = ""; 21 | }); 22 | 23 | z.addHandler( "smartClassProcess", function ( e, data ) { 24 | var 25 | eData = e.data, 26 | reverse = !(data[0] == "add" || data[0] == "addOnly"), 27 | oneWay = (data[0].indexOf("Only") !== -1), 28 | className = data[1], 29 | expressions = data.slice(2), 30 | res = false, 31 | thisNode = this 32 | ; 33 | for ( var i=expressions.length; i-->0; ) { 34 | var 35 | exprArr = expressions[i].split("="), 36 | existCheck = false 37 | ; 38 | expr = exprArr.map( function (e) { 39 | if ( e.indexOf("$") == 0 ) return eData[e.slice(1)]; 40 | if ( e.indexOf("@") == 0 ) return thisNode.getAttribute(e.slice(1)); 41 | if ( e.indexOf("?") == 0 ) { 42 | existCheck = true; 43 | try { return !!(eData[e.slice(1)].length) } catch (e) { return false }; 44 | } 45 | return e 46 | }) 47 | 48 | if ( existCheck ) { 49 | if (expr[0]) res = true; 50 | } 51 | else 52 | { 53 | if ( expr[0] == expr[1] ) res = true; 54 | if ( exprArr[1] === undefined && expr[0] ) res = true; 55 | } 56 | }; 57 | 58 | var 59 | aRes = (reverse)? !res : res, 60 | action = (aRes)? "add" : "remove" 61 | ; 62 | 63 | if (data[0] == "toggle" && res) { 64 | var hasClass = this.classList.contains(className); 65 | if (aRes && hasClass) action = "remove"; 66 | if (!aRes && !hasClass) action = "add"; 67 | } 68 | 69 | if (oneWay && !res) return; 70 | 71 | this.classList[action]( className ); 72 | }); 73 | 74 | z.addHandler( "setAttr", function ( e, data ) { 75 | 76 | var 77 | eData = e.data, 78 | attrName = data[0], 79 | defaultValue = data[1] || 1, 80 | propertyName = data[2] || attrName, 81 | attrValue = (eData[propertyName] !== undefined)? eData[propertyName] : defaultValue 82 | ; 83 | 84 | if ( !attrName ) return; 85 | 86 | this.setAttribute( attrName, attrValue ); 87 | 88 | }); 89 | 90 | z.addHandler( "checkIf", function ( e, data ) { 91 | 92 | var 93 | eData = e.data, 94 | expressions = data, 95 | res = false, 96 | thisNode = this 97 | ; 98 | 99 | for ( var i=expressions.length; i-->0; ) { 100 | var exprArr = expressions[i].split("="); 101 | expr = exprArr.map( function (e) { 102 | if ( e.indexOf("$") == 0 ) return eData[e.slice(1)]; 103 | if ( e.indexOf("@") == 0 ) return thisNode.getAttribute(e.slice(1)); 104 | return e 105 | }) 106 | 107 | if ( expr[0] == expr[1] ) res = true; 108 | } 109 | 110 | this.checked = res; 111 | 112 | }); 113 | 114 | z.addHandler( "collectData", function ( e, data ) { 115 | 116 | var 117 | name = this.name || this.id || data[0], 118 | mode = data[1] || "replace", 119 | nodeName = this.nodeName, 120 | value = "", 121 | eData = e.data, 122 | keyExists = false 123 | ; 124 | 125 | if ( name === undefined ) return; 126 | 127 | if ( eData.hasOwnProperty(name) ) keyExists = true; 128 | if ( keyExists && mode == "once" ) return; 129 | 130 | switch ( nodeName ) { 131 | case "SELECT": 132 | if ( this.multiple ) { 133 | value = []; 134 | for ( var i=this.options.length; i--; ) { 135 | var option = this.options[i]; 136 | if ( option.selected ) value.unshift( option.value ); 137 | } 138 | } 139 | else 140 | { 141 | value = this.value; 142 | } 143 | 144 | break; 145 | case "INPUT": 146 | if ( this.type === "checkbox" ) { 147 | if ( mode == "simple" ) { 148 | value = (this.checked); 149 | } 150 | else 151 | { 152 | value = ( eData[name] instanceof Array )? eData[name] : []; 153 | value.unshift( (this.checked)? this.value : "" ); 154 | } 155 | } 156 | else 157 | { 158 | if ( this.type === "radio" && !this.checked ) return; 159 | value = this.value; 160 | } 161 | break; 162 | case "TEXTAREA": 163 | { 164 | value = this.value; 165 | break; 166 | } 167 | 168 | default: 169 | value = this.textContent.trim(); 170 | } 171 | 172 | eData[name] = value; 173 | 174 | }); 175 | 176 | z.addHandler( "removeChildAndDispatch", function ( e, data ) { 177 | 178 | var 179 | parentNode = this.parentNode, 180 | eData = e.data, 181 | execArr = eData.dispatch || [], 182 | that = this, 183 | execArr = execArr.map(function(d){ 184 | d.f = ( d.f )? z.getParentNode( that, d.f ) : parentNode; 185 | d.data = eData; 186 | return d; 187 | }) 188 | ; 189 | 190 | parentNode.removeChild(this); 191 | 192 | for ( var i=0, l=execArr.length; i 2 | 3 | 4 | 5 | Z • TodoMVC 6 | 7 | 8 | 9 | 10 | 11 | 12 |
    13 | todoappStoredData 14 | filter,all 15 | todoLayout,replace,propagate,* 16 |
    17 | 18 | 23 | 24 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | -------------------------------------------------------------------------------- /examples/4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Z :: Темплейтирование 5 | 6 | 7 | 8 | 9 | 45 | 46 | 47 | 48 | « 49 |

    Темплейтирование

    50 | 51 |
    52 | tabContentTpl 53 | 54 | 55 | { 56 | "tabs": 57 | [ 58 | { 59 | "id": "fruit", 60 | "name": "Фрукты", 61 | "values": [ "Яблоки", "Сливы", "Апельсины" ] 62 | }, 63 | { 64 | "id": "vegetables", 65 | "name": "Овощи", 66 | "values": [ "Капуста", "Горох", "Морковь", "Тыква" ] 67 | }, 68 | { 69 | "id": "something", 70 | "name": "Нечто" 71 | } 72 | ] 73 | } 74 | 75 | 76 |
    77 | 78 | 115 | 116 | <div class="content"> 117 | <!-- 118 | Устанавливаем контейнеру обработчик события "init". 119 | Он будет темплейтировать данные, которые придут 120 | вместе с событием 121 | В качестве шаблона будет взят template#tabContentTpl 122 | --> 123 | <e on="init" do="template">tabContentTpl</e> 124 | <!-- Делаем вызов инструкций --> 125 | <exec> 126 | <!-- 127 | Запускаем событие "init", которому устанавливаем 128 | JSON-объект в качестве данных 129 | --> 130 | <dispatch e="init"> 131 | { 132 | "tabs": 133 | [ 134 | { 135 | "id": "fruit", 136 | "name": "Фрукты", 137 | "values": [ "Яблоки", "Сливы", "Апельсины" ] 138 | }, 139 | { 140 | "id": "vegetables", 141 | "name": "Овощи", 142 | "values": [ "Капуста", "Горох", "Морковь", "Тыква" ] 143 | }, 144 | { 145 | "id": "something", 146 | "name": "Нечто" 147 | } 148 | ] 149 | } 150 | </dispatch> 151 | </exec> 152 | </div> 153 | <!-- Все темплейты должны находиться в данном контейнере --> 154 | <template id="zTemplates"> 155 | <!-- Темплейт для отображения табов --> 156 | <template id="tabContentTpl"> 157 | <div class="tabContainer"> 158 | <!-- 159 | Этот диспатчер будет запускать событие "tabActivate", 160 | которое будет распростаняться от контейнера div.tabContainer 161 | только на прямых потомков 162 | --> 163 | <dispatch id="tabActivator" e="tabActivate" p="childNodes"></dispatch> 164 | <!-- Итерируем пришедшие с событием "init" данные.--> 165 | <foreach from="tabs" item="tab"> 166 | <a href="#" class="tab"> 167 | <!-- 168 | Устанавливаем тегу <a> обработчик события "tabActivate" 169 | по которому будем устанавливать класс "active" 170 | если в пришедших с событием данных 171 | будет присутствовать ключ, название которого 172 | будет совпадать со значением атрибута "data-id" 173 | --> 174 | <e on="tabActivate" do="addClassIfAttrMatch">active,data-id</e> 175 | <!-- 176 | Устанавливаем тегу <a> атрибут "data-id" 177 | который равен "tab.id" 178 | --> 179 | <attr name="data-id"><value>tab.id</value></attr> 180 | <!-- 181 | Устанавливаем тегу <a> обработчик 182 | DOM-события "click" 183 | --> 184 | <handler on="click"> 185 | <!-- 186 | По которому будем запускать диспатчер с id 187 | равным "tabActivator" (описан выше), 188 | а в качестве параметров передадим JSON-объект 189 | --> 190 | <dispatch use="tabActivator">{ "<value>tab.id</value>": 1 }</dispatch> 191 | </handler> 192 | <!-- Выводим название таба --> 193 | <value>tab.name</value> 194 | </a> 195 | <div class="tabContent"> 196 | <!-- 197 | Подключаем темплейт template#listTpl, 198 | в который передадим данные таба 199 | --> 200 | <include tpl="listTpl"><value>JSON.stringify(tab)</value></include> 201 | </div> 202 | </foreach> 203 | <exec> 204 | <!-- 205 | Активируем таб путем запуска события "tabActivator", 206 | которому передадим в качестве параметра id первого таба 207 | --> 208 | <dispatch use="tabActivator">{ "<value>tabs[0].id</value>": 1 }</dispatch> 209 | </exec> 210 | </div> 211 | </template> 212 | <template id="listTpl"> 213 | <!-- Если массив "values" не пустой --> 214 | <if expr="values.length"> 215 | <!-- выводим его значения --> 216 | <then> 217 | <ul> 218 | <foreach from="values" item="item"> 219 | <li><value>item</value></li> 220 | </foreach> 221 | </ul> 222 | </then> 223 | <!-- иначе выводим сообщение --> 224 | <else> 225 | <p>Извините, тут пусто.</p> 226 | </else> 227 | </if> 228 | </template> 229 | </template> 230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /examples/6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Z :: Интерактивный пример 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | « 15 |

    Интерактивный пример

    16 | 17 |
    18 |

    Данный пример показывает, как оперировать контейнером начала распространения события и зонами видимости

    19 |

    Диспатчеры интерактивные, можно смело тыкать в них палочкой

    20 |
    21 | 22 | 146 | 147 | hlhl<div class="content"> 148 | <exec> 149 | <!-- 150 | Простые примеры. 151 | Контейнер начала распространения события: div.content 152 | --> 153 | <dispatch e="test"></dispatch> 154 | <dispatch e="test" p="global"></dispatch> 155 | <dispatch e="test" p="parent"></dispatch> 156 | <dispatch e="test" p="childNodes"></dispatch> 157 | <dispatch e="test" p="childNodes,b,.special"></dispatch> 158 | </exec> 159 | <e on="test" do="debug"></e> 160 | hlhl<div class="container_1"> 161 | <e on="test" do="debug"></e> 162 | hlhl<ul> 163 | <e on="test" do="debug"></e> 164 | hlhl<li> 165 | <e on="test" do="debug"></e> 166 | hlhlhl<b> 167 | <e on="test" do="debug"></e> 168 | <e hlhlglobal on="test" do="debug"></e> 169 | </b> 170 | </li> 171 | hlhl<li class="special"> 172 | <e on="test" do="debug"></e> 173 | hlhl<ul> 174 | <e on="test" do="debug"></e> 175 | hlhl<li> 176 | <e on="test" do="debug"></e> 177 | hlhl<b> 178 | <e on="test" do="debug"></e> 179 | </b> 180 | </li> 181 | <exec> 182 | <!-- 183 | Контейнером начала распространения события сделаем 184 | родителя с классом .special 185 | --> 186 | <dispatch e="test" f=".special"></dispatch> 187 | <dispatch e="test" f=".special" p="global"></dispatch> 188 | <dispatch e="test" f=".special" p="parent"></dispatch> 189 | <dispatch e="test" f=".special" p="childNodes"></dispatch> 190 | <dispatch e="test" f=".special" p="childNodes,b,.special"></dispatch> 191 | </exec> 192 | </ul> 193 | </li> 194 | hlhl<li> 195 | <e on="test" do="debug"></e> 196 | </li> 197 | </ul> 198 | </div> 199 | <exec> 200 | <!-- 201 | Сделаем началом распространения события контейнер #listItem 202 | --> 203 | <dispatch e="test" f="#listItem"></dispatch> 204 | <dispatch e="test" f="#listItem" p="global"></dispatch> 205 | <dispatch e="test" f="#listItem" p="parent"></dispatch> 206 | <dispatch e="test" f="#listItem" p="childNodes"></dispatch> 207 | <dispatch e="test" f="#listItem" p="childNodes,b,.special"></dispatch> 208 | </exec> 209 | hlhlhl<div class="container_2"> 210 | <e on="test" do="debug"></e> 211 | <e hlhlglobal on="test" do="debug"></e> 212 | hlhl<ul> 213 | <e on="test" do="debug"></e> 214 | hlhl<li> 215 | <e on="test" do="debug"></e> 216 | hlhl<b> 217 | <e on="test" do="debug"></e> 218 | </b> 219 | </li> 220 | hlhl<li id="listItem"> 221 | <e on="test" do="debug"></e> 222 | hlhl<ul> 223 | <e on="test" do="debug"></e> 224 | <!-- 225 | Эти диспатчеры не вызываются, а только инициализируются 226 | --> 227 | hlhl<dispatch id="d1" e="test" f=".container_2"></dispatch> 228 | hlhl<dispatch id="d2" e="test" f=".container_2" p="global"></dispatch> 229 | hlhl<dispatch id="d3" e="test" f=".container_2" p="parent"></dispatch> 230 | hlhl<dispatch id="d4" e="test" f=".container_2" p="childNodes"></dispatch> 231 | hlhl<dispatch id="d5" e="test" f=".container_2" p="childNodes,b,.special"></dispatch> 232 | hlhl<li> 233 | <e on="test" do="debug"></e> 234 | hlhl<b class="special"> 235 | <e on="test" do="debug"></e> 236 | <exec> 237 | <!-- 238 | Контейнером начала распространения события сделаем 239 | родителя с именем LI 240 | --> 241 | <dispatch e="test" f="li"></dispatch> 242 | <dispatch e="test" f="li" p="global"></dispatch> 243 | <dispatch e="test" f="li" p="parent"></dispatch> 244 | <dispatch e="test" f="li" p="childNodes"></dispatch> 245 | <dispatch e="test" f="li" p="childNodes,b,.special"></dispatch> 246 | </exec> 247 | </b> 248 | </li> 249 | </ul> 250 | </li> 251 | hlhl<li> 252 | <e on="test" do="debug"></e> 253 | </li> 254 | </ul> 255 | </div> 256 | <exec> 257 | <!-- 258 | Вызываем удаленные диспатчеры 259 | --> 260 | <dispatch use="d1"></dispatch> 261 | <dispatch use="d2"></dispatch> 262 | <dispatch use="d3"></dispatch> 263 | <dispatch use="d4"></dispatch> 264 | <dispatch use="d5"></dispatch> 265 | </exec> 266 | </div> 267 | 268 | 269 | 270 | -------------------------------------------------------------------------------- /src/core/z.js: -------------------------------------------------------------------------------- 1 | /* 2 | Z (zet) framework, version 0.8.5 3 | Code is available under the Piblic Domain (Unlicense) 4 | */ 5 | 6 | var z = (function(){ 7 | 8 | var 9 | handlers = {}, 10 | templates = document, 11 | globals = null, 12 | plurals = [], 13 | pluralFuncs = [ 14 | /* 0: Chinese */ [1, function(n) { return 0 }], 15 | /* 1: English */ [2, function(n) { return (n!=1)?1:0 }], 16 | /* 2: French */ [2, function(n) { return (n>1)?1:0 }], 17 | /* 3: Latvian */ [3, function(n) { return (n%10==1&&n%100!=11)?1:(n!=0)?2:0 }], 18 | /* 4: Scottish Gaelic */ [4, function(n) { return (n==1||n==11)?0:(n==2||n==12)?1:(n>0&&n<20)?2:3 }], 19 | /* 5: Romanian */ [3, function(n) { return (n==1)?0:(n==0||n%100>0&&n%100<20)?1:2 }], 20 | /* 6: Lithuanian */ [3, function(n) { return (n%10==1&&n%100!=11)?0:(n%10>=2&&(n%100<10||n%100>=20))?2:1 }], 21 | /* 7: Ukrainian */ [3, function(n) { return (n%10==1&&n%100!=11)?0:(n%10>=2&&n%10<=4&&(n%100<10||n%100>=20))?1:2 }], 22 | /* 8: Slovak */ [3, function(n) { return (n==1)?0:(n>=2&&n<=4)?1:2 }], 23 | /* 9: Polish */ [3, function(n) { return (n==1)?0:(n%10>=2&&n%10<=4&&(n%100<10||n%100>=20))?1:2 }], 24 | /* 10: Slovenian */ [4, function(n) { return (n%100==1)?0:(n%100==2)?1:(n%100==3||n%100==4)?2:3 }], 25 | /* 11: Irish Gaeilge */ [5, function(n) { return (n==1)?0:(n==2)?1:(n>=3&&n<=6)?2:(n>=7&&n<=10)?3:4 }], 26 | /* 12: Arabic */ [6, function(n) { return (n==0)?5:(n==1)?0:(n==2)?1:(n%100>=3&&n%100<=10)?2:(n%100>=11&&n%100<=99)?3:4 }], 27 | /* 13: Maltese */ [4, function(n) { return (n==1)?0:(n==0||n%100>0&&n%100<=10)?1:(n%100>10&&n%100<20)?2:3 }], 28 | /* 14: Macedonian */ [3, function(n) { return (n%10==1)?0:(n%10==2)?1:2 }], 29 | /* 15: Icelandic */ [2, function(n) { return (n%10==1&&n%100!=11)?0:1 }], 30 | /* 16: Breton */ [5, function(n) { return (n%10==1&&n%100!=11&&n%100!=71&&n%100!=91)?0:(n%10==2&&n%100!=12&&n%100!=72&&n%100!=92)?1:((n%10==3||n%10==4||n%10==9)&&n%100!=13&&n%100!=14&&n%100!=19&&n%100!=73&&n%100!=74&&n%100!=79&&n%100!=93&&n%100!=94&&n%100!=99)?2:(n%1000000==0&&n!=0)?3:4 }] 31 | ] 32 | ; 33 | 34 | var init = function ( ) { 35 | try { 36 | addZStyles(); 37 | detachTemplateContainer(); 38 | addGlobalContainer(); 39 | processAllNodes(document); 40 | } catch(e){} 41 | }; 42 | 43 | var addZStyles = function( ) { 44 | var 45 | zElements = [ "template", "e", "dispatch", "exec", "hidden", "handler", "#zTemplates", "#zGlobal" ], 46 | styleNode = document.createElement('style'), 47 | styleSheet 48 | ; 49 | styleNode.appendChild(document.createTextNode(''));// Safari magic 50 | 51 | document.head.appendChild(styleNode); 52 | 53 | styleSheet = styleNode.sheet; 54 | 55 | styleSheet.insertRule( zElements.join(", ") + "{ display: none !important; }", 0 ); 56 | 57 | }; 58 | 59 | var detachTemplateContainer = function () { 60 | 61 | var tplNode = $("zTemplates"); 62 | if ( tplNode ) { 63 | var 64 | tplNode = tplNode.parentNode.removeChild(tplNode), 65 | iFrameNode = document.createElement("iframe"), 66 | bodyNode = document.body 67 | ; 68 | 69 | iFrameNode.width = iFrameNode.height = 0; 70 | iFrameNode.id = "zTemplates"; 71 | bodyNode.insertBefore(iFrameNode, bodyNode.firstChild); 72 | 73 | var iFrameDoc = iFrameNode.contentDocument; 74 | 75 | iFrameDoc.open(); 76 | iFrameDoc.write("\n" + tplNode.innerHTML + ""); 77 | iFrameDoc.close(); 78 | 79 | templates = iFrameNode.contentDocument; 80 | 81 | } 82 | 83 | }; 84 | 85 | var addGlobalContainer = function () { 86 | 87 | var 88 | bodyNode = document.body 89 | ; 90 | 91 | globals = document.createElement("global"); 92 | globals.id = "zGlobal"; 93 | 94 | bodyNode.insertBefore(globals, bodyNode.firstChild); 95 | 96 | }; 97 | 98 | var processAllNodes = function ( container ) { 99 | processENodes(container); 100 | processHandlerNodes(container); 101 | processWrapperNodes(container); 102 | processPluralNodes(); 103 | processExecNodes(container); 104 | }; 105 | 106 | var processENodes = function ( container ) { 107 | 108 | var eList = container.getElementsByTagName( "e" ); 109 | 110 | for ( var i=0, l=eList.length; i 1)? path[1] || path[2] : path[0], 482 | absolutePath = !(path[2]) 483 | parentNode = (path.length > 1)? getParentNode( node, path[0] ) : null 484 | ; 485 | if (parentNode) { 486 | if (absolutePath) { 487 | for (var i=parentNode.childNodes.length; i--;) { 488 | var 489 | childNode = parentNode.childNodes.item(i), 490 | nodeName = childNode.nodeName.toUpperCase() || "", 491 | nameAttr = childNode.getAttribute && childNode.getAttribute("name") || "" 492 | ; 493 | if ( nodeName === "DISPATCH" && nameAttr == id ) return childNode; 494 | } 495 | } 496 | else 497 | { 498 | return parentNode.querySelector("dispatch[name='" + id + "']"); 499 | } 500 | } 501 | else 502 | { 503 | return $(id); 504 | } 505 | }; 506 | 507 | var getEventObj = function ( node, mixin ) { 508 | 509 | var 510 | eventObj = createEObjFromDispatchNode( node ), 511 | tmpData = eventObj.data 512 | ; 513 | 514 | try { 515 | var useNode = findDispatchNode(node, eventObj.use); 516 | if ( useNode ) eventObj = createEObjFromDispatchNode( useNode ); 517 | if ( tmpData === "{}" && eventObj.data ) tmpData = eventObj.data; 518 | 519 | } catch ( e ) { }; 520 | 521 | tmpData = JSON.parse(tmpData); 522 | 523 | if (mixin) extend(mixin, tmpData); 524 | 525 | eventObj.data = tmpData; 526 | 527 | return eventObj; 528 | }; 529 | 530 | var template = function ( event, options ) { 531 | 532 | var 533 | tplID = options[0], 534 | mode = ( options[1] )? options[1] : "replace", 535 | containerNode = event.c, 536 | broadcast = !!options[2], 537 | childNodesCache = [], 538 | 539 | tplNode = $tpl(tplID) 540 | ; 541 | 542 | if ( !tplNode || ( mode === "once" && containerNode._templated_ ) ) return containerNode; 543 | 544 | var tmpFragment = document.createDocumentFragment(); 545 | 546 | iterateTemplate(tplNode, tmpFragment, event.data, containerNode); 547 | 548 | if ( ["replace", "once"].indexOf(mode) !== -1 ) containerNode.innerHTML = ""; 549 | 550 | for ( var i=0, l=tmpFragment.childNodes.length; i 1 ) { 636 | var 637 | retFunc = new Function ( "_data_", "with(_data_){ try { return " + exprArr[1] + " } catch (e) { return '' } }" ), 638 | pluralVal = retFunc(data), 639 | type = exprArr[2] || 1, 640 | txt = getPluralText( type, txt, pluralVal ) 641 | ; 642 | }; 643 | if (container.nodeName === "TEXTAREA") { 644 | container.value = txt; 645 | } 646 | else 647 | { 648 | container.appendChild( document.createTextNode(txt) ); 649 | } 650 | break; 651 | 652 | case "inner_html": 653 | var 654 | defaultVal = childNode.getAttribute("default") || "", 655 | expr = childNode.textContent, 656 | retFunc = new Function ( "_data_", "with(_data_){ try { return " + expr + " } catch (e) { return '' } }" ), 657 | res = retFunc(data), 658 | res = ( res === undefined || res === null )? "" : res, 659 | res = ( res === "" && defaultVal )? defaultVal : res 660 | ; 661 | if ( container.innerHTML !== undefined ) { 662 | container.innerHTML += res; 663 | } 664 | else 665 | { 666 | var tmpElement = document.createElement("div"); 667 | tmpElement.innerHTML = res; 668 | while (tmpElement.childNodes.length) { 669 | container.appendChild(tmpElement.childNodes.item(0)); 670 | } 671 | } 672 | break; 673 | 674 | case "dom": 675 | var 676 | expr = childNode.textContent, 677 | retFunc = new Function ( "_data_", "with(_data_){ try { return " + expr + " } catch (e) { return '' } }" ), 678 | res = retFunc(data) 679 | ; 680 | 681 | if ( !res ) break; 682 | 683 | while (res.length) { 684 | container.appendChild(res.item(0)); 685 | } 686 | 687 | break; 688 | 689 | case "val_by": 690 | var 691 | defaultVal = childNode.getAttribute("default") || "", 692 | expr = childNode.textContent, 693 | retFunc = new Function ( "_data_", "with(_data_){ try { return " + expr + " } catch (e) { return '' } }" ), 694 | res = retFunc(data), 695 | retFunc = new Function ( "_data_", "with(_data_){ try { return " + res + " } catch (e) { return '' } }" ), 696 | res = retFunc(data), 697 | res = ( res === undefined || res === null )? "" : res, 698 | res = ( res === "" && defaultVal )? defaultVal : res, 699 | tmpNode = document.createTextNode(res) 700 | ; 701 | container.appendChild( tmpNode ); 702 | break; 703 | 704 | case "include": 705 | var 706 | tplId = childNode.getAttribute("tpl"), 707 | tmpFragment = document.createDocumentFragment() 708 | ; 709 | 710 | iterateTemplate( childNode, tmpFragment, data, domNode ); 711 | 712 | var 713 | expr = tmpFragment.textContent.trim(), 714 | retFunc = new Function ( "_data_", "with(_data_){ try { return " + expr + " } catch (e) { return '' } }" ), 715 | res = retFunc(data) || data, 716 | tplNode = $tpl(tplId), 717 | tmpFragment = document.createDocumentFragment() 718 | ; 719 | 720 | iterateTemplate( tplNode, container, res, domNode ); 721 | 722 | break; 723 | 724 | case "tag": 725 | 726 | var 727 | cloneNode = childNode.cloneNode(true), 728 | nameNode = getChildByName.call( cloneNode, "name" ), 729 | tmpFragment = document.createDocumentFragment() 730 | ; 731 | 732 | if ( !nameNode ) break; 733 | 734 | iterateTemplate( nameNode, tmpFragment, data ); 735 | var tagName = tmpFragment.textContent.trim(); 736 | cloneNode.removeChild( nameNode ); 737 | 738 | if ( tagName ) { 739 | var newNode = document.createElement( tagName ); 740 | iterateTemplate( cloneNode, newNode, data, domNode ); 741 | container.appendChild( newNode ); 742 | } 743 | else 744 | { 745 | tmpFragment = document.createDocumentFragment(); 746 | iterateTemplate( cloneNode, tmpFragment, data, domNode ); 747 | container.appendChild( tmpFragment ); 748 | } 749 | break; 750 | 751 | case "attr": 752 | 753 | var 754 | nameAttr = childNode.getAttribute("name"), 755 | tmpFragment = document.createDocumentFragment(), 756 | targetNode = ( container.nodeName.toUpperCase() === "WRAPPER" )? container.getElementsByTagName("*").item(0) : container, 757 | targetNode = ( targetNode.nodeType == 11 )? domNode : targetNode 758 | ; 759 | 760 | iterateTemplate( childNode, tmpFragment, data, domNode ); 761 | targetNode.setAttribute( nameAttr, tmpFragment.textContent.trim().replace(/\s+/g," ") ); 762 | 763 | break; 764 | 765 | case "class": 766 | var 767 | cloneNode = childNode.cloneNode(), 768 | targetNode = ( container.nodeName.toUpperCase() === "WRAPPER" )? container.getElementsByTagName("*").item(0) : container, 769 | targetNode = ( targetNode.nodeType == 11 )? domNode : targetNode 770 | ; 771 | iterateTemplate( childNode, cloneNode, data, domNode ); 772 | targetNode.classList.add( cloneNode.textContent.trim() ); 773 | break; 774 | 775 | case "foreach": 776 | var 777 | fromAttr = childNode.getAttribute("from"), 778 | itemAttr = childNode.getAttribute("item"), 779 | keyAttr = childNode.getAttribute("key") 780 | ; 781 | 782 | if ( !fromAttr || !itemAttr ) break; 783 | 784 | var 785 | retFunc = new Function ( "_data_", "with(_data_){ try { return " + fromAttr + " } catch (e) { return '' } }" ), 786 | res = retFunc(data), 787 | instanceOf = Object.prototype.toString.call(res).slice(8,-1) 788 | ; 789 | 790 | if ( Array.isArray(res) || instanceOf == "FileList" ) { 791 | for ( var j=0, k=res.length; j= ys && dtv < tds)? "yesterday" : rel, 888 | rel = (dtv >= tds && dtv < tms)? "today" : rel, 889 | rel = (dtv >= tms && dtv < tme)? "tomorrow" : rel, 890 | rel = ( dtv >= tme )? "future" : rel, 891 | 892 | dtObj = 893 | { 894 | raw: dt, 895 | y: dt[getFullYear](), 896 | m: dt[getMonth](), 897 | d: dt[getDate](), 898 | H: dt[getHours](), 899 | M: dt[getMinutes](), 900 | S: dt[getSeconds](), 901 | wd: dt[getDay](), 902 | daysDiff: daysDiff, 903 | rel: rel, 904 | utc: utc 905 | } 906 | ; 907 | } 908 | 909 | iterateTemplate( childNode, container, { dt: dtObj }, domNode ); 910 | 911 | break; 912 | 913 | case "z:table": 914 | case "z:caption": 915 | case "z:colgroup": 916 | case "z:col": 917 | case "z:thead": 918 | case "z:tfoot": 919 | case "z:tbody": 920 | case "z:tr": 921 | case "z:td": 922 | case "z:th": 923 | case "z:select": 924 | case "z:option": 925 | case "z:textarea": 926 | var tmpName = nodeName.substr(2); 927 | var tmpNode = document.createElement( tmpName ); 928 | cloneAttributes(childNode, tmpNode); 929 | iterateTemplate( childNode, tmpNode, data, tmpNode ); 930 | container.appendChild( tmpNode ); 931 | break; 932 | 933 | default: 934 | var cloneNode = childNode.cloneNode(); 935 | iterateTemplate( childNode, cloneNode, data, cloneNode ); 936 | container.appendChild( cloneNode ); 937 | } 938 | 939 | } 940 | }; 941 | 942 | var addHandler = function ( handlerName, handlerFunc ) { 943 | handlers[handlerName] = handlerFunc; 944 | }; 945 | 946 | var getParentNode = function ( node, path ) { 947 | 948 | try { 949 | var firstChar = path.charAt(0); 950 | 951 | switch ( firstChar ) { 952 | case "#": return $(path.substr(1)); 953 | case ".": 954 | var parentNode = node.parentNode; 955 | var className = path.substr(1); 956 | if ( !className ) return node; 957 | while ( parentNode && !parentNode.classList.contains(className) ) parentNode = parentNode.parentNode; 958 | return parentNode; 959 | default: 960 | var nodeName = path.toUpperCase(); 961 | var parentNode = node.parentNode; 962 | while ( parentNode && parentNode.nodeName != nodeName ) parentNode = parentNode.parentNode; 963 | return parentNode; 964 | } 965 | 966 | } 967 | catch ( e ) { return node.parentNode || null; }; 968 | }; 969 | 970 | var getChildByName = function ( nodeName ) { 971 | for ( var i=0, l=this.childNodes.length; i freezeTime ) { 1061 | freezeObj[eventType] = currentTime; 1062 | handler( DOMEvnt ); 1063 | } 1064 | else 1065 | { 1066 | DOMEvnt.preventDefault(true); 1067 | DOMEvnt.stopPropagation(true); 1068 | } 1069 | }; 1070 | var handlerWithAllows = function ( handler, allowDefault, allowPropagation, DOMEvnt ) { 1071 | DOMEvnt._z_ = { 1072 | allowDefault: allowDefault, 1073 | allowPropagation: allowPropagation 1074 | } 1075 | handler( DOMEvnt ); 1076 | }; 1077 | var handlerWithExcludingSelection = function ( handler, DOMEvnt ) { 1078 | if ( window.getSelection().toString() === "" ) { 1079 | handler( DOMEvnt ); 1080 | } 1081 | else 1082 | { 1083 | DOMEvnt.preventDefault(true); 1084 | DOMEvnt.stopPropagation(true); 1085 | }; 1086 | }; 1087 | 1088 | var getPluralText = function ( type, key, value ) { 1089 | 1090 | if ( !(plurals[type] instanceof Object) ) return ""; 1091 | 1092 | try { 1093 | var 1094 | pluralValuesByType = plurals[type], 1095 | pluralArr = pluralValuesByType[key], 1096 | pArrLength = pluralArr.length || 0, 1097 | pFunc = pluralFuncs[type], 1098 | requiredLength = pFunc[0], 1099 | pFunc = pFunc[1] 1100 | ; 1101 | 1102 | if ( pArrLength !== requiredLength ) return ""; 1103 | 1104 | return pluralArr[pFunc(value)] || ""; 1105 | 1106 | } catch ( e ) { return "" } 1107 | 1108 | }; 1109 | 1110 | var extend = function ( from, to ) { 1111 | 1112 | if (from == null || typeof from != "object") return from; 1113 | if (from.constructor != Object && from.constructor != Array) return from; 1114 | if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function || from.constructor == String || from.constructor == Number || from.constructor == Boolean) return new from.constructor(from); 1115 | 1116 | to = to || new from.constructor(); 1117 | 1118 | for (var name in from) { 1119 | to[name] = (typeof to[name] == "undefined") ? extend(from[name], null) : to[name]; 1120 | } 1121 | 1122 | return to; 1123 | } 1124 | 1125 | var isDOMEvent = function ( DOMEvnt ) { 1126 | 1127 | try { 1128 | return ( DOMEvnt instanceof window.Event || DOMEvnt instanceof templates.defaultView.Event ); 1129 | } catch ( e ) { return false } 1130 | 1131 | }; 1132 | 1133 | document.addEventListener("DOMContentLoaded", function(event) { z.init() }); 1134 | 1135 | var map = { 1136 | init: init, 1137 | dispatch: dispatch, 1138 | dispatchById: dispatchById, 1139 | addHandler: addHandler, 1140 | template: template, 1141 | getParentNode: getParentNode 1142 | }; 1143 | 1144 | return map; 1145 | 1146 | }()); 1147 | 1148 | z.addHandler( "template", function ( e, data ) { 1149 | e.c = this; 1150 | z.template( e, data ); 1151 | }); 1152 | z.addHandler( "templateIfMatch", function ( e, data ) { 1153 | 1154 | var 1155 | property = data[0], 1156 | constant = data[1], 1157 | options = data.slice(2) 1158 | ; 1159 | 1160 | if ( e.data[property] != constant ) return; 1161 | 1162 | e.c = this; 1163 | z.template( e, options ); 1164 | 1165 | }); 1166 | z.addHandler( "templateIfAttrMatch", function ( e, data ) { 1167 | 1168 | var 1169 | property = data[0], 1170 | tmpPath = property.split("."), 1171 | attrName = tmpPath.pop(), 1172 | value = this.getAttribute(attrName), 1173 | exprFunc = new Function ( "_data_", "with(_data_){ try { return " + property + "} catch(e) { return false } }" ), 1174 | options = data.slice(1) 1175 | ; 1176 | 1177 | if ( exprFunc(e.data) != value ) return; 1178 | 1179 | e.c = this; 1180 | z.template( e, options ); 1181 | 1182 | }); 1183 | z.addHandler( "templateIfExists", function ( e, data ) { 1184 | 1185 | var 1186 | property = data[0], 1187 | options = data.slice(1) 1188 | ; 1189 | 1190 | if ( e.data[property] === undefined ) return; 1191 | 1192 | e.c = this; 1193 | z.template( e, options ); 1194 | 1195 | }); 1196 | z.addHandler( "templateScopeIfExists", function ( e, data ) { 1197 | var 1198 | property = data[0], 1199 | options = data.slice(1), 1200 | exprFunc = new Function ( "_data_", "with(_data_){ try { return " + property + "} catch(e) { return undefined } }" ), 1201 | targetObj = exprFunc (e.data), 1202 | propName = property.split(".").pop(), 1203 | eventClone = Object.create(e), 1204 | localDataObj 1205 | ; 1206 | 1207 | if ( targetObj === undefined ) return; 1208 | 1209 | if ( targetObj instanceof Array ) { 1210 | localDataObj = targetObj.slice(); 1211 | } 1212 | else 1213 | { 1214 | localDataObj = JSON.parse(JSON.stringify(targetObj)); 1215 | } 1216 | 1217 | eventClone.c = this; 1218 | eventClone.data = {}; 1219 | eventClone.data[propName] = localDataObj; 1220 | 1221 | z.template( eventClone, options ); 1222 | }); 1223 | 1224 | z.addHandler( "templateOnce", function ( e, data ) { 1225 | e.c = this; 1226 | data[1] = "once"; 1227 | z.template( e, data ); 1228 | }); 1229 | 1230 | z.addHandler( "broadcastEvent", function ( e, data ) { 1231 | var newE = Object.create(e, { f: { value: this }, p: { value: data }, b: { value: true } } ); 1232 | newE.data = e.data; 1233 | z.dispatch( newE ); 1234 | }); 1235 | 1236 | z.addHandler( "dispatchEvent", function ( e, data ) { 1237 | var 1238 | nodeID = data[0], 1239 | mode = data[1] || "default", 1240 | mixin = undefined 1241 | ; 1242 | 1243 | if ( !nodeID ) return; 1244 | 1245 | if ( mode === "checkEmpty" && this.textContent.trim() !== "" ) { return; } 1246 | 1247 | if ( mode === "mixin" ) { 1248 | if ( data[2] ) { 1249 | mixin = {}; 1250 | mixin[data[2]] = e.data; 1251 | } 1252 | else 1253 | { 1254 | mixin = e.data; 1255 | } 1256 | } 1257 | 1258 | z.dispatchById.call( this, nodeID, mixin ); 1259 | }); 1260 | -------------------------------------------------------------------------------- /examples/todomvc/js/z.js: -------------------------------------------------------------------------------- 1 | /* 2 | Z (zet) framework, version 0.8.5 3 | Code is available under the Piblic Domain (Unlicense) 4 | */ 5 | 6 | var z = (function(){ 7 | 8 | var 9 | handlers = {}, 10 | templates = document, 11 | globals = null, 12 | plurals = [], 13 | pluralFuncs = [ 14 | /* 0: Chinese */ [1, function(n) { return 0 }], 15 | /* 1: English */ [2, function(n) { return (n!=1)?1:0 }], 16 | /* 2: French */ [2, function(n) { return (n>1)?1:0 }], 17 | /* 3: Latvian */ [3, function(n) { return (n%10==1&&n%100!=11)?1:(n!=0)?2:0 }], 18 | /* 4: Scottish Gaelic */ [4, function(n) { return (n==1||n==11)?0:(n==2||n==12)?1:(n>0&&n<20)?2:3 }], 19 | /* 5: Romanian */ [3, function(n) { return (n==1)?0:(n==0||n%100>0&&n%100<20)?1:2 }], 20 | /* 6: Lithuanian */ [3, function(n) { return (n%10==1&&n%100!=11)?0:(n%10>=2&&(n%100<10||n%100>=20))?2:1 }], 21 | /* 7: Ukrainian */ [3, function(n) { return (n%10==1&&n%100!=11)?0:(n%10>=2&&n%10<=4&&(n%100<10||n%100>=20))?1:2 }], 22 | /* 8: Slovak */ [3, function(n) { return (n==1)?0:(n>=2&&n<=4)?1:2 }], 23 | /* 9: Polish */ [3, function(n) { return (n==1)?0:(n%10>=2&&n%10<=4&&(n%100<10||n%100>=20))?1:2 }], 24 | /* 10: Slovenian */ [4, function(n) { return (n%100==1)?0:(n%100==2)?1:(n%100==3||n%100==4)?2:3 }], 25 | /* 11: Irish Gaeilge */ [5, function(n) { return (n==1)?0:(n==2)?1:(n>=3&&n<=6)?2:(n>=7&&n<=10)?3:4 }], 26 | /* 12: Arabic */ [6, function(n) { return (n==0)?5:(n==1)?0:(n==2)?1:(n%100>=3&&n%100<=10)?2:(n%100>=11&&n%100<=99)?3:4 }], 27 | /* 13: Maltese */ [4, function(n) { return (n==1)?0:(n==0||n%100>0&&n%100<=10)?1:(n%100>10&&n%100<20)?2:3 }], 28 | /* 14: Macedonian */ [3, function(n) { return (n%10==1)?0:(n%10==2)?1:2 }], 29 | /* 15: Icelandic */ [2, function(n) { return (n%10==1&&n%100!=11)?0:1 }], 30 | /* 16: Breton */ [5, function(n) { return (n%10==1&&n%100!=11&&n%100!=71&&n%100!=91)?0:(n%10==2&&n%100!=12&&n%100!=72&&n%100!=92)?1:((n%10==3||n%10==4||n%10==9)&&n%100!=13&&n%100!=14&&n%100!=19&&n%100!=73&&n%100!=74&&n%100!=79&&n%100!=93&&n%100!=94&&n%100!=99)?2:(n%1000000==0&&n!=0)?3:4 }] 31 | ] 32 | ; 33 | 34 | var init = function ( ) { 35 | try { 36 | addZStyles(); 37 | detachTemplateContainer(); 38 | addGlobalContainer(); 39 | processAllNodes(document); 40 | } catch(e){} 41 | }; 42 | 43 | var addZStyles = function( ) { 44 | var 45 | zElements = [ "template", "e", "dispatch", "exec", "hidden", "handler", "#zTemplates", "#zGlobal" ], 46 | styleNode = document.createElement('style'), 47 | styleSheet 48 | ; 49 | styleNode.appendChild(document.createTextNode(''));// Safari magic 50 | 51 | document.head.appendChild(styleNode); 52 | 53 | styleSheet = styleNode.sheet; 54 | 55 | styleSheet.insertRule( zElements.join(", ") + "{ display: none !important; }", 0 ); 56 | 57 | }; 58 | 59 | var detachTemplateContainer = function () { 60 | 61 | var tplNode = $("zTemplates"); 62 | if ( tplNode ) { 63 | var 64 | tplNode = tplNode.parentNode.removeChild(tplNode), 65 | iFrameNode = document.createElement("iframe"), 66 | bodyNode = document.body 67 | ; 68 | 69 | iFrameNode.width = iFrameNode.height = 0; 70 | iFrameNode.id = "zTemplates"; 71 | bodyNode.insertBefore(iFrameNode, bodyNode.firstChild); 72 | 73 | var iFrameDoc = iFrameNode.contentDocument; 74 | 75 | iFrameDoc.open(); 76 | iFrameDoc.write("\n" + tplNode.innerHTML + ""); 77 | iFrameDoc.close(); 78 | 79 | templates = iFrameNode.contentDocument; 80 | 81 | } 82 | 83 | }; 84 | 85 | var addGlobalContainer = function () { 86 | 87 | var 88 | bodyNode = document.body 89 | ; 90 | 91 | globals = document.createElement("global"); 92 | globals.id = "zGlobal"; 93 | 94 | bodyNode.insertBefore(globals, bodyNode.firstChild); 95 | 96 | }; 97 | 98 | var processAllNodes = function ( container ) { 99 | processENodes(container); 100 | processHandlerNodes(container); 101 | processWrapperNodes(container); 102 | processPluralNodes(); 103 | processExecNodes(container); 104 | }; 105 | 106 | var processENodes = function ( container ) { 107 | 108 | var eList = container.getElementsByTagName( "e" ); 109 | 110 | for ( var i=0, l=eList.length; i 1)? path[1] || path[2] : path[0], 482 | absolutePath = !(path[2]) 483 | parentNode = (path.length > 1)? getParentNode( node, path[0] ) : null 484 | ; 485 | if (parentNode) { 486 | if (absolutePath) { 487 | for (var i=parentNode.childNodes.length; i--;) { 488 | var 489 | childNode = parentNode.childNodes.item(i), 490 | nodeName = childNode.nodeName.toUpperCase() || "", 491 | nameAttr = childNode.getAttribute && childNode.getAttribute("name") || "" 492 | ; 493 | if ( nodeName === "DISPATCH" && nameAttr == id ) return childNode; 494 | } 495 | } 496 | else 497 | { 498 | return parentNode.querySelector("dispatch[name='" + id + "']"); 499 | } 500 | } 501 | else 502 | { 503 | return $(id); 504 | } 505 | }; 506 | 507 | var getEventObj = function ( node, mixin ) { 508 | 509 | var 510 | eventObj = createEObjFromDispatchNode( node ), 511 | tmpData = eventObj.data 512 | ; 513 | 514 | try { 515 | var useNode = findDispatchNode(node, eventObj.use); 516 | if ( useNode ) eventObj = createEObjFromDispatchNode( useNode ); 517 | if ( tmpData === "{}" && eventObj.data ) tmpData = eventObj.data; 518 | 519 | } catch ( e ) { }; 520 | 521 | tmpData = JSON.parse(tmpData); 522 | 523 | if (mixin) extend(mixin, tmpData); 524 | 525 | eventObj.data = tmpData; 526 | 527 | return eventObj; 528 | }; 529 | 530 | var template = function ( event, options ) { 531 | 532 | var 533 | tplID = options[0], 534 | mode = ( options[1] )? options[1] : "replace", 535 | containerNode = event.c, 536 | broadcast = !!options[2], 537 | childNodesCache = [], 538 | 539 | tplNode = $tpl(tplID) 540 | ; 541 | 542 | if ( !tplNode || ( mode === "once" && containerNode._templated_ ) ) return containerNode; 543 | 544 | var tmpFragment = document.createDocumentFragment(); 545 | 546 | iterateTemplate(tplNode, tmpFragment, event.data, containerNode); 547 | 548 | if ( ["replace", "once"].indexOf(mode) !== -1 ) containerNode.innerHTML = ""; 549 | 550 | for ( var i=0, l=tmpFragment.childNodes.length; i 1 ) { 636 | var 637 | retFunc = new Function ( "_data_", "with(_data_){ try { return " + exprArr[1] + " } catch (e) { return '' } }" ), 638 | pluralVal = retFunc(data), 639 | type = exprArr[2] || 1, 640 | txt = getPluralText( type, txt, pluralVal ) 641 | ; 642 | }; 643 | if (container.nodeName === "TEXTAREA") { 644 | container.value = txt; 645 | } 646 | else 647 | { 648 | container.appendChild( document.createTextNode(txt) ); 649 | } 650 | break; 651 | 652 | case "inner_html": 653 | var 654 | defaultVal = childNode.getAttribute("default") || "", 655 | expr = childNode.textContent, 656 | retFunc = new Function ( "_data_", "with(_data_){ try { return " + expr + " } catch (e) { return '' } }" ), 657 | res = retFunc(data), 658 | res = ( res === undefined || res === null )? "" : res, 659 | res = ( res === "" && defaultVal )? defaultVal : res 660 | ; 661 | if ( container.innerHTML !== undefined ) { 662 | container.innerHTML += res; 663 | } 664 | else 665 | { 666 | var tmpElement = document.createElement("div"); 667 | tmpElement.innerHTML = res; 668 | while (tmpElement.childNodes.length) { 669 | container.appendChild(tmpElement.childNodes.item(0)); 670 | } 671 | } 672 | break; 673 | 674 | case "dom": 675 | var 676 | expr = childNode.textContent, 677 | retFunc = new Function ( "_data_", "with(_data_){ try { return " + expr + " } catch (e) { return '' } }" ), 678 | res = retFunc(data) 679 | ; 680 | 681 | if ( !res ) break; 682 | 683 | while (res.length) { 684 | container.appendChild(res.item(0)); 685 | } 686 | 687 | break; 688 | 689 | case "val_by": 690 | var 691 | defaultVal = childNode.getAttribute("default") || "", 692 | expr = childNode.textContent, 693 | retFunc = new Function ( "_data_", "with(_data_){ try { return " + expr + " } catch (e) { return '' } }" ), 694 | res = retFunc(data), 695 | retFunc = new Function ( "_data_", "with(_data_){ try { return " + res + " } catch (e) { return '' } }" ), 696 | res = retFunc(data), 697 | res = ( res === undefined || res === null )? "" : res, 698 | res = ( res === "" && defaultVal )? defaultVal : res, 699 | tmpNode = document.createTextNode(res) 700 | ; 701 | container.appendChild( tmpNode ); 702 | break; 703 | 704 | case "include": 705 | var 706 | tplId = childNode.getAttribute("tpl"), 707 | tmpFragment = document.createDocumentFragment() 708 | ; 709 | 710 | iterateTemplate( childNode, tmpFragment, data, domNode ); 711 | 712 | var 713 | expr = tmpFragment.textContent.trim(), 714 | retFunc = new Function ( "_data_", "with(_data_){ try { return " + expr + " } catch (e) { return '' } }" ), 715 | res = retFunc(data) || data, 716 | tplNode = $tpl(tplId), 717 | tmpFragment = document.createDocumentFragment() 718 | ; 719 | 720 | iterateTemplate( tplNode, container, res, domNode ); 721 | 722 | break; 723 | 724 | case "tag": 725 | 726 | var 727 | cloneNode = childNode.cloneNode(true), 728 | nameNode = getChildByName.call( cloneNode, "name" ), 729 | tmpFragment = document.createDocumentFragment() 730 | ; 731 | 732 | if ( !nameNode ) break; 733 | 734 | iterateTemplate( nameNode, tmpFragment, data ); 735 | var tagName = tmpFragment.textContent.trim(); 736 | cloneNode.removeChild( nameNode ); 737 | 738 | if ( tagName ) { 739 | var newNode = document.createElement( tagName ); 740 | iterateTemplate( cloneNode, newNode, data, domNode ); 741 | container.appendChild( newNode ); 742 | } 743 | else 744 | { 745 | tmpFragment = document.createDocumentFragment(); 746 | iterateTemplate( cloneNode, tmpFragment, data, domNode ); 747 | container.appendChild( tmpFragment ); 748 | } 749 | break; 750 | 751 | case "attr": 752 | 753 | var 754 | nameAttr = childNode.getAttribute("name"), 755 | tmpFragment = document.createDocumentFragment(), 756 | targetNode = ( container.nodeName.toUpperCase() === "WRAPPER" )? container.getElementsByTagName("*").item(0) : container, 757 | targetNode = ( targetNode.nodeType == 11 )? domNode : targetNode 758 | ; 759 | 760 | iterateTemplate( childNode, tmpFragment, data, domNode ); 761 | targetNode.setAttribute( nameAttr, tmpFragment.textContent.trim().replace(/\s+/g," ") ); 762 | 763 | break; 764 | 765 | case "class": 766 | var 767 | cloneNode = childNode.cloneNode(), 768 | targetNode = ( container.nodeName.toUpperCase() === "WRAPPER" )? container.getElementsByTagName("*").item(0) : container, 769 | targetNode = ( targetNode.nodeType == 11 )? domNode : targetNode 770 | ; 771 | iterateTemplate( childNode, cloneNode, data, domNode ); 772 | targetNode.classList.add( cloneNode.textContent.trim() ); 773 | break; 774 | 775 | case "foreach": 776 | var 777 | fromAttr = childNode.getAttribute("from"), 778 | itemAttr = childNode.getAttribute("item"), 779 | keyAttr = childNode.getAttribute("key") 780 | ; 781 | 782 | if ( !fromAttr || !itemAttr ) break; 783 | 784 | var 785 | retFunc = new Function ( "_data_", "with(_data_){ try { return " + fromAttr + " } catch (e) { return '' } }" ), 786 | res = retFunc(data), 787 | instanceOf = Object.prototype.toString.call(res).slice(8,-1) 788 | ; 789 | 790 | if ( Array.isArray(res) || instanceOf == "FileList" ) { 791 | for ( var j=0, k=res.length; j= ys && dtv < tds)? "yesterday" : rel, 888 | rel = (dtv >= tds && dtv < tms)? "today" : rel, 889 | rel = (dtv >= tms && dtv < tme)? "tomorrow" : rel, 890 | rel = ( dtv >= tme )? "future" : rel, 891 | 892 | dtObj = 893 | { 894 | raw: dt, 895 | y: dt[getFullYear](), 896 | m: dt[getMonth](), 897 | d: dt[getDate](), 898 | H: dt[getHours](), 899 | M: dt[getMinutes](), 900 | S: dt[getSeconds](), 901 | wd: dt[getDay](), 902 | daysDiff: daysDiff, 903 | rel: rel, 904 | utc: utc 905 | } 906 | ; 907 | } 908 | 909 | iterateTemplate( childNode, container, { dt: dtObj }, domNode ); 910 | 911 | break; 912 | 913 | case "z:table": 914 | case "z:caption": 915 | case "z:colgroup": 916 | case "z:col": 917 | case "z:thead": 918 | case "z:tfoot": 919 | case "z:tbody": 920 | case "z:tr": 921 | case "z:td": 922 | case "z:th": 923 | case "z:select": 924 | case "z:option": 925 | case "z:textarea": 926 | var tmpName = nodeName.substr(2); 927 | var tmpNode = document.createElement( tmpName ); 928 | cloneAttributes(childNode, tmpNode); 929 | iterateTemplate( childNode, tmpNode, data, tmpNode ); 930 | container.appendChild( tmpNode ); 931 | break; 932 | 933 | default: 934 | var cloneNode = childNode.cloneNode(); 935 | iterateTemplate( childNode, cloneNode, data, cloneNode ); 936 | container.appendChild( cloneNode ); 937 | } 938 | 939 | } 940 | }; 941 | 942 | var addHandler = function ( handlerName, handlerFunc ) { 943 | handlers[handlerName] = handlerFunc; 944 | }; 945 | 946 | var getParentNode = function ( node, path ) { 947 | 948 | try { 949 | var firstChar = path.charAt(0); 950 | 951 | switch ( firstChar ) { 952 | case "#": return $(path.substr(1)); 953 | case ".": 954 | var parentNode = node.parentNode; 955 | var className = path.substr(1); 956 | if ( !className ) return node; 957 | while ( parentNode && !parentNode.classList.contains(className) ) parentNode = parentNode.parentNode; 958 | return parentNode; 959 | default: 960 | var nodeName = path.toUpperCase(); 961 | var parentNode = node.parentNode; 962 | while ( parentNode && parentNode.nodeName != nodeName ) parentNode = parentNode.parentNode; 963 | return parentNode; 964 | } 965 | 966 | } 967 | catch ( e ) { return node.parentNode || null; }; 968 | }; 969 | 970 | var getChildByName = function ( nodeName ) { 971 | for ( var i=0, l=this.childNodes.length; i freezeTime ) { 1061 | freezeObj[eventType] = currentTime; 1062 | handler( DOMEvnt ); 1063 | } 1064 | else 1065 | { 1066 | DOMEvnt.preventDefault(true); 1067 | DOMEvnt.stopPropagation(true); 1068 | } 1069 | }; 1070 | var handlerWithAllows = function ( handler, allowDefault, allowPropagation, DOMEvnt ) { 1071 | DOMEvnt._z_ = { 1072 | allowDefault: allowDefault, 1073 | allowPropagation: allowPropagation 1074 | } 1075 | handler( DOMEvnt ); 1076 | }; 1077 | var handlerWithExcludingSelection = function ( handler, DOMEvnt ) { 1078 | if ( window.getSelection().toString() === "" ) { 1079 | handler( DOMEvnt ); 1080 | } 1081 | else 1082 | { 1083 | DOMEvnt.preventDefault(true); 1084 | DOMEvnt.stopPropagation(true); 1085 | }; 1086 | }; 1087 | 1088 | var getPluralText = function ( type, key, value ) { 1089 | 1090 | if ( !(plurals[type] instanceof Object) ) return ""; 1091 | 1092 | try { 1093 | var 1094 | pluralValuesByType = plurals[type], 1095 | pluralArr = pluralValuesByType[key], 1096 | pArrLength = pluralArr.length || 0, 1097 | pFunc = pluralFuncs[type], 1098 | requiredLength = pFunc[0], 1099 | pFunc = pFunc[1] 1100 | ; 1101 | 1102 | if ( pArrLength !== requiredLength ) return ""; 1103 | 1104 | return pluralArr[pFunc(value)] || ""; 1105 | 1106 | } catch ( e ) { return "" } 1107 | 1108 | }; 1109 | 1110 | var extend = function ( from, to ) { 1111 | 1112 | if (from == null || typeof from != "object") return from; 1113 | if (from.constructor != Object && from.constructor != Array) return from; 1114 | if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function || from.constructor == String || from.constructor == Number || from.constructor == Boolean) return new from.constructor(from); 1115 | 1116 | to = to || new from.constructor(); 1117 | 1118 | for (var name in from) { 1119 | to[name] = (typeof to[name] == "undefined") ? extend(from[name], null) : to[name]; 1120 | } 1121 | 1122 | return to; 1123 | } 1124 | 1125 | var isDOMEvent = function ( DOMEvnt ) { 1126 | 1127 | try { 1128 | return ( DOMEvnt instanceof window.Event || DOMEvnt instanceof templates.defaultView.Event ); 1129 | } catch ( e ) { return false } 1130 | 1131 | }; 1132 | 1133 | document.addEventListener("DOMContentLoaded", function(event) { z.init() }); 1134 | 1135 | var map = { 1136 | init: init, 1137 | dispatch: dispatch, 1138 | dispatchById: dispatchById, 1139 | addHandler: addHandler, 1140 | template: template, 1141 | getParentNode: getParentNode 1142 | }; 1143 | 1144 | return map; 1145 | 1146 | }()); 1147 | 1148 | z.addHandler( "template", function ( e, data ) { 1149 | e.c = this; 1150 | z.template( e, data ); 1151 | }); 1152 | z.addHandler( "templateIfMatch", function ( e, data ) { 1153 | 1154 | var 1155 | property = data[0], 1156 | constant = data[1], 1157 | options = data.slice(2) 1158 | ; 1159 | 1160 | if ( e.data[property] != constant ) return; 1161 | 1162 | e.c = this; 1163 | z.template( e, options ); 1164 | 1165 | }); 1166 | z.addHandler( "templateIfAttrMatch", function ( e, data ) { 1167 | 1168 | var 1169 | property = data[0], 1170 | tmpPath = property.split("."), 1171 | attrName = tmpPath.pop(), 1172 | value = this.getAttribute(attrName), 1173 | exprFunc = new Function ( "_data_", "with(_data_){ try { return " + property + "} catch(e) { return false } }" ), 1174 | options = data.slice(1) 1175 | ; 1176 | 1177 | if ( exprFunc(e.data) != value ) return; 1178 | 1179 | e.c = this; 1180 | z.template( e, options ); 1181 | 1182 | }); 1183 | z.addHandler( "templateIfExists", function ( e, data ) { 1184 | 1185 | var 1186 | property = data[0], 1187 | options = data.slice(1) 1188 | ; 1189 | 1190 | if ( e.data[property] === undefined ) return; 1191 | 1192 | e.c = this; 1193 | z.template( e, options ); 1194 | 1195 | }); 1196 | z.addHandler( "templateScopeIfExists", function ( e, data ) { 1197 | var 1198 | property = data[0], 1199 | options = data.slice(1), 1200 | exprFunc = new Function ( "_data_", "with(_data_){ try { return " + property + "} catch(e) { return undefined } }" ), 1201 | targetObj = exprFunc (e.data), 1202 | propName = property.split(".").pop(), 1203 | eventClone = Object.create(e), 1204 | localDataObj 1205 | ; 1206 | 1207 | if ( targetObj === undefined ) return; 1208 | 1209 | if ( targetObj instanceof Array ) { 1210 | localDataObj = targetObj.slice(); 1211 | } 1212 | else 1213 | { 1214 | localDataObj = JSON.parse(JSON.stringify(targetObj)); 1215 | } 1216 | 1217 | eventClone.c = this; 1218 | eventClone.data = {}; 1219 | eventClone.data[propName] = localDataObj; 1220 | 1221 | z.template( eventClone, options ); 1222 | }); 1223 | 1224 | z.addHandler( "templateOnce", function ( e, data ) { 1225 | e.c = this; 1226 | data[1] = "once"; 1227 | z.template( e, data ); 1228 | }); 1229 | 1230 | z.addHandler( "broadcastEvent", function ( e, data ) { 1231 | var newE = Object.create(e, { f: { value: this }, p: { value: data }, b: { value: true } } ); 1232 | newE.data = e.data; 1233 | z.dispatch( newE ); 1234 | }); 1235 | 1236 | z.addHandler( "dispatchEvent", function ( e, data ) { 1237 | var 1238 | nodeID = data[0], 1239 | mode = data[1] || "default", 1240 | mixin = undefined 1241 | ; 1242 | 1243 | if ( !nodeID ) return; 1244 | 1245 | if ( mode === "checkEmpty" && this.textContent.trim() !== "" ) { return; } 1246 | 1247 | if ( mode === "mixin" ) { 1248 | if ( data[2] ) { 1249 | mixin = {}; 1250 | mixin[data[2]] = e.data; 1251 | } 1252 | else 1253 | { 1254 | mixin = e.data; 1255 | } 1256 | } 1257 | 1258 | z.dispatchById.call( this, nodeID, mixin ); 1259 | }); 1260 | --------------------------------------------------------------------------------