jquerymy.js ‹{}›

Описание и API v1.1.9

 Описание версии 1.2.8 доступно только на английском

Для поиска по руководству используйте Ctrl-F или ⌘-F. Для быстрого старта без чтения лучше смотреть исходники примеров на заглавной.

В cloudwall.me встроен IDE для создания и отладки $.my-приложений.

Основные концепции

$.my – плагин, а не фреймворк. $.my работает в браузере и позволяет создавать сложные интерактивные формы, непрерывно отображающее своё состояние на JSON-совместимый объект. $.my не включает раутер, механизмы обмена данными с сервером, авторизации и т.п. 

Одно приложение – один объект. Любое приложение $.my, как поведение, так и внешний вид, можно описать одним стандартным js-объектом, т.н. «манифестом». Нет необходимости изучать дополнительные языки разметки, все внешние плагины подключаются стандартными для них способами. 

JSON-friendly. Манифесты $.my легко сериализуются в JSON и отлично подходят для хранения в NoSQL-базах. Манипуляция манифестами приложений – обычная манипуляция javascript-объектами. 

Вкладываемость. Любая форма $.my может быть контролом для другой формы. Это позволяет создавать сложные приложения с множеством зависимых и подчинённых форм. Включение одних манифестов в другие – просто присоединение javascript-объекта дочки к объекту родителя.

Открытость. jQuery.my автоматически распознаёт множество типов контролов, сгенерированных сторонними плагинами и легко расширяется для поддержки новых.

Надёжность. $.my совместим с IE9+ и всеми современными браузерами. Плагин корректно обрабатывает ошибки кода форм таким образом, что отказ частей приложения не приводит к отказу всего приложения.

Quick start

jQuery.my зависит от Sugar.js 1.4+ и jQuery.js 1.9+ и должен подключаться после них.

xxxxxxxxxx
 
<script src="/js/sugar.min.js"></script>
<script src="/js/jquery.min.js"></script>
<script src="/js/jquerymy.min.js"></script>
xxxxxxxxxx
 
// Place your javascript here
xxxxxxxxxx
 
/* Container for CSS code */

Инициализация и управление экземплярами jQuery.my типичны для jQuery-плагинов и по схеме вызовов соответствуют рекомендациям jQuery

Общая схема синтаксиса – $(DOM_node).my("command", param1, param2). Если команда не указана, считается, что запрошена инициализация "init".

Создание формы

xxxxxxxxxx
 
<!-- Put HTML code here -->
xxxxxxxxxx
 
// Source data
var person={ 
  name: "Ann", 
  metrics:{ age:"25" } 
};
// Manifest
var manifest = {
  
  //Default data  
  data: { name:"", metrics:{ age:"" }},
  
  // Init function  
  init: function ($node, runtime) {
    $node.html(
      '<h4>Simplest form</h4>'+
      '<div><input id="name" type="text" placeholder="Name"/></div>' +
      '<div><input id="age" type="number" placeholder="Age"/></div>'
    );
  },
  
  // Bindings
  ui: {
    "#name": { bind: "name" },
    "#age" : { bind: "metrics.age" }
  }
};
// Init $.my over DOM node
$("#form").my( manifest, person );
xxxxxxxxxx
 
/* Container for CSS code */

После исполнения этот код сгенерирует примерно такую форму:

После инициализации переменная person отражает состояние контролов в реальном времени – любое изменение input’ов формы мутирует переменную.

Указание связывания строками вида "metrics.age" – синтаксический сахар для упрощения связывания. Поддерживается указание индексов в массивах вида "arrayName.3".

Первый параметр, переданный при инициализации $.my – переменная manifest – в дальнейшем в тексте будет именоваться манифестом.

Снятие данных с формы

Команда $("#form").my("data") возвращает ссылку на динамически обновляемый объект, содержащий данные. В момент обновления данных форма вызывает событие change на DOM-объекте, на котором она смонтирована.

Внешнее обновление данных

$("#form").my("data", { /*данные*/ }) обновляет данные и перерисовывает форму. Переданный объект объединяется с данными формы глубоким клонированием.

Данные могут обновляться частично, для примера выше выполнение $("#form").my("data", {name:"John"}) заменит только имя.

Если данные формы привязаны через переменную (см. person в примере выше), внешнее обновление полей переменной не приведёт к перерисовке формы само по себе. После внешнего присваивания необходимо обновить форму с помощью $("#form").my("redraw").

Структура манифеста

