Генерация URL для rest API

Для генерации адреса c целью доступа к API есть специальный метод

SUGAR.App.api.buildURL(
	module, // Строка, имя подуля (Calls,Accounts,Opportunities)
	action, // действие, параметр необязательный,
			// прилепляется в конец итогового URL
	attributes, // необязательный объект,
				// можно задать id записи, link и relatedId
	params // необязательный объект для дополнения адреса get переменными
)

К примеру для привязки существующего звонка к сделке — url должен быть следующего вида
/<module>/:record/link/:link_name/:remote_id
для получения такого результата параметры должны быть следующими

SUGAR.App.api.buildURL(
	'Accounts', // основной модуль
	'', // не нужен в данном случае
	{
		link : 'calls', // связанный модуль
		id : '123-456', // ид сделки (записи основного модуля)
		relatedId : '789-012' // ид звонка (записи связанного модуля)
	}
)

на выходе получаем следующий url:
rest/v10/Accounts/123-456/link/calls/789-012
его уже можно использовать в вызове методов rest API

Для случаев типа /<module>/filter/count — достаточно указать модуль и в качестве action — оставшуюся часть ссылки «filter/count»

SUGAR.App.api.buildURL('Accounts', 'filter/count')

Параметр params этого метода введен, видимо, на всякий случай, совершенно, правда, непонятно на какой.
Этот метод всегда работает в паре с SUGAR.App.api.call, который принимает объект data со всеми данными, которые необходимо передать на сервер, и сам уже решает куда помещать эти данные: в post или в get параметры.

Вызов метода rest API javascript

Вызов метода API бэкэнда осуществляется следующим методом javascript API

SUGAR.App.api.call(method, url, data, callbacks, options)

первый параметр method — это вопреки ожиданиям не HTTP метод, а CRUD action, который может принимать одно из значений:
— read (GET)
— create (POST)
— update (PUT)
— delete (DELETE)
В скобках указан HTTP метод, который придет в бэкенд, именно он должен быть указан в массиве регистрации метода API c ключом «reqType», к примеру для обновления записи (метод js api — «update», метод для PHP rest api — «PUT»)

