191 lines
5.8 KiB
JavaScript
191 lines
5.8 KiB
JavaScript
/*
|
||
*
|
||
* Реализация шаблона "издатель-подписчик"
|
||
*
|
||
*/
|
||
|
||
function Уведомитель()
|
||
{
|
||
function Подписка(id, отклик, уведомитель)
|
||
{
|
||
this.id = id;
|
||
this.отклик = отклик;
|
||
this.уведомитель = уведомитель;
|
||
};
|
||
|
||
this.уведомить = function()
|
||
{
|
||
// Попутно собираем подписки без отклика.
|
||
var безотклика = [];
|
||
for (var номер in this.подписки)
|
||
{
|
||
var подписка = this.подписки[номер];
|
||
if (подписка.отклик)
|
||
{
|
||
подписка.отклик();
|
||
}
|
||
else
|
||
{
|
||
безотклика.push(подписка);
|
||
}
|
||
}
|
||
|
||
// И удаляем их.
|
||
if (безотклика.length)
|
||
{
|
||
this._удалитьПодпискиБезОтклика(безотклика);
|
||
}
|
||
};
|
||
|
||
this.подписать = function(отклик)
|
||
{
|
||
var id = this._сгенерироватьUUID();
|
||
var подписка = new Подписка(id, отклик, this);
|
||
this.подписки.push(подписка);
|
||
return подписка;
|
||
};
|
||
|
||
this.отписать = function(подписка)
|
||
{
|
||
подписка.отклик = null;
|
||
};
|
||
|
||
this._удалитьПодпискиБезОтклика = function(удалить)
|
||
{
|
||
var подписка = удалить.shift()
|
||
while (подписка)
|
||
{
|
||
var индекс = this.подписки.indexOf(подписка);
|
||
if (индекс !== -1)
|
||
{
|
||
this.подписки.splice(индекс, 1);
|
||
}
|
||
var подписка = удалить.shift()
|
||
}
|
||
};
|
||
|
||
this._сгенерироватьUUID = function()
|
||
{
|
||
// https://stackoverflow.com/a/2117523
|
||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
|
||
/[xy]/g,
|
||
function(c)
|
||
{
|
||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||
return v.toString(16);
|
||
}
|
||
);
|
||
};
|
||
|
||
// Конструктор.
|
||
this.подписки = [];
|
||
}
|
||
|
||
/*
|
||
*
|
||
* Связь событий и реакций в виде последовательности (череды)
|
||
*
|
||
*/
|
||
|
||
function Мир()
|
||
{
|
||
// Разобрать события и реакции, выраженные в тексте.
|
||
this.разобрать = function(череда)
|
||
{
|
||
var соответствия = this._событияРеакции(череда);
|
||
for (var событие in соответствия)
|
||
{
|
||
if (!(событие in this.события))
|
||
{
|
||
this.события[событие] = new Уведомитель();
|
||
}
|
||
var реакции = соответствия[событие];
|
||
for (var номер in реакции)
|
||
{
|
||
const реакция = реакции[номер];
|
||
const название = this._имяФункцииИзРеакции(реакция);
|
||
const функция = eval(название);
|
||
var тут = this;
|
||
this.события[событие].подписать(function(){
|
||
функция(тут);
|
||
});
|
||
}
|
||
}
|
||
};
|
||
|
||
// Уведомить о событии при его наличии.
|
||
this.уведомить = function(событие)
|
||
{
|
||
if (событие in this.события)
|
||
{
|
||
this.события[событие].уведомить();
|
||
}
|
||
};
|
||
|
||
// Разобрать текст с событиями и реакциями, вернуть словарь их соответствия.
|
||
this._событияРеакции = function(текст)
|
||
{
|
||
var соответствие = {};
|
||
|
||
var элементы = текст.split("\n");
|
||
var имяСобытия = null;
|
||
for (var номер in элементы)
|
||
{
|
||
var элемент = элементы[номер];
|
||
// Пропускаем комментарии.
|
||
if (элемент.charAt(0) == "#")
|
||
{
|
||
continue;
|
||
}
|
||
var имя = элемент.trim();
|
||
// Пропускаем пустые строки.
|
||
if (!имя.length)
|
||
{
|
||
continue;
|
||
}
|
||
// Событие.
|
||
if (имя == элемент)
|
||
{
|
||
if (!(имя in соответствие))
|
||
{
|
||
имяСобытия = имя;
|
||
соответствие[имя] = [];
|
||
}
|
||
}
|
||
// Реакция.
|
||
else
|
||
{
|
||
соответствие[имяСобытия].push(имя);
|
||
}
|
||
}
|
||
|
||
return соответствие;
|
||
};
|
||
|
||
// Преобразовать имя реакции в название функции.
|
||
this._имяФункцииИзРеакции = function(реакция)
|
||
{
|
||
var имя = "";
|
||
|
||
var части = реакция.split(" ");
|
||
for (var номер in части)
|
||
{
|
||
var часть = части[номер];
|
||
имя += часть.charAt(0).toUpperCase() + часть.slice(1);
|
||
}
|
||
|
||
return имя;
|
||
};
|
||
|
||
// Конструктор.
|
||
this.события = {};
|
||
}
|
||
|
||
/*
|
||
*
|
||
* Создание глобального мира.
|
||
*
|
||
*/
|
||
|
||
мир = new Мир();
|