Манифест – стандартный javascript-объект. Объект может иметь некоторое количество свойств с зарезервированными именами и неограниченно количество других свойств. Зарезервированные свойства:

  • id Строка, уникальный идентификатор манифеста.
  • params Объект, определяет настройки $.my для данного экземпляра формы.
  • inherit Объект, массив или строка, список объектов, наследуемых от родительской формы
  • expose Объект, массив или строка. Список объектов, которые могут быть унаследованы дочерними формами.
  • require Массив, необходимые для работы формы ресурсы и их URL.
  • files Объект, ключи – названия ресурсов, значения – объекты с полем data, содержащим base64-кодированный ресурс.
  • style Объект, определяет CSS-правила, локальные для формы.
  • data Значение объекта data по умолчанию.
  • init Функция инициализации, выполняется до начала связывания.
  • ui Обязательное свойство, определяет связывание DOM-контролов и данных. Объект, каждый ключ которого – jQuery-селектор, а значение под ключом – описание параметров связи. 
  • radio Объект, массив или строка, список каналов pub/sub, подлежащих ретрансляции.
  • die Функция, вызывается при удалении формы. Аргументы – как для init-функции.

Свойства в списке расположены в порядке их обработки при старте формы.

Другие свойства манифеста, «пользовательские», могут быть любыми. Желательно использовать Capitalized или camelCase ключи для гарантированного исключения конфликта с будущими версиями jQuery.

Связывание контролов
и данных

Объект ui манифеста – это плоский словарь «ключ-значение», где ключи – это jQuery-селекторы контролов, а значения – объекты, описывающий связь.

Свойство bind такого объекта непосредственно управляет связыванием и может быть как строкой-указателем, так и функцией. 

Если свойство bind – строка, она трактуется как указатель на ветку объекта data манифеста. Указатель может быть записан в dot-нотации, не существующие промежуточные ветки такого «пути» будут созданы автоматически. Запись "this.SomeKeyName" указывает на ветку в рантайм-манифесте.

Если bind – функция, она получает (dataObj, newValue, $control) как параметры и вызывается каждый раз, как изменился контрол или форма требует пересчёта поля. 

Если параметр newValue равен null, функция должна вернуть значение для контрола, в противном случае – сохранить значение в объект данных dataObj в нужное свойство.

Таким образом bind-функция реализует сразу и getter, и setter. Предыдущий пример может быть переписан следующим образом:

xxxxxxxxxx
 
<!-- Put HTML code here -->
xxxxxxxxxx
 
$("#form").my({
  ui:{
    "#name": "name",
    "#age" : {
      bind: function (data, value, $control) {
        var dm = data.metrics;
        if (value != null) dm.age = value; 
        return dm.age = (dm.age + "").replace(/\D/g,"");
      }
    }
  }
}, person);
xxxxxxxxxx
 
/* Container for CSS code */

При такой реализации в поле #age невозможно будет набрать ничего кроме цифр. Нажатие на любую клавишу кроме цифры не будет вызывать видимого изменения контрола – не-цифры сразу вырезаются из значения.

Третий параметр – $control – обрабатываемая jQuery коллекция или объект. Он может быть полезен для вызова событий или навигации по форме. Вызов $control.my("find", "#name") вернёт jQuery-объект поля #name формы.

Зависимые контролы

Указание зависимостей производится с помощью свойств watch и/или recalc описания контрола.

В манифесте продемонстрированы оба подхода. Параметры watch и recalc могут быть строками и массивами.

Если зависимости контрола определены как параметрами recalc, так и свойством watch, при построении графа пересчёта зависимостей сначала обрабатываются зависимости, заданные параметрами recalc.

Циклические зависимости

Контролы могут взаимно зависеть друг от друга:

При создании сложных ветвлений зависимостей с циклами иногда необходимо вручную контролировать глубину пересчёта циклических связей. Свойство recalcDepth лимитирует максимальную глубину графа пересчёта, построенного от обрабатываемого контрола. По умолчанию recalcDepth равен 2.

События связывания

По умолчанию $.my «откликается» на такие события изменения контролов, которые позволяют отображать контрол на данные в реальном времени. Это не всегда необходимо – например, в некоторых случаях поля набора текста должны менять подлежащие данные только после снятия фокуса, а не по мере набора. 

Для управления списком событий, активизирующих обновление данных, служит свойство events. Свойство может быть строкой или массивом.

Указание постфикса .my после типа события служит для корректного отвязывания обработчиков при удалении формы.

