Иногда человечество поражает своей тупостью и нежеланием видеть ситуацию так, как она действительно есть… ( Неизвестные )
Возможно, данный эпиграф имеет ко мне большее отношение, чем я думаю, но хочется надеяться на лучшее…
Мой первый пост получился, мягко говоря, не очень, но у любого человека должна быть возможность все исправить и сейчас я попробую этим воспользоваться.
Расскажу небольшую историю, которая случилась со мной на работе. Дело было давно, как-то приасанили меня к новому web-проекту, проектированием которого я не имел ни малейшего отношения. Тимлид первым делом залил в svn php и js framework'и.
В качестве JS был выбран Mootools 1.2.2 или 1.2.4, а в дополнение к нему Jx UI Library в виде JxLib. Все шло хорошо, проект подходил к своему завершению, версии браузеров росли и тд. И вот, в один прекрасный момент, придя на работу я увидел баг, в кором говорилось а том что в IE9 (а он только-только вышел) админка не работает вообще. Первым делам начал искать проблему, как оказалось Mootools 1.2.x — не поддерживает IE9, а вот в версиях over 1.3.x — все работает и летает. Ну, долго не думая, качаем последнюю версию сего чуда и ставим. Радоватся пришлось не долго — начали вылазить баги в JxLib. Оказывается, что проект 'заглох', и последняя доступная версия совместима только с Mootools 1.2.x. Много времени прошло с того момента, но тот говнокод, с помощью которого пришлось залатать этот баг, надломал мою веру в Mootools и ему подобные вещи. Теперь, я думаю, стало ясно, откуда такое отношение к JS либам.
А теперь хочется ответить (пояснить почему так, а не иначе) на некоторые комментарии.
1)
Сonsole.log — вот этого я не ожидал, я предлогал многое но только не это. Я в курсе того что он (console.log) поддерживает форматирование и передачу нескольких параметров, но я не могу вспомнить, когда последний раз я этим пользовался. Мне вполне хватает одной переменной, а усложнять _d() ради того чтобы использовать всю мощь сonsole.log раз в месяц, — нецелесообразно (проще написать заветные 11 символов `сonsole.log`).
2)
Функция addEvent продолжит выполняться после обработки ошибки с помощью функции _d.
— я в курсе этого, это сделано специально, ибо проверку на существование DOM объекта, а предпочитаю делать в основном скрипте, но вы меня меня убедили — добавлю return false.
3)
Загрязнение глобального пространства переменных — очень плохой тон.
— тут вы правы, но кроме readyList = []; я ничего не могу найти глобального :)
Ну что ж, я думаю пора заканчивать эти холивары и начать рассмотрение новой партии JS кода.
// Учитывая какие гуру сидят на хабре - первые 3 функции (тем более что их названия сами за себя говорят) я оставлю без комментариев
/**
* Function insert DOM element before some element
*
* @version 2012-11-06
* @param Object new_element
* @param Object targetElement
* @return void
*/
function insertBefore(new_element, targetElement)
{
targetElement.parentNode.insertBefore(new_element, targetElement);
}
/**
* Function insert DOM element after some element
*
* @version 2012-11-06
* @param Object new_element
* @param Object targetElement
* @return void
*/
function insertAfter(new_element, targetElement)
{
var parent = targetElement.parentNode;
//if the parents lastchild is the targetElement...
if(parent.lastchild == targetElement)
parent.appendChild(new_element);
else
parent.insertBefore(new_element, targetElement.nextSibling);
}
/**
* Function make clone of income object
*
* @version 2012-11-07
* @param Object obj
* @return Object
*/
function clone(obj)
{
if(obj == null || typeof(obj) != 'object')
return obj;
var temp = {};
for(var key in obj)
temp[key] = clone(obj[key]);
return temp;
}
// Функция вернет следующий DOM объект в узле, если такой существует
/**
* Function return next element in dom object
*
* @version 2013-01-08
* @param Object obj
* @return Object
*/
function getNext(obj)
{
obj = obj.nextSibling;
if(!obj)
return false;
while(obj.nodeType != 1){
obj = obj.nextSibling;
if(!obj)
return false;
}
return obj;
}
// Функция вернет предыдущий DOM объект в узле, если такой существует
/**
* Function return previous element in dom object
*
* @version 2013-02-13
* @param Object obj
* @return Object
*/
function getPrevious(obj)
{
obj = obj.previousSibling;
if(!obj)
return false;
while(obj.nodeType != 1){
obj = obj.previousSibling;
if(!obj)
return false;
}
return obj;
}
// Следующие 2 функции - это установка и чтение Cookies. C ними, я думаю, проблем не будет
/**
* Function new Cookies
*
* @version 2013-03-26
* @param string name
* @param string value
* @param string expires
* @param string path
* @return void
*/
function setCookies(name, value, expires, path)
{
if(!name || !value)
return 0;
if(!expires){
expires = new Date();
expires.setTime(expires.getTime() + (1000 * 86400 * 365)); //save 1 year
}
if(!path)
path = '/';
// set Cookies
document.cookie = name+'='+escape(value)+'; expires='+expires.toGMTString()+'; path='+path;
}
/**
* Function return Cookies value by name
*
* @version 2013-03-26
* @param string name
* @return void
*/
function getCookies(name)
{
var results = document.cookie.match ( '(^|;) ?' + name + '=([^;]*)(;|$)' );
if ( results )
return ( unescape ( results[2] ) );
else
return null;
}
// А вот здесь придется задержатся
// Вначале создаем функцию Ajax(params), а потом прикручиваем к ней prototype
/**
* Ajax object
*
* Send request by post/get to some url
*
* @version 2013-02-13
* @param Object params
* @return Object
*/
window.Ajax = function(params)
{
// параметры по умолчанию
this.options = {
// default url
url: '',
// default method
method: 'get',
// Is synchronous request?
async: true,
// in seconds
timeout: 10000,
// callback function, in default - empty function
onComplete: function(){}
};
// set config params
this.setConfig(params);
// создаем и подготавливаем кроссбраузерный XMLHttpRequest для дальнейшей работы
// initialize
this.init(params);
};
/**
* Pablic methods
*/
window.Ajax.prototype = {
/**
* some internal params
*/
xml_http_request: null,
timeout: null,
// json|xml|text
response:'json',
// устанавливаем параметры ajax запроса, если таковы были переданы
/**
* configure functionality
*/
setConfig: function(opt)
{
// set url
if(opt.url != undefined)
this.options.url = opt.url;
// set method
if(opt.method != undefined)
this.options.method = opt.method;
// set asynchronus param
if(opt.async != undefined)
this.options.async = opt.async;
// set timeout
if(opt.timeout != undefined)
this.options.timeout = opt.timeout;
// set callback functions
if((opt.onComplete != undefined) && (typeof(opt.onComplete) == 'function'))
this.options.onComplete = opt.onComplete;
},
/**
* Initialize XMLHTTPRequest
*/
init: function()
{
// кроссбраузерное создание XMLHttpRequest
// Cross-browser compatibility for browsers
if (typeof XMLHttpRequest != 'undefined') {
this.xml_http_request = new XMLHttpRequest();
}
else{
try {
this.xml_http_request = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
try {
this.xml_http_request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (E) {
alert('Your brouser don't support Ajax technology. Please download real browser :)');
}
}
}
var self_ = this;
// устанавливаем метод (post/get), url, синхронный/несинхронный запрос
// open XMLHttpRequest
this.xml_http_request.open(self_.options.method, self_.options.url, self_.options.async);
/** Вешаем callback в случае успешного
*
* Список состояний readyState такой:
* 0 - Unitialized
* 1 - Loading
* 2 - Loaded
* 3 - Interactive
* 4 - Complete
* Состояния 0-2 вообще не используются.
*
* self_.xml_http_request.status - код ответа сервера (200, 404 и тд)
*/
// set callback function for XMLHttpRequest
this.xml_http_request.onreadystatechange = function(){
if((self_.xml_http_request.readyState == 4) && (self_.xml_http_request.status > 0)){
// отмена принудительного разрыва Ajax запроса (ниже будут пояснения)
// delete timeout
clearTimeout(self_.timeout);
// в завивимости от того, что вы ожидаете формируем ответ сервера
// JSON.parse - встроеный парсер JSON (старые браузеры - bye-bye)
if(self_.response == 'json')
var response = JSON.parse(self_.xml_http_request.responseText);
else{
if(self_.response == 'xml')
var response = self_.xml_http_request.responseXML;
else
var response = self_.xml_http_request.responseText;
}
// вызываем обработчик результатов
self_.options.onComplete(response);
}
}
},
// Данный метод нужен в очень специфических задачах, когда необходимо играться с заголовками для сервера
/**
* Set some headers if need
*/
setRequestHeader: function(name, value)
{
this.xml_http_request.setRequestHeader(name, value);
},
// Отсылаем Ajax на сервер
/**
* Send request
*/
send: function(params)
{
this.xml_http_request.send(params);
var self_ = this;
// про это я говорил ранее, если вас запрос повис или на серваке проблемы - обрываем его принудительно
// по умолчанию - 10 секунд ждем, хотя можно и больше
// set timeout need for abort request
this.timeout = setTimeout( function(){ self_.xml_http_request.abort(); }, this.options.timeout);
},
// Данная фишка для того, что бы вы могли в любое время прервать запрос, например при живом поиске (как у хабра или гугла :) )
/**
* Abort request
*/
abort: function()
{
this.xml_http_request.abort();
}
}
Пример использования Ajax():
new Ajax({
url: '/index.php',
timeout: 20000,
method: 'post',
onComplete: function(data){ alert(data);}
}).send('qwerty=123');
PS. Как и обещал, исходники. Статья написана для того, что бы люди не забывали истинный JavaScript и переходила на светлую сторону силы (печеньки тут тоже имеются :) ).
Автор: mamchyts