На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
! Правила раздела "Наши Исходники"
Раздел предназначен для публикации различных исходников и программных решений (в виде исходных текстов), которые Вы считаете достойными внимания и/или интересными. Язык исходника значения не имеет. Это может быть C/C++, Pascal, Perl, PHP, C#, Foth, Prolog или любой другой существующий язык программирования. Единственный момент – в названии темы этот язык должен быть указан. Например, «[C++] Представление чисел в римской записи». Сам исходный текст должен содержаться в первом посте темы. Если исходник занимает не больше одного-двух экранов, то допустимо его публикация непосредственно в посте. Иначе исходный текст должен быть прикреплен к посту в виде архива. Кроме того, первый пост должен содержать:

- Информацию о платформе/компиляторе, для которых предназначен исходный текст (если эта информация существенна)
- Ссылку на оригинал и автора (если публикуется чужой исходный текст)
- Максимально подробное описание – какую задачу решает опубликованный исходный текст, и чем он интересен.

Плагиат крайне не приветствуется. Также не приветствуется публикация исходных текстов вирусов, крэков и т. п. информации. Это элементарно противозаконно.

Для быстрого поиска нужного исходника можно воспользоваться навигатором:
Быстрый поиск по разделу
Модераторы: Flex Ferrum
  
> [CS\JS] Knockout в стиле Angular, Knockdown 1.0.0
    Дополнение для knockout, которое реализует биндинги для DOM-элементов вида "nd-***". Каждый биндинг, например data-bind="foreach: ***" теперь можно писать как nd-foreach="***". Полный список биндингов:
    Скрытый текст
    Цитата
    nd-component
    nd-attr
    nd-checked
    nd-checkedValue
    nd-css
    nd-enable
    nd-disable
    nd-event
    nd-foreach
    nd-hasfocus
    nd-hasFocus
    nd-html
    nd-if
    nd-ifnot
    nd-with
    nd-options
    nd-selectedOptions
    nd-style
    nd-submit
    nd-text
    nd-textInput
    nd-textinput
    nd-uniqueName
    nd-value
    nd-visible
    nd-click
    nd-template


    Для автоматического применения вьюмодели\контроллера к DOM элементу присутствует биндинг nd-controller, в качестве аругмента которого применяется псевдоним для функции:

    ExpandedWrap disabled
      nd.controller('home', var HomeController = (function(){
        function HomeController() {
          this.some = ko.observableArray([]);
          // Конструктор контроллера
        };
        
        HomeController.prototype.someAction = function(){};
       
        return HomeController;
      })());

    ExpandedWrap disabled
      <div nd-controller="home" nd-foreach="some" nd-click="someAction"></div>


    Также присутствует реализация Dependency Injection (Ioc контейнер). Таким образом в аргументах конструктора можно получить контроллер ил что-либо длругое, если назвать аргумент с префиксом '$'.


    ExpandedWrap disabled
      nd.controller('header', var HeaderController = (function(){
        function HeaderController ($dom, $home) {
          // В $dom теперь будет лежать DOM-объект к которому прицепили контроллер
          // В $home будет лежать контроллер с псевдонимом home
        }
      })());


    В виде CoffeeScript кода это будет выглядеть так:

    ExpandedWrap disabled
      nd.controller 'header', class HeaderController
        constructor: ($dom, $home) ->
          @some = ko.observable 23
        
        someAction: =>
          @some 42

    ExpandedWrap disabled
      <header nd-controller="header">
        <span nd-text="some"></span>
        <button nd-click="someAction">Изменить значение переменной some</button>
      </header>






    Исходный код:
    ExpandedWrap disabled
      class window.nd
        ndBindings = {
          prefix:     'nd-'
          controller: 'controller'
        }
       
        templateBindings = {}
        templateBindings[binding] = binding for binding of ko.bindingHandlers
       
       
        @container = {
          $window: window
        }
       
       
        @controller: (name, controller) =>
          @container[name] = controller
       
       
       
        @invokeBindings: (dom) =>
          parent = dom.parentNode
       
          for rule, replace of templateBindings
            for node in parent.querySelectorAll("[#{ndBindings.prefix + rule}]")
              node.bindings = node.bindings || []
              node.bindings.push "#{replace}: #{node.getAttribute("#{ndBindings.prefix + rule}")}"
       
          for rule, replace of templateBindings
            for node in parent.querySelectorAll("[#{ndBindings.prefix + rule}]")
              node.setAttribute('data-bind', node.bindings.join(', '))
       
          dom
       
       
       
        @invoke: (controller, dom) =>
          @dynamic = {
            dom: dom
          }
       
       
          args = (controller
            .toString()
            .match(/function\s*[\$_A-Z0-9a-z]+\s*\((.*?)\)/)[1]
          )
            .replace(/\s*/g, '')
            .split(',')
       
       
          invoke = (arg) =>
            if arg.substr(0, 1) is '$'
              key = arg.substr(1)
              if @container[key]?
                return @container[key]
              else if @dynamic[key]?
                return @dynamic[key]
            null
       
       
          [invoker, invokerString, iterator] = [[], [], 0]
          for arg in args
            invokerString.push "invoker[#{iterator++}]"
            invoker.push invoke(arg)
          return eval "new controller(#{invokerString.join(',')})"
       
       
        @extend: (cls) ->
          cls
       
       
        @init: =>
          for dom in document.querySelectorAll("[#{ndBindings.prefix + ndBindings.controller}]")
            attr = dom.getAttribute(ndBindings.prefix + ndBindings.controller)
            if @container[attr]?
              controller = @invoke(@extend(@container[attr]), dom)
              ko.applyBindings controller, @invokeBindings(dom)
       
       
        if document.addEventListener
          document.addEventListener 'DOMContentLoaded', =>
            document.removeEventListener 'DOMContentLoaded', arguments.callee, false
            @init()
          , false
        else if document.attachEvent
          document.attachEvent 'onreadystatechange', =>
            if document.readyState is 'complete'
              document.detachEvent 'onreadystatechange', arguments.callee
              @init()



    Скомпилированный исходный код:
    ExpandedWrap disabled
      window.nd = (function() {
        var binding, ndBindings, templateBindings;
       
        function nd() {}
       
        ndBindings = {
          prefix: 'nd-',
          controller: 'controller'
        };
       
        templateBindings = {};
       
        for (binding in ko.bindingHandlers) {
          templateBindings[binding] = binding;
        }
       
        nd.container = {
          $window: window
        };
       
        nd.controller = function(name, controller) {
          return nd.container[name] = controller;
        };
       
        nd.invokeBindings = function(dom) {
          var node, parent, replace, rule, _i, _j, _len, _len1, _ref, _ref1;
          parent = dom.parentNode;
          for (rule in templateBindings) {
            replace = templateBindings[rule];
            _ref = parent.querySelectorAll("[" + (ndBindings.prefix + rule) + "]");
            for (_i = 0, _len = _ref.length; _i < _len; _i++) {
              node = _ref[_i];
              node.bindings = node.bindings || [];
              node.bindings.push("" + replace + ": " + (node.getAttribute("" + (ndBindings.prefix + rule))));
            }
          }
          for (rule in templateBindings) {
            replace = templateBindings[rule];
            _ref1 = parent.querySelectorAll("[" + (ndBindings.prefix + rule) + "]");
            for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
              node = _ref1[_j];
              node.setAttribute('data-bind', node.bindings.join(', '));
            }
          }
          return dom;
        };
       
        nd.invoke = function(controller, dom) {
          var arg, args, invoke, invoker, invokerString, iterator, _i, _len, _ref;
          nd.dynamic = {
            dom: dom
          };
          args = (controller.toString().match(/function\s*[\$_A-Z0-9a-z]+\s*\((.*?)\)/)[1]).replace(/\s*/g, '').split(',');
          invoke = function(arg) {
            var key;
            if (arg.substr(0, 1) === '$') {
              key = arg.substr(1);
              if (nd.container[key] != null) {
                return nd.container[key];
              } else if (nd.dynamic[key] != null) {
                return nd.dynamic[key];
              }
            }
            return null;
          };
          _ref = [[], [], 0], invoker = _ref[0], invokerString = _ref[1], iterator = _ref[2];
          for (_i = 0, _len = args.length; _i < _len; _i++) {
            arg = args[_i];
            invokerString.push("invoker[" + (iterator++) + "]");
            invoker.push(invoke(arg));
          }
          return eval("new controller(" + (invokerString.join(',')) + ")");
        };
       
        nd.extend = function(cls) {
          return cls;
        };
       
        nd.init = function() {
          var attr, controller, dom, _i, _len, _ref, _results;
          _ref = document.querySelectorAll("[" + (ndBindings.prefix + ndBindings.controller) + "]");
          _results = [];
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            dom = _ref[_i];
            attr = dom.getAttribute(ndBindings.prefix + ndBindings.controller);
            if (nd.container[attr] != null) {
              controller = nd.invoke(nd.extend(nd.container[attr]), dom);
              _results.push(ko.applyBindings(controller, nd.invokeBindings(dom)));
            } else {
              _results.push(void 0);
            }
          }
          return _results;
        };
       
        if (document.addEventListener) {
          document.addEventListener('DOMContentLoaded', function() {
            document.removeEventListener('DOMContentLoaded', arguments.callee, false);
            return nd.init();
          }, false);
        } else if (document.attachEvent) {
          document.attachEvent('onreadystatechange', function() {
            if (document.readyState === 'complete') {
              document.detachEvent('onreadystatechange', arguments.callee);
              return nd.init();
            }
          });
        }
       
        return nd;
       
      })();



    Минифицированный код:
    ExpandedWrap disabled
      window.nd=function(){function nd(){}var binding,ndBindings,templateBindings;ndBindings={prefix:"nd-",controller:"controller"};templateBindings={};for(binding in ko.bindingHandlers){templateBindings[binding]=binding}nd.container={$window:window};nd.controller=function(e,t){return nd.container[e]=t};nd.invokeBindings=function(e){var t,n,r,i,s,o,u,a,f,l;n=e.parentNode;for(i in templateBindings){r=templateBindings[i];f=n.querySelectorAll("["+(ndBindings.prefix+i)+"]");for(s=0,u=f.length;s<u;s++){t=f[s];t.bindings=t.bindings||[];t.bindings.push(""+r+": "+t.getAttribute(""+(ndBindings.prefix+i)))}}for(i in templateBindings){r=templateBindings[i];l=n.querySelectorAll("["+(ndBindings.prefix+i)+"]");for(o=0,a=l.length;o<a;o++){t=l[o];t.setAttribute("data-bind",t.bindings.join(", "))}}return e};nd.invoke=function(controller,dom){var arg,args,invoke,invoker,invokerString,iterator,_i,_len,_ref;nd.dynamic={dom:dom};args=controller.toString().match(/function\s*[\$_A-Z0-9a-z]+\s*\((.*?)\)/)[1].replace(/\s*/g,"").split(",");invoke=function(e){var t;if(e.substr(0,1)==="$"){t=e.substr(1);if(nd.container[t]!=null){return nd.container[t]}else if(nd.dynamic[t]!=null){return nd.dynamic[t]}}return null};_ref=[[],[],0],invoker=_ref[0],invokerString=_ref[1],iterator=_ref[2];for(_i=0,_len=args.length;_i<_len;_i++){arg=args[_i];invokerString.push("invoker["+iterator++ +"]");invoker.push(invoke(arg))}return eval("new controller("+invokerString.join(",")+")")};nd.extend=function(e){return e};nd.init=function(){var e,t,n,r,i,s,o;s=document.querySelectorAll("["+(ndBindings.prefix+ndBindings.controller)+"]");o=[];for(r=0,i=s.length;r<i;r++){n=s[r];e=n.getAttribute(ndBindings.prefix+ndBindings.controller);if(nd.container[e]!=null){t=nd.invoke(nd.extend(nd.container[e]),n);o.push(ko.applyBindings(t,nd.invokeBindings(n)))}else{o.push(void 0)}}return o};if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);return nd.init()},false)}else if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);return nd.init()}})}return nd}()
    user posted image user posted image user posted image
      Версия 1.1.0-dev:
      • Убран ioc, теперь все объекты из контейнера можно получить с помощью метода this.get('key') в контроллере - с долларом в аргументах есть проблема в минифиакторах JS
      • Теперь nd является объектом
      • Теперь можно вручную проверять наличие nd-controller вызовом метода nd.applyBindings(DOM)
      • Теперь можно вручную биндить контроллер вызовом nd.bind('key', DOM)
      • Теперь для биндингов attr, event, css, style, component и template не надо указывать фигурные скобки в аргументах (nd-attr="class: some" вместо nd-attr="{class: some}")
      • Добавлено свойство nd.version
      • Теперь доступно содержание di контейнера извне: nd.container
      • Теперь можно изменять именования биндингов: nd.attr содержит все ключи
      • Теперь можно получить конкретный dom элемент с помощью выражения this.get('node').KEY, где KEY является значением nd-node (например: <canvas nd-node="KEY"></canvas>)
      • Теперь dom после инициализации не содержит атрибутов nd-***, что избавляет от ошибок двойной инициализации



      CoffeeScript
      ExpandedWrap disabled
        ###
         
         This file is part of Knockdown package.
         
         @author serafim <nesk@xakep.ru> (19.06.2014 21:18)
         @version: 1.1.0-dev
         
         For the full copyright and license information, please view the LICENSE
         file that was distributed with this source code.
         
        ###
         
         
         
        class Knockdown
          @::version = '1.1.0-dev'
         
          ###
            Attributes
          ###
          class Attributes
            toObject         = 'toObject'
         
            constructor: ->
              @prefix    = 'nd-'
              controller = 'controller'
              node       = 'node'
         
              # nd-controller
              Object.defineProperty @, 'controller', {
                get:       => @prefix + controller
                set: (val) => controller = val
              }
         
              # nd-node
              Object.defineProperty @, 'node', {
                get:       => @prefix + node
                set: (val) => node = val
              }
         
              # original knockout bindings
              @template = {}
              @template[binding] = binding for binding of ko.bindingHandlers
         
              # transform bindings
              @transform   = {
                attr:       toObject
                event:      toObject
                css:        toObject
                style:      toObject
                component:  toObject
                template:   toObject
              }
         
            with: (rule) => @prefix + rule
         
         
          ###
            Di Container
          ###
          class Container
            constructor: (items = {}) ->
              @items = {
                window: window
              }
         
              @[key] = value for key, value of items
         
            get: (key) => @items[key]
         
            has: (key) => !!@get(key)
         
            set: (key, val) =>
              @items[key] = val
              @
         
            with: (items) => new Container(items)
         
         
         
          ###
            Initialize
          ###
          constructor: ->
            @attr       = new Attributes
            @container  = new Container
         
            @ready      = false
            @onReady    => @applyBindings()
         
         
         
          ###
            Add to container
          ###
          controller: (name, controller) =>
            @container.set(name, controller)
         
         
         
          ###
            Replace bindings
          ###
          invokeBindings: (dom) =>
            parent = dom.parentNode
         
            for rule, replace of @attr.template
              for node in parent.querySelectorAll("[#{@attr.with(rule)}]")
                node.bindings = node.bindings || []
                value = node.getAttribute("#{@attr.with(rule)}")
         
                # Реплейсы в значениях биндингов
                if @attr.transform[replace]?
                  if @attr.transform[replace] is 'toObject'
                    value = "{ #{value} }"
                  else
                    throw new Error 'Undefined keyword type'
         
                node.bindings.push "#{replace}: #{value}"
         
            for rule, replace of @attr.template
              for node in parent.querySelectorAll("[#{@attr.with(rule)}]")
                node.setAttribute('data-bind', node.bindings.join(', '))
                node.removeAttribute("#{@attr.with(rule)}")
         
            dom
         
         
          ###
            Create container for controller
          ###
          invoke: (controller, dom) =>
            container = @container.with({dom: dom})
         
            nodes = {}
            for node in dom.querySelectorAll("[#{@attr.node}]")
              nodes[node.getAttribute("#{@attr.node}")] = node
            container.set(node, nodes)
         
            do (container, controller) =>
              controller::get = (key) => container.get key
            return new controller(dom)
         
         
          ###
            Bind new controllers for dom elements
          ###
          applyBindings: (node = document) =>
            for dom in node.querySelectorAll("[#{@attr.controller}]")
              value = dom.getAttribute(@attr.controller)
              if @container.has(value)
                @bind(value, dom)
         
         
          ###
            Bind controller by key for target dom element
          ###
          bind: (key, dom) =>
            controller = @invoke(@container.get(key), dom)
            dom.removeAttribute("#{@attr.controller}")
            ko.applyBindings controller, @invokeBindings(dom)
            @
         
         
         
         
          ###
            Ready events
          ###
          onReady: (cb = (->)) =>
            readyCallback = =>
              @ready = true
              cb()
         
            return readyCallback() if @ready
         
            if document.addEventListener
              document.addEventListener 'DOMContentLoaded', =>
                document.removeEventListener 'DOMContentLoaded', arguments.callee, false
                readyCallback()
              , false
            else if document.attachEvent
              document.attachEvent 'onreadystatechange', =>
                if document.readyState is 'complete'
                  document.detachEvent 'onreadystatechange', arguments.callee
                  readyCallback()
         
         
        window.nd = new Knockdown



      JavaScript:
      ExpandedWrap disabled
        /**
         
         This file is part of Knockdown package.
         
         @author serafim <nesk@xakep.ru> (19.06.2014 21:18)
         @version: 1.1.0-dev
         
         For the full copyright and license information, please view the LICENSE
         file that was distributed with this source code.
         */
         
        (function() {
          var Knockdown,
            __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
         
          Knockdown = (function() {
            var Attributes, Container;
         
            Knockdown.prototype.version = '1.1.0-dev';
         
         
            /*
              Attributes
             */
         
            Attributes = (function() {
              var toObject;
         
              toObject = 'toObject';
         
              function Attributes() {
                this["with"] = __bind(this["with"], this);
                var binding, controller, node;
                this.prefix = 'nd-';
                controller = 'controller';
                node = 'node';
                Object.defineProperty(this, 'controller', {
                  get: (function(_this) {
                    return function() {
                      return _this.prefix + controller;
                    };
                  })(this),
                  set: (function(_this) {
                    return function(val) {
                      return controller = val;
                    };
                  })(this)
                });
                Object.defineProperty(this, 'node', {
                  get: (function(_this) {
                    return function() {
                      return _this.prefix + node;
                    };
                  })(this),
                  set: (function(_this) {
                    return function(val) {
                      return node = val;
                    };
                  })(this)
                });
                this.template = {};
                for (binding in ko.bindingHandlers) {
                  this.template[binding] = binding;
                }
                this.transform = {
                  attr: toObject,
                  event: toObject,
                  css: toObject,
                  style: toObject,
                  component: toObject,
                  template: toObject
                };
              }
         
              Attributes.prototype["with"] = function(rule) {
                return this.prefix + rule;
              };
         
              return Attributes;
         
            })();
         
         
            /*
              Di Container
             */
         
            Container = (function() {
              function Container(items) {
                var key, value;
                if (items == null) {
                  items = {};
                }
                this["with"] = __bind(this["with"], this);
                this.set = __bind(this.set, this);
                this.has = __bind(this.has, this);
                this.get = __bind(this.get, this);
                this.items = {
                  window: window
                };
                for (key in items) {
                  value = items[key];
                  this[key] = value;
                }
              }
         
              Container.prototype.get = function(key) {
                return this.items[key];
              };
         
              Container.prototype.has = function(key) {
                return !!this.get(key);
              };
         
              Container.prototype.set = function(key, val) {
                this.items[key] = val;
                return this;
              };
         
              Container.prototype["with"] = function(items) {
                return new Container(items);
              };
         
              return Container;
         
            })();
         
         
            /*
              Initialize
             */
         
            function Knockdown() {
              this.onReady = __bind(this.onReady, this);
              this.bind = __bind(this.bind, this);
              this.applyBindings = __bind(this.applyBindings, this);
              this.invoke = __bind(this.invoke, this);
              this.invokeBindings = __bind(this.invokeBindings, this);
              this.controller = __bind(this.controller, this);
              this.attr = new Attributes;
              this.container = new Container;
              this.ready = false;
              this.onReady((function(_this) {
                return function() {
                  return _this.applyBindings();
                };
              })(this));
            }
         
         
            /*
              Add to container
             */
         
            Knockdown.prototype.controller = function(name, controller) {
              return this.container.set(name, controller);
            };
         
         
            /*
              Replace bindings
             */
         
            Knockdown.prototype.invokeBindings = function(dom) {
              var node, parent, replace, rule, value, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3;
              parent = dom.parentNode;
              _ref = this.attr.template;
              for (rule in _ref) {
                replace = _ref[rule];
                _ref1 = parent.querySelectorAll("[" + (this.attr["with"](rule)) + "]");
                for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
                  node = _ref1[_i];
                  node.bindings = node.bindings || [];
                  value = node.getAttribute("" + (this.attr["with"](rule)));
                  if (this.attr.transform[replace] != null) {
                    if (this.attr.transform[replace] === 'toObject') {
                      value = "{ " + value + " }";
                    } else {
                      throw new Error('Undefined keyword type');
                    }
                  }
                  node.bindings.push("" + replace + ": " + value);
                }
              }
              _ref2 = this.attr.template;
              for (rule in _ref2) {
                replace = _ref2[rule];
                _ref3 = parent.querySelectorAll("[" + (this.attr["with"](rule)) + "]");
                for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) {
                  node = _ref3[_j];
                  node.setAttribute('data-bind', node.bindings.join(', '));
                  node.removeAttribute("" + (this.attr["with"](rule)));
                }
              }
              return dom;
            };
         
         
            /*
              Create container for controller
             */
         
            Knockdown.prototype.invoke = function(controller, dom) {
              var container, node, nodes, _i, _len, _ref;
              container = this.container["with"]({
                dom: dom
              });
              nodes = {};
              _ref = dom.querySelectorAll("[" + this.attr.node + "]");
              for (_i = 0, _len = _ref.length; _i < _len; _i++) {
                node = _ref[_i];
                nodes[node.getAttribute("" + this.attr.node)] = node;
              }
              container.set(node, nodes);
              (function(_this) {
                return (function(container, controller) {
                  return controller.prototype.get = function(key) {
                    return container.get(key);
                  };
                });
              })(this)(container, controller);
              return new controller(dom);
            };
         
         
            /*
              Bind new controllers for dom elements
             */
         
            Knockdown.prototype.applyBindings = function(node) {
              var dom, value, _i, _len, _ref, _results;
              if (node == null) {
                node = document;
              }
              _ref = node.querySelectorAll("[" + this.attr.controller + "]");
              _results = [];
              for (_i = 0, _len = _ref.length; _i < _len; _i++) {
                dom = _ref[_i];
                value = dom.getAttribute(this.attr.controller);
                if (this.container.has(value)) {
                  _results.push(this.bind(value, dom));
                } else {
                  _results.push(void 0);
                }
              }
              return _results;
            };
         
         
            /*
              Bind controller by key for target dom element
             */
         
            Knockdown.prototype.bind = function(key, dom) {
              var controller;
              controller = this.invoke(this.container.get(key), dom);
              dom.removeAttribute("" + this.attr.controller);
              ko.applyBindings(controller, this.invokeBindings(dom));
              return this;
            };
         
         
            /*
              Ready events
             */
         
            Knockdown.prototype.onReady = function(cb) {
              var readyCallback;
              if (cb == null) {
                cb = (function() {});
              }
              readyCallback = (function(_this) {
                return function() {
                  _this.ready = true;
                  return cb();
                };
              })(this);
              if (this.ready) {
                return readyCallback();
              }
              if (document.addEventListener) {
                return document.addEventListener('DOMContentLoaded', (function(_this) {
                  return function() {
                    document.removeEventListener('DOMContentLoaded', arguments.callee, false);
                    return readyCallback();
                  };
                })(this), false);
              } else if (document.attachEvent) {
                return document.attachEvent('onreadystatechange', (function(_this) {
                  return function() {
                    if (document.readyState === 'complete') {
                      document.detachEvent('onreadystatechange', arguments.callee);
                      return readyCallback();
                    }
                  };
                })(this));
              }
            };
         
            return Knockdown;
         
          })();
         
          window.nd = new Knockdown;
         
        }).call(this);
      user posted image user posted image user posted image
        Обновил до версии 1.2.1. Довольно много внутренних изменений.

        Скачать можно тут: https://github.com/SerafimArts/Knockdown

        Начал описывать документацию: https://github.com/SerafimArts/Knockdown/wiki
        user posted image user posted image user posted image
        1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
        0 пользователей:


        Рейтинг@Mail.ru
        [ Script Execution time: 0,1225 ]   [ 14 queries used ]   [ Generated: 26.06.19, 12:10 GMT ]