Задержка связывания

Для некоторых контролов разумно устанавливать подавление частых событий изменения – некоторую задержку связывания. Она регулируется свойством delay.

При пересчёте зависимых контролов их bind-функции исполняются без задержки. Задержка применяется только к контролу, изменяемомому пользователем, а не формой.

Обновление по событию в pub/sub канале

Контрол может быть обновлён по событию pub/sub канала. Скажем, получены новые данные и надо пересчитать какой-то список. Подписка контрола на канал определяется полем listen в описании контрола в разделе ui

xxxxxxxxxx
 
<!-- Put HTML code here -->
xxxxxxxxxx
 
({
  List:[],
  ui:{
    "#filter":{
      bind: function(){ /* We (re)build list here */},
      
      listen:{
        // Here we listen to /list and /item channels
        "/list": true,
        "/item": function(data, message, $ctrl){
          if (message && message.doctype == "item") return true;
        },
      }
    },
    "#list":{
      bind:"this.List",
      watch:"#filter",  // Watch for filter changes
      manifest: { /* Here goes list item manifest */ }
    }
  }
})
xxxxxxxxxx
 
/* Container for CSS code */

Контрол #filter перерисовывается, когда канал /list получает любое сообщение, или когда канал /item получает сообщение определённого типа. Предполагается, что объект #filter перестраивает список, а затем объект #list его перерисовывает. 

Отправка широковещательных сообщений может производиться двумя способами.

Вызов $.my.radio(channel, msg) отправлет глобальное не блокируемое сообщение в канал channel. 

Вызов $ctrl.trigger("radio", {channel:"channelName", message:msg}) отправляет сообщение, распространение которого может быть ограничено родительской формой («локальное»).

Валидация

Существует 3 способа валидации: валидатор может быть определён в HTML-атрибутах контрола, либо в манифесте – как функция или регулярное выражение.

В отличие от валидации с помощью регэкспа, валидация с помощью функции может вернуть разные сообщения об ошибках в зависимости от нарушенного условия. 

Валидация производится непосредственно до исполнения bind. Если валидация не удалась, ближайшему контейнеру интерактивного контрола присваивается класс .my-error. Внутри этого контейнера ищется элемент с классом .my-error-tip, он делается видимым и в него помещается сообщение об ошибке. Если элемент не найден, сообщение помещается в атрибут title контрола – для отображения сообщения всплывающей подсказкой.

Для не-интерактивных контролов – например, поле данных привязано к элементу <div> –  класс .my-error применяется к самому элементу, а не к контейнеру.

Валидация регэкспом

Если строка в поле ввода #name содержит менее 3 букв, контейнер поля ввода получает класс .my-error.

Сообщения валидатора размещаются в элемент с классом .my-error-tip контейнера контрола. Таким образом, для того, чтобы сделать сообщения видимыми, внутри контейнера должен быть DOM-элемент для приёма сообщения. 

Если DOM-контейнер для сообщения не найден, оно помещается в атрибут title контрола – для отображения сообщения всплывающей подсказкой. Предыдущее содержимое атрибута title сохраняется и возвращается обратно в момент, когда контрол снова становится валиден.

Валидация функцией

Функция валидации check получает те-же параметры, что и bind, но выполняется до связывания.

В отличии от bind-функции, check никогда не вызывается со значением value равным null.

Сообщения валидатора размещаются в элемент с классом .my-error-tip контейнера контрола. Таким образом, для того, чтобы сделать сообщения видимыми, внутри контейнера должен быть DOM-элемент для приёма сообщения. 

Если DOM-контейнер для сообщения не найден, оно помещается в атрибут title контрола – для отображения сообщения всплывающей подсказкой. Предыдущее содержимое атрибута title сохраняется и возвращается обратно в момент, когда контрол снова становится валиден.

Валидность всей формы

Команда $("#formObj").my("valid") возвращает true, если все контролы формы валидны и false в противном случае.

$("#formObj").my("errors") возвращает объект, ключи которого – селекторы контролов, а значения – сообщения об ошибках. Если возвращён пустой объект, форма не содержит ошибок.

Из bind-функций контролов доступ этим свойствам всей формы осуществляется конструкцией $control.my().errors, содержащий ссылку на объект с ошибками всей формы.

Условное форматирование

Условное форматирование позволяет применять к контейнеру контрола те или иные CSS-стили в зависимости от состояния объекта с данными.