public function registerApiRest() {
    return array(
        'saveSomething' => array(
	    'reqType' => 'PUT',
	    'path' => array(
                'Opportunities', 
                '?', 
                'save-something'
            ),
            'pathVars' => array(
                'module', 
                'id', 
                'method'
            ),
            'method' => 'saveSomething',
            'shortHelp' => 'save something for opportunity',
            'longHelp' => '...save-something.html',
    ),
    ...

Второй параметр — url
Указывать прямую ссылку — явно дурной тон, будем использовать для этого возможности js api, об этом в следующем посте
Третий параметр — data — привычный объект c данными, которые необходимо передать на сервер, к примеру

{
    summa: 10000,
    sales_stage: "work"
}

Четвертый параметр — callbacks — объект с функциями-коллбэками

{
    success: function(answer) {
        "Успешное завершение вызова, answer (при дефолтных параметрах) - объект";
    },
    error: function(error) {
        "Произошла какая-то ошибка, лучшее решение в этом случае - показать сообщение об ошибке и добавить какие-то рекомендации человеку, который возможно это увидит";
        App.alert.show('wrong_save', {level:'error',autoClose:false,messages:error.message+"\n Попробуйте перезагрузить страницу и попробовать заново, если не поможет - сообщите программисту."});
    },
    complete: function() {
        "Этот коллбэк отработает в любом случае, тут можно убрать блокировки на время загрузки"
    },
}

Ну и последний параметр — это объект с параметрами для совершения ajax запроса
Дефолтные параметры:

{
    url: null, // если url не указали вторым параметром - его можно указать здесь
    type: "GET", // тут можно указать HTTP метод напрямую
    dataType: 'json',
    headers: {},
    timeout: 30000, // 30 sec
    contentType: 'application/json'
}

Тэг base и jquery tabs

Сегодня мне вынес мозг стандартный плагин jquery. Казалось бы, какие с ним-то могут быть трудности, давно все сколько-нибудь возможные баги выловлены, все пользуются, у всех все нормально.
У меня, в принципе, тоже. Было. До сегодняшнего дня.
На странице уже есть один tabs с тремя вкладками, контент в которые загружаются по аяксу. Все работает как положено.
И тут понадобился мне переключатель для двух табов. Аякс был не нужен, всего лишь нужно было, чтобы открывался по клику на вкладку див со сгенерированным яваскриптом графиком. Один график желательно, чтобы всегда был под рукой, а другой связанный с ним по смыслу, должен быть легкодоступным. То есть строим график в первой вкладке после загрузки страницы, а второй только после того, как открыли вторую вкладку по ивенту activate. Все довольно банально, делов на пару минут.
«А вот хрен тебе» — сказал мне плагин… И загрузил и вставил мне перед первым графиком главную страницу сайта.
Думаю, что это за хрень такая? Лезу проверять: ссылки в списке указывают на якоря, совпадают с идэшниками у дивов. Структура идентичная указанной в примере на jqueryui. На всякий случай вставляю в href идэшники копипастом, чтобы исключить ошибку по этой причине.
Обновляю страницу — перед контентом первой вкладки загружается аяксом главная страница сайта ( Смотрю в дебагер: в контейнере добавлены к моим двум дивам еще два дива, в один из которых загружена главная страница сайта.
Копипащу со страницы примера html код

tab1
tab2

их рыбу заменил на текст покороче, чтобы глаза не мозолил.
И что бы вы думали? Перед tab1 загрузилась главная страница.
Добавляю в li аттрибут aria-controls с указателями на ид дивов-контейнеров, дополнительные дивы перестали создаваться, главная страница загружается в первую вкладку и заменяет собой контент, который там был %)
Копирую исоlный код страницы в отдельный файл оставляю только head со стилями, только div#tabs в body и script с загрузкой jquery и jquery из cdn яндекса. Все тот же идиотский прикол…
Сношу к хренам все стили, заменяю загрузку скриптов на cdn jquery. Нифига..
Остается только несколько записей в head, кодировка страницы, метатеги, автор, ссылки на иконку сайта, стандартная все, вроде, лабуда. Начинаю сносить уже и все оставшееся по одной строчке, потому что хрен его уже знает, что бы еще сделать, бустраповский табулятор не вариант вешать, нет нужных мне плюшек, допиливать неохота, кодить свой тем более лень.
И тут: о чудо! Табулятор заработал как надо. И во всем был виноват какой бы вы думали тэг?
Правильно,


Как только проблема вылечилась, сразу и понятно стало, как так получилось. ссылка <li><a href="#tabs-1" из-за тега base выглядит для скрипта как href="http://mysite.ru/#tabs-1", которую он и пытается загрузить в заботливо созданный им новый контейнер.
Тэг base добавил из-за того, что тема была покупная, пути к картинкам в некоторых js и css файлах были прописаны относительные, но без предваряющего ./ Добавление тега base проблему с такими путями решило, но, как оказалось добавило более интересную. Пришлось убрать <base и поправить пути к картинкам. Надо только будет просмотреть логи на предмет не найденных путей и поправить неправильные пропущенные.

Расстановка переносов в html тексте на русском языке

Существует серия правил css для разных браузеров hyphens, -moz-hyphens, -webkit-hyphens, -ms-hyphens

К сожалению эти правила не работают в chrome, поэтому пришлось искать выход.
Я не сторонник нагрузок на сервер, которых можно избежать, поэтому начал искать нет ли уже готового решения на javascript — пусть работу, которую можно переложить на браузер — делает браузер. Плюс к тому, на стороне сервера пришлось бы изменять текст, который попадет в браузер, что может плохо сказаться на индексации поисковиками. и как быть?

Сортировка по русской дате в dataTable

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

$.fn.dataTableExt.oSort['ru-date-asc']  = function(a, b) {
	a = a.split(/\D+/).reverse().join('');
	b = b.split(/\D+/).reverse().join('');
	return ((a < b) ? -1 : ((a > b) ?  1 : 0));
};
$.fn.dataTableExt.oSort['ru-date-desc']  = function(a, b) {
	a = a.split(/\D+/).reverse().join('');
	b = b.split(/\D+/).reverse().join('');
	return ((a > b) ? -1 : ((a < b) ?  1 : 0));
};

Не очень, конечно, понятно почему было не сделать одну функцию, в которую бы передавался порядок сортировки, ну да ладно.
И после объявления можно использовать сортировку для нужных столбцов:

$('#tasks_table').dataTable({
    ...
    aaSorting : [[2, 'desc']],
    aoColumnDefs: [ 
        {
            sType : 'ru-date',
            aTargets : [2]
        }
    ]
    ...
});

X-editable и Select2

x-editable select2Хочу рассказать о своих мытарствах при использовании библиотеки x-editable совместно с select2 для редактирования данных в таблице dataTables что могло пойти не так?

dataTables и TouchSpin

UdIM1vU
Встроить фильтр по значению в столбце таблицы dataTables прямо в заголовке этого столбца показалось мне хорошей идеей. Не надо впихивать отдельную форму фильтрации на страницу и сам фильтр всегда перед глазами и воспринимается более наглядно.
Но при клике в поле ввода и на кнопках «+» и «-» срабатывает сортировка по столбцу, что, конечно же, совершенно недопустимо. Читать далее dataTables и TouchSpin