Помимо обычных классов существует специальный ключ :disabled, с помощью которого можно разрешать/запрещать активность контрола.

Условное форматирование применяется после исполнения функций check и bind контрола.

Локальные CSS-классы

В примере выше классы .Red и .Green определены локально в секции style манифеста – и будут действовать только в рамках экземпляров этого манифеста. 

При инициализации формы создаётся набор правил, локальных по отношению к экземпляру формы, затем этот набор правил вставляется в код страницы. По окончанию жизненного цикла манифеста правила удаляются.

Для исключения создания множественных HTML-секций <style> с одинаковыми правилами, манифест должен иметь поле id.

Ключи с названиями классов начинаются с пробелов – это необходимо для корректной конкатенации. " .Red" + " input" при разборе образуют класс 
.my-manifest-1ftwlphd .Red input {background-color: #FCC; color: #C02;}.

Правила могут задаваться не только строками, но и функциями:

xxxxxxxxxx
 
<!-- Put HTML code here -->
xxxxxxxxxx
 
style:{
  " .item": function ($form, runtime) {
    if ($form.width()<500) return "display:none";
    return {
      " p": "font-size:80%",
      " a": "color: blue"
    };
  }
}
xxxxxxxxxx
 
/* Container for CSS code */

Вычисление правил, заданных функциями, происходит при старте конкретного экземпляра формы перед выполнением init-функций.

Также правила, заданные функциями, могут автоматически перевычислятся при измении размеров окна браузера. Поле params.restyle манифеста определяет задержку пересчёта правил после перехвата события изменения окна, в миллисекундах. По умолчанию содержит -1, что означает отсутствие пересчёта – он может быть чрезвычайно затратен.

В примере выше функция оценит ширину объекта, на который применяется манифест, и если она меньше 500px, создаст правило, скрывающее DOM-объекты с классом item. Правило будет действовать только в рамках конкретного экземпляра формы.

Если ширина 500+ px – будут созданы правила .item p и .item a, также действующие только для одного экземпляра формы.

Когда форма содержит правила, определённые как функциями, так и объектами/строками, создаются два объекта <style>.

Функции инициализации

Свойства init, содержащие функции инициализации, могут быть определены как для манифеста в целом, так и для каждого контрола.

Функции исполняются однократно при старте формы и могут служить для получения исходных данных, создания HTML-каркаса, инициализации сторонних rich ui плагинов.

Любая init-функция может вернуть promise – таким образом функция сигнализирует об асинхронной операции. Инициализации формы продолжается, как только promise перейдёт в состояние resolved.

Пример ниже рисует HTML-скелет формы, загружает данные из внешнего источника, строит на их основе <select>, монтирует на него плагин и после этого делает форму видимой.

Обработчик, снимающий полупрозрачность, подписан на окончание инициализации всей формы.

Функция инициализации исполняется после исполнения секций inherit,  require, files и style, если таковые в манифесте присутствуют.

Инициализация поля radio, а, значит, и подписок pub/sub производится после успешного окончания исполнения init-функции формы и полной инициализации всех контролов.

Вложенные формы

Каждый экземпляр формы $.my может служить контролом для другой формы $.my. Формы могут быть вложены друг в друга, более того, из вложенных форм можно формировать списки, связанные с массивами как однотипных, так и разнотипных элементов.

Связывание вложенных форм с данными возможно только по фиксированной ссылке – поле bind может иметь только строковое значение.

Дочерняя форма

Дочерние формы могут быть асинхронными, это учитывается при инициализации родительской формы.

Однородный список

Для генерации списка однотипных форм достаточно связать дочернюю форму с массивом объектов и указать свойство list, содержащее HTML-конструкцию контейнера для каждого экземпляра дочерней формы.

Вставка и удаление производятся вызовом событий insert и remove на любом контроле внутри списка. Событие вставки также применимо к самому объекту списка.

Вставка поддерживает дополнительный аргумент, указывающий на позицию вставки и передающий вставляемый объект. Синтаксис вставки из bind-функции $control.trigger ("insert", param). Параметр может быть следующим:

  • "before" вставляет пустой объект перед вызвавшим элементом списка
  • "after" – аналогично, но после
  • {where: "before" | "after" | index, what: ObjToInsert} – вставляет переданный объект в указанную позицию. Если передан индекс – вставка производится перед элементом с указанным индексом.

Список разнотипных форм

Свойство manifest может быть не только объектом или ссылкой на объект, но и функцией. Функция должна в зависимости от переданного элемента списка вернуть подходящий для его обработки манифест.

Функция выбора манифеста также может использоваться для «проброса» в дочернюю форму каких-то свойств родительской. Для этого достаточно расширить возвращаемый манифест на это свойство.

Поле stamp введено в элементы списка для их однозначной идентификации при перестановке. 

В целом, динамическая подстановка манифеста в зависимости от типа объекта позволяет создавать легко расширяемые приложения.

Идентификаторы дочерних форм

При связывании массива со списком дочерних форм возможно указать свойства id и/или hash, содержащие функции генерации уникального идентификатора и хэша экземпляра дочерней формы. Хэш служит как индикатор изменений, требующих перерисовки, а идентификатор – для привязки обновлений к уже имеющейся в списке форме.

Свойства id и hash могут быть строками или массивами строк – тогда как значение берётся поле формы с соответствующим ключом либо конкатенация значений полей (если указан массив строк).

Если значения id или hash не указаны, используется sdbm hash. 

Свойства inherit и expose

Поле inherit манифеста указывает, какие свойства форма должна унаследовать от родительской формы при инициализации. Родительской считается первая форма, встретившаяся в цепочке родителей в DOM.

Поле expose ограничивает набор свойств, которые могут быть унаследованы от данной формы. Если поле не определено, «дочки» могут наследовать любые свойства.

Монтирование унаследованных свойств на runtime-экземпляр манифеста формы происходит в самом начале инициализации, до исполнения любых других действий.

Внешние ресурсы

Для нормального исполнения форме может требоваться наличие внешних бибилиотек, а также предзагруженных данных, смонтированных как свойства манифеста (например, динамические списки для дропдаунов и т.п.).

Для проверки и предзагрузки ресурсов служит свойство require манифеста.

xxxxxxxxxx
 
<!-- Put HTML code here -->
xxxxxxxxxx
 
({
  require:[
    // First group
    {
      // Checks if select2 plugin loaded –
      // window.$.fn.select2 isn’t undefined –
      // and loads it if necessery
      "$.fn.select2": "http://some.url/select2.js",
      
      // Same here, but for manifest property Dict,
      // and with extended params defined
      "this.Dict" :{
        url: "http://some.url/some.json",
        dataType: "json"
      }
    },
    
    // Second group, executed after all 
    // resources of the first group are loaded
    {
      // some keys with urls as values
    }
  ],
  init: function(){ /* ... */ },
  ui:{ /* ... */ }
})
xxxxxxxxxx
 
/* Container for CSS code */

Проверки и предзагрузки, определённые полем require, исполняются в самом начале инициализации формы. Любой сбой делает дальнейшую инициализацию невозможной – promise формы принимает состояние rejected.

Ресурсы, встроенные в манифест

Манифест может содержать base64-представления файлов, которые при старте формы получат локальный URL.

После инициализации сессионный URL ресурса доступен через this.files["image.png"].url из любой функции внутри манифеста. Формат представления ресурсов совпадает с форматом поля _attachments БД CouchDB.

Параметры

Параметры формы передаются в объекте params манифеста и применяются однократно при инициализации экземпляра формы.

xxxxxxxxxx
 
<!-- Put HTML code here -->
xxxxxxxxxx
 
var manifest = { 
  data:   { /* data section */ },
  ui:     { /* ui section */ },
  init:   function ( $form ){ /* init */ },
  // Manifest settings
  params: { 
    container:function ($control) {/**/}, // Container getter, returns $ obj
    change: function ($form){},           // Change handler
    recalcDepth:2,                        // Depth of dependencies resolver tree
    delay:0,                              // Delay of bind invocation, ms
    strict:false,                         // If true form assumed unjsonned
    errorTip:".my-error-tip",             // $ selector of err tip container
    errorCss:"my-error",                  // Class applied to container on err
    pendingCss:"my-form-pending",         // Class applied when form starts
    remember:0,                           // Undo steps to remember
    historyDelay:100                      // Min history steps gap, ms
    restyle:-1                            // Custom styles recalc delay on window.resize
  }
}
xxxxxxxxxx
 
/* Container for CSS code */

Параметры формы recalcDepth и delay могут быть переопределены локально для каждого контрола, остальные параметры действуют глобально для всей формы.

Команды

Команды имеют синтаксис $obj.my("cmd", argument). $obj может быть как формой, так и контролом.

Команды формы

Команды служат для управления формой после её инициализации. Синтаксис – $("#runningForm").my("command", arg). Команды могут быть следующие.

$form.my('data')

.my("data") возвращает ссылку на «живой» объект с данными формы.

.my("data", object) заменяет данные формы глубоким клонированием object. Данные могут заменяться частично.

$.form.my('disabled')

Устанавливает или возвращает состояние формы. $form.my("disabled", true) помечает все контролы и форму, как временно неактивные. Форма перестаёт реагировать на ввод. $form.my("disabled") возвращает false если форма активна.

$form.my('valid')

Возвращает true, если все поля формы валидны, иначе false.

$.form.my('errors')

Возвращает объект вида {"#selector":"Error mesage", ...}, если форма или дочерние формы с указанным check:true содержат ошибки. Если ошибок нет, возвращается пустой объект {}.

$form.my('manifest')

Возвращает рантайм-версию манифеста формы.

$.form.my('redraw')

Инициирует полную перерисовку всех полей и дочерних форм. 

$form.my('restyle')

Инициирует пересчёт всех динамических стилей у формы и видимых дочерних форм.

$form.my('remove')

Отмонтирует плагин от HTML-объектов, отключает все плагины и снимает все обработчики событий контролов. Возвращает данные формы на момент отключения.

$form.my('undo')

$form.my("undo", steps) откатывает значение формы на steps шагов назад. Для корректной работы при инициализации должен быть передан параметр history с количеством шагов undo. По умолчанию undo не запоминается. Параметр steps может быть опущен.

$form.my( )

Возвращает data object "my", смонтированный плагином на DOM-объект формы. Эквивалент $form.data("my"). Возвращаемый объект содержит следующие свойства:

  • cid уникальный идентификатор формы
  • mid уникальный идентификатор манифеста
  • data ссылка на объект data формы
  • id идентификатор, поле id манифеста, если было указано, либо автоматически сгенерированное значение
  • initial клон данных на момент инициализации
  • manifest рабочий манифест формы
  • params полный список параметров, с которыми форма инициализирована
  • ui внутренний рантайм-список объектов, практически совпадает с ui-секцией манифеста

Команды контрола

Общий синтаксис $control.my("command", arg). Команды:

"container"

Возвращает DOM-контейнер контрола как объект jQuery.

"find"

$ctrl.my("find", "#selector") ищет и возвращает jQuery-объект #selector внутри формы, вглубь от её корневого элемента.

"insert"

$ctrl.my( "insert", where, what ) ищет родительский список для контрола и вставляет в него объект what в позицию where. Для вставки в конец списка – where:1e12, например.

"remove"

Удаляет из родительского списка элемент, внутри которого находится контрол.

"val"

Возвращает значение контрола.

$control.my()

Возвращает data object "my" контрола. Свойства:

  • data ссылка на объект data всей формы (не контрола)
  • errors ссылка на объект список ошибок всей формы
  • events строка, список событий, на которые «откликается» обработчик изменения контрола
  • id уникальный id манифеста всей формы
  • params параметры всей формы
  • root ссылка на корневой jQuery-объект формы
  • selector jQuery-селектор контрола
  • ui раздел объекта ui манифеста формы, отвечающий за данный контрол

Доставка, кэширование
и сборка

Манифест – единый объект, с помощью которого можно полностью определить внешний вид формы и её поведение. 

JSON-представление

При условии конвертации регулярных выражений и функций в строки, манифест – стандартный JSON-объект.

jQuery.my содержит функцию $.my.tojson (obj), корректно конвертирующую в JSON javascript-объекты с функциями и регэкспами. 

$.my.tojson( {x: function () {}} ) >> '{"x": "function (){}"}'

При такой конвертации теряется scope, однако манифесты устроены так, что функциям не требуется фиксация контекста.

Обратная операция $.my.fromjson( extJsonString ) преобразует JSON-строки с определениями функций и регулярных выражений обратно в корректный объект.

При преобразовании строковых представлений регэкспов и функций в js-объекты используются конструкторы, а не eval().

Кэширование

Манифесты, имеющие id вида "ns.Name1.Name2" могут быть закэшированы исполнением $.my.cache( manifest ). Закэшированная форма становится доступна из других манифестов – указание manifest: "ns.Name1.Name2" в описании дочерней формы подставит манифест из кэша при инициализации.

Для корректного кэширования поле id должно быть строкой латиницей, без пробелов, в dot notation, минимум из двух элементов. ns – произвольная строка пространства имён, цепочка Name1.Name2 .... NameN – уникальное имя формы.

Такой подход позволяет формам использовать компоненты друг друга без дублирования кода.

Выборка из кэша – $.my.cache( formId ).

Сборка модулей

Компоненты идентификатора id формы определяют «путь» внутри глобального кэша, куда будет монтироваться переданный манифест.

Если сначала закэшировать манифест с id:"app.Name", а затем другой манифест с id:"app.Name.Component", внутри первого манифеста в кэше появится поле Component, содержащее второй манифест.

Таким образом последующая выборка $.my.cache( "app.Name" ) вернёт манифест, уже содержащий компонент app.Name.Component  в свойстве Component.

Компонентная сборка манифестов позволяет собирать манифесты из многих компонентов, реализуя функциональность плагинов, расширений и т.п. 

Pub/sub и свойство radio

Как формы, так и контролы могут отправлять и слушать сообщения в каналах pub/sub. Эта техника хорошо подходит для широковещания сообщений неопределённому кругу получателей без проверки доставки.

Отправленные в канал сообщения могут распространяться как только внутри приложения, так и между приложениями (между разными экземплярами разных манифестов). Эти две модели распространения называются, соответственно, «локальное радио» и «глобальное радио».

Способ подписка на события одинаков в обоих случаях – это поля listen в поле соответствующего контрола раздела ui

Разница заключается в способе отправки и распространения сообщений.

Локальное радио

Когда сообщение отправляется в канал от имени какого-то контрола конструкцией $ctrl.trigger("radio", {channel:"chName", msg:message}),  происходит «всплытие» пакета с сообщением.

Всплытие останавливается, как только:

  • встречается форма, у которой в разделе radio обозначен канал, в котором всплывает сообщение, или
  • сообщение достигает узла body.

После этого сообщение «отражается» и распространяется вниз всем доступным получателям-дочкам.

Таким образом, если необходимо оставить радиоканал внутри формы, её манифест должен иметь свойство radio (массив строк или строка с запятыми), в котором перечислен этот канал. 

Глобальное радио

Второй способ отправки сообщений в канал – глобальный вызов формата $.my.radio("channelName", message). Такой способ доставляет сообщение всем подписчикам канала, не зависимо от того, есть у формы поле radio с обработчиком канала или нет.

Сервисные функции

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

$.my.tojson( obj )

Конвертирует объект в json. В отличие от JSON.stringify не удаляет функции и регэкспы, а конвертирует их в строковое представление.

$.my.fromjson( string )

Парсит JSON-строку в объект, строковые представления функций и регэкспов разворачиваются до функций/регэкспов.

$.my.ajax( obj or fn)

Выполняет (obj) или заменяет (fn) внутреннюю реализацию ajax, используемую, например, при обработке require. После старта $.my – просто обёртка для $.ajax.

При запросе с obj возвращает promise.

$.my.f.mask( obj, mask )

Делает выборку из объекта по маске. Маска может быть другим объектом, строкой-указателем в dot notation или массивом таких указателей.

xxxxxxxxxx
 
<!-- Put HTML code here -->
xxxxxxxxxx
 
$.my.f.mask( {a:{b:1, c:2, d:{ e:3 }}}, "a.d.e" )         » 3
$.my.f.mask( {a:{b:1, c:2, d:{ e:3 }}}, ["a.d", "a.b"] )  » [{e:3}, 1]
$.my.f.mask( {a:{b:1, c:2, d:{ e:3 }}}, {a:{c:true}} )    » {a:{ c:2 }}
xxxxxxxxxx
 
/* Container for CSS code */

Если значения по ссылке не существует, возвращается undefined.

$.my.f.unmask( obj, mask ) 

Разворачивает свёрнутый маскировщиком результат в объект. Может использоваться для быстрого создания разветвлённых объектов.

xxxxxxxxxx
 
<!-- Put HTML code here -->
xxxxxxxxxx
 
// Two arguments
$.my.f.unmask( [{e:3}, 1], ["a.d", "a.b"] )         
  // returns {a:{b:1, d:{ e:3 }}}
$.my.f.unmask( ["","",""], ["a.b.c","e.f","e.h"] )  
  // returns {a:{b:{c:""}}, e:{f:"", h:""}}
// Three arguments
var obj = {a:[5]};
$.my.f.unmask (obj, [6,7], ["a.1", "c.d"]);
  // Returns mutated obj, that is now
  // {a:[5,6], c:{d:7}}
xxxxxxxxxx
 
/* Container for CSS code */

$.my.f.sdbmCode( any )

Возвращает быстрый хэш переданного аргумента. Хэш вычисляется по алгоритму sdbm из Berkeley DB и не является криптографическим.

Отличается высокой скоростью работы.

$.my.f.css2json( string )

Конвертирует строковое представление наборе CSS-правил в объект формата секции style манифеста.

Модальный диалог

В плагин включена promise-реализация модальных и немодальных диалогов. Диалог может быть создан из манифеста, HTML-строки или из объекта jQuery, ссылающегося на изображения.

Модальный диалог – синглтон, блокирующий ввод для всей страницы. Наличие ужé открытого модального диалога делает невозможным открытие нового до закрытия имеющегося.

Немодальный диалог – «всплывающая» по месту вызова контекстуальная форма, например, настройки.

$pivot.modal( obj , done )  » promise

Инициализирует или закрывает всплывающее окно. Момент закрытия диалога может фиксироваться как функцией done, так и обработчиками промиса. В отличие от обработчиков внутри промиса, функция done может отменить закрытие.

Объект $pivot может быть контролом или просто объектом внутри $.my-формы, при уничтожении формы все подчинённые модальные диалоги и поп-апы будут принудительно закрыты. 

К одному объекту $pivot не может быть привязано более одного открытого диалога. Наличие привязанного диалога можно определить по существованию объекта $pivot.data("modal").

При вызове в формате $.my.modal(obj), без указания объекта привязки, выводится глобальный блокирующий модальный диалог, не привязанный к какой-либо форме.

Объект obj:

Object Инициализирует модальный диалог с интерактивной формой, определяемой манифестом. Свойства объекта:

  • manifest манифест формы
  • data данные для формы, форма мутирует именно этот объект
  • root jQuery object, который будет использоваться как контейнер для модального диалога. Если не указан, берётся контейнер контрола или ближайшая родительская форма.
  • width ширина формы
  • esc разрешает «холодное» закрытие по нажатию Esc – promise принимает reject ("Cancelled"); по умолчанию false для интерактивных диалогов
  • enter разрешает нормальное («тёплое») закрытие по нажатию Enter – promise принимает resolve(formData); по умолчанию false
  • nose строка, определяет положение «носика» всплывающего окна. Допустимые значения – "left", "right", "top", "bottom"
  • global Булево значение, указание true выводит глобальный модальный диалог (не более одного на всё окно)
  • align строка, смещение модала относительно объекта-пивота, вида top:103%;left:0px – что означает «отступить 103% высоты объекта-пивота от верхней грани и ноль от левой грани»
  • screen Булево значение или строка-цвет. Управляет показом накрывающего корневой элемент фона.
  • focus Булево значение или строка-селектор, управляет автофокусом после всплытия окна. По умолчанию true.
  • drag Булево значение или строка-селектор, делает окно таскаемым мышкой. Работает, если загружен плагин jQuery UI Draggable. Указание селектора сделает поп-ап таскаемым только за селектор.
  • bound Число, минимальное расстояние в пикселях от границ root-объекта до поп-апа, либо false, если ограничение не требуется
  • done – функция, разрешающая/запрещающая закрытие.

Boolean Закрывает диалог. Закрытие может происходить по «тёплой» и «холодной» схемам. Тёплое закрытие $.my.modal(false) отменяемо – перед закрытием вызывается функция done(null or err, data), которая может вернуть true и диалог останется открытым (а промис – в состоянии pending). Холодное закрытие $.my.modal(true) вызывает done (null, null) и вне зависимости от результата переводит промис в состояние reject ("Cancelled").

jQuery image Картинка будет выведена во всплывающем окне с максимально доступными размерами.

HTML string Всплывающий диалог с HTML-контентом.

При удачной инициализации возвращает promise, который станет resolved или rejected по закрытию диалога.  Возвращаемый promise принимает состояние resolved с данными манифеста или значением true для неинтерактивных диалогов по «тёплому» закрытию.

$.my.modal.visible() » boolean

Возвращает видимость глобального модального диалога.

$.my.modal.parent( selector or null ) » jQuery object

Устанавливает или возвращает DOM-объект, внутрь которого будут монтироваться всплывающее окно и затеняющая шторка. Используется в случае если модальный диалог должен наследовать стили от какого-то контейнера. По умолчанию $("body").



© 2016 ermouth, лицензия MIT