воскресенье, 30 июня 2013 г.

OpenCart: краткое описание товара

Как-то понадобилось мне вывести краткое описание продукта, чтоб получилось вот так:
Ну ок
Смотрим, у какой модели наш модуль инфу берет:

$this->load->model('catalog/product');

Хорошо, идем смотреть в модель product, ищем какую инфу он нам еще передает:
Как видим, описание нам передается

Пробуем достать его, в модуль добавляем это:
Вот так мы подцепили описание из модели и передали его нашему шаблону
А ну ка попробуем его вывести:

<div class="text"><?php echo $product['description']; ?></div>  
И получаем такое:

Описание вывелось! Но стоп: почему тэги вывелись текстом а не отрендерились? Я хз :о) Где-то они переводятся из html-сущности в текст, так давайте это исправим:



Прекрасно!
Ну теперь осталось лишь избавиться от тэгов и обрезать описание до нужных нам размеров:

'description' => substr(strip_tags(html_entity_decode($result['description'])), 0, 60)."...",
Вуаля!)))

воскресенье, 26 мая 2013 г.

Анимация в Flash: урок для начинающих

И так, достаем где-нибудь программу Adobe Flash (тут используется версия CS6), желательно её установить, запускаем и... половина работы сделана, далее расписаны лишь мелочи, которых должно хватить для рисовки угарного мульта и последующего его показа пьяным друзьям

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

Панель инструментов трогать почти не будем, мы будем использовать хот-кеи как настоящие джедаи

И так, находим рабочее поле/холст, готовимся в нем рисовать
Приготовились
Жмем  "В"(кисть) и ищем Properties (эта вкладка показывает настройки текущего инструмента/объекта)
Давайте попробуем нарисовать несколько линий, играясь с параметром Smoothing (размер кисти регулируем кнопками "[" и "]")
Должно получится нечто на подобии современного искусства
Жмем "Q"(выделялка), выделаем их (Ctrl+A - выделить все) и удаляем

А теперь нарисуй круг
Жмем "К"(заливка), тыкаем в середину круга и радуемся

Ну все, теперь мы готовы рисовать настоящий мульт!!1
Я хочу нарисовать голубое небо
Жмем "R"(прямоугольник), выбираем небесно голубой цвет заливки и заполняем им весь холст
В новом слое кистью рисуем землю, облака, солнце, и заливаем подходящими цветами (почему в новом слое? попробуйте продолжить работу без создания слоя, сами увидите :о) )
Вы можете столкнуться с маленькой проблемкой, которая изрядно бесит еще со времен Paint-a - невозможность залить пространство из за возможных дырок
ЗЫ: то, что за пределами холста, в исходном ролике видно не будет
В Adobe Flash решение есть! У инструмента заливки есть параметр степени пофигизма к дыркам!!1
Фухх, идем дальше
В новом слое нарисуем деревья
По-моему получилось восхитительно, и у вас, уверен, тоже!
Но кое чего явно не хватает, скорее всего объема
Жмем "Y"(ручка), и проделываем что-то типа такого:
А теперь заливаем нужные места цветами чуть-темнее исходных
Выделяем все (Q, Ctrl+A), и жмакаем на следующее:

Как видим, дерево очистилось от линий ручки, а тени остались
Что это вообще было?
Создай новый слой, нарисуй две фигуры отдельно друг от друга
Жмем "V"(выделялка объектов), выбираем одну фигуру, перетаскиваем на другую, убираем выделение (щелкаем где-нибудь в пустое место холста), опять выбираем нашу фигуру - и - о чудо! - они объединились! Что это за злое волшебство?
В Flash, на сколько я понимаю, один слой == один объект (есть конечно возможность рисовать в отдельный объект (подслой), но это уже другая история)
Tак вот: ручку мы использовали для рисования вспомогательных контуров, которые можно легко убрать

Давайте теперь в новом слое нарисуем Главного Героя (далее как гг) гордо стоящего на сотворенной нами земле

Надо бы вдохнуть в него жизнь, а то стоит там как столб
Жмем "F7"(новый кадр), и чтобы видеть предыдущий кадр (его "тень") тычем вооот сюда:
Рисуем чувачка еще раз, или не чувачка, на ваше усмотрение
ЗЫ: параметр fps можно понизить до 12, при этом пострадает плавность анимации, но зато придется меньше рисовать кадров :о)
Ну что еще?
Можно нажать "F6"(новый кадр, на основе предыдущего), или "F5"(увеличение размера кадра во временной шкале)
"<" и ">" - перемещение между кадрами
И святой "Enter"(пуск/пауза воспроизведения анимации)

Еще есть такая штука как Сцена (Scene)
Каждая из них это отдельный холст со своей временной шкалой, и в финальном ролике они воспроизводятся поочередно
"Ctrl+Enter"(построить ролик)

Ну в принципе и все )))
Рисовать - умеем, кадры создавать - умеем, что еще? Ну разве что пару уроков по чёрной магии не помешает :о)
А я пока отлучусь на кое-какое время:

вторник, 14 мая 2013 г.

Что лучше? Mozilla Firefox vs Google Chrome

Все началось с того, что комп начал сильно тормозить (если точнее, то тормоза были во время работы браузера)
Отнес на полную чистку (смазка кулеров и т п), но к моему величайшему удивлению тормоза остались О_О
Проверил температуру процессора во время воспроизведения ютубе-видео
В пределах нормы, но все же слишком горячо
Да и оперативка забита фиг знает чем
Поставил Firefox и - о Боги! - все работает без тормозов!(кхм, сравнительно)
Открыл в мозилле больше десяти вкладок - оперативы хавает ++280мб, открыл в хроме 7 вкладок - ++1000мб (мог бы и больше, но задолбался ждать когда они наконец-то загрузятся, в мозилле намного быстрее)
Получается православный хром уже не торт
Но почему так?

Что лучше? Mozilla Firefox vs Google Chrome - ответ прост: зависит от вашей машины
Chrome хорошо использует многоядерные процессоры, каждая его вкладка живет "своей жизнью", и это плохо сказывается на одноядерных
Если у вас нетбук, старый ноутбук или допотопный пк - Firefox однозначно подойдет именно вам
Если у вас больше одного ядра в процессоре и больше 2гб оперативки - Chrome это ваш браузер (то же касается Windows Vista/7/8)
В остальном же (функциональные возможности, количество расширений, удобство) они в принципе равны
На этом пока можно поставить точку в войне между двумя Гигантами
ЗЫ: эта запись является компиляцией моей темы в моем любимом форуме

Как сгенерировать/создать юзербар на PHP?

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


<?php
// Тип содержимого
header('Content-Type: image/png');

define('FONT_NAME', 'trebuc.ttf');//имя шрифта
define('FONT_SIZE', 9);//кегль
define('SPACING', 1);//расстояние между символами
$im = imagecreatefrompng('userbar_template.png')//достаем "подложку" юзербара
  or die('Cannot create image');
define('WIDTH', imagesx($im));
define('HEIGHT', imagesy($im));

// Создание цветов
$white = imagecolorallocate($im, 255, 255, 255);
$grey = imagecolorallocate($im, 128, 128, 128);
$yellow = imagecolorallocate($im, 255, 216, 0);
$black = imagecolorallocate($im, 0, 0, 0);

// Текст надписи
$text = "Введите название...";

//определяем нужный для нашего текста размер области
$coord = imagettfbbox(
  FONT_SIZE,  // размер шрифта
  0,          // угол наклона шрифта (0 = не наклоняем)
  FONT_NAME,  // имя шрифта, а если точнее, ttf-файла
  $text       // собственно, текст
);
$width = $coord[2] - $coord[0];
$height = $coord[1] - $coord[7];
// Зная ширину и высоту изображения, располагаем текст по центру:
//$X = (WIDTH - $width) / 2;
$Y = (HEIGHT + $height) / 2.5;

// Тень
imagettftextSp($im, FONT_SIZE, 0, 11, $Y, $white, FONT_NAME, $text, SPACING);

// Текст
imagettftextSp($im, FONT_SIZE, 0, 10, $Y, $white, FONT_NAME, $text, SPACING);

// Достаем вторую, верхнюю часть шаблона
$cover = imagecreatefrompng('userbar_template_cover.png')
  or die('Cannot create image');
// Накладываем её поверх нашего супер-юзербара
imagecopy ($im, $cover, 0, 0, 0, 0, WIDTH, HEIGHT);

imagepng($im);
imagedestroy($im);

// Функция эта украдена уже не помню у кого и где, но автору большая от меня благодарность - именно благодаря ей можно управлять расстоянием между символами, ибо стандартные средства этого не предусмотрели О_О
function imagettftextSp($image, $size, $angle, $x, $y, $color, $font, $text, $spacing = 0)
{        
    if ($spacing == 0){imagettftext($image, $size, $angle, $x, $y, $color, $font, $text);}
    else
    {
        $temp_x = $x;
        for ($i = 0; $i < strlen($text); $i++)
        {
            $bbox = imagettftext($image, $size, $angle, $temp_x, $y, $color, $font, mb_substr($text,$i,1,'UTF-8'));
            $temp_x += $spacing + ($bbox[2] - $bbox[0]);
        }
    }
}
?>
В html на время тестов можно прописать что-то типа такого:
<img src="userbar.php">
Php-скрипт выдает картинку в качестве результата благодаря записи header('Content-Type: image/png'), позже её надо бы убрать, а саму картинку сохранять на сервере
Ссылка на архив с шрифтом и шаблоном
Ну и конечно же можно генерить BB-коды и просто ссылки для вставки в html
//BB-code
$res = $res."[url=".ссылка на вашу страницу."][img]".ссылка на юзербар."[/img][/url]";
//HTML
$res = $res."<a target=\"_blank\" href=\"".ссылка на вашу страницу."\"><img src=\"".ссылка на юзербар."\" border=\"0\"></a>";

среда, 8 мая 2013 г.

Свои иконки

Каждый хоть раз наверняка хотел запилить свою иконку
Вот и я захотел
Случилось это под впечатлением от игры FEZ, а точнее - от её иконки (ибо сама игра так и не изволила загрузиться, но я не удивляюсь, ибо Фила Фиша всегда сопровождали баги)

 Взяв в руки AWIcons (наверное самый лучший редактор иконок) получилось вот это:
Ну все, конец этой бесполезной записи, мир

вторник, 7 мая 2013 г.

Динамическое обновление контента

Выкладываю свои наработки в этом направлении, ибо незачем добру пропадать
Или не добру
Вам виднее

Что конкретно делают нижеследующие скрипты? Перехватывают нажатие на ссылки, блокируя дефолтную загрузку страницы, вместо чего вызывается заданная пользователем функция, которая может грузить через аякс какую-то инфу Еще перехватываются нажатия на кнопки "назад-вперед" в браузере, т е сохраняется полноценная история просмотров, по которой можно переходить
На практике это выглядит вот так
Вот скрипт, отвечающий за историю (добавление/удаление значений в адресной строке):
function updateAnalytics(){
 $("#google_analytics").html("<script type='text/javascript'>"+
 "var _gaq = _gaq || [];"+
 "_gaq.push(['_setAccount', 'UA-39346755-1']);"+
 "_gaq.push(['_trackPageview']);"+
 "(function() {"+
  "var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;"+
  "ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';"+
  "var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);"+
 "})();</script>");
}
function _get_url_vars(string){
 var vars = [], hash;
 var hashes;
 if (string){hashes = string.slice(string.indexOf('?') + 1).split('&');}
    else {  hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');}
    for(var i = 0; i < hashes.length; i++)
    {
   if(hashes[i] == window.location){return vars;}
      else {
  hash = hashes[i].split('=');
        vars.push(hash[0]);
        vars[hash[0]] = hash[1];
   }
    }
    return vars;
}
function getUrlVar(name, string){
 if (string){return (_get_url_vars(string))[name];}
    else {return _url_vars[name];}
}
function getPrewUrlVar(name){
 return prew_url_vars[name];
}
var _url_vars = [];//тут храним массив переменных из адресной строки {имя->значение}
var prew_url_vars = [];
var tmp_prew_url_vars = [];
$(function (e) {//обновляем актуальность вышеопределенной переменной, при загрузке страницы и при нажатии по кнопочках "назад"/"вперед" в браузере
 $( window ).bind( "popstate", function( e ) {
   //var returnLocation = history.location || document.location;
   prew_url_vars = _copy_url_vars(_url_vars);
   _url_vars = _get_url_vars();
   _upd_url_vars();
   updbg();
   updateAnalytics();
    }); 
 _url_vars = _get_url_vars();
 _upd_url_vars();
 updbg();
 updateAnalytics();
});
var _urlvars_ = "?";//тут храним уже готовую строку с переменными, которую можно уже использовать
function addUrlVar(var_name, var_val){//доюавление переменной
 for (var i=0; i < _url_vars.length; i++){//если переменная с таким именем уже есть, то заменяем
  if (_url_vars[i] == var_name){delete _url_vars[_url_vars[i]]; _url_vars.splice(i, 1);}
 }
 _url_vars.push(var_name);
 _url_vars[var_name] = var_val;
 _upd_url_vars();
}
function removeUrlVar(var_name){//удаление переменной
 for (var i=0; i < _url_vars.length; i++){//удаляем уже существующую переменную с таким же именем, если она есть
  if (_url_vars[i] == var_name){delete _url_vars[_url_vars[i]]; _url_vars.splice(i, 1);}
 }
 _upd_url_vars();
}
function _copy_url_vars(o) {
 var r = [];
 for (var i=0; i < o.length; i++){
  r.push(o[i]);
     r[o[i]] = o[o[i]];
 }
 return r;
}
function updateUrl(){//обновляем историю, впихнув в нее нашу сгенерированную строку "_urlvars_"
 var sm = true;
 if (_url_vars.length == prew_url_vars.length && _url_vars.length > 0){
  for (var i=0; i < _url_vars.length && i < prew_url_vars.length; i++){
   if ((_url_vars[i] != tmp_prew_url_vars[i]) || (_url_vars[_url_vars[i]] != tmp_prew_url_vars[tmp_prew_url_vars[i]])){
    sm = false; break;
   }
  }
 }
 else {sm = false;}
 if (sm == true){return;}
 delete prew_url_vars;
 prew_url_vars = _copy_url_vars(tmp_prew_url_vars);
 tmp_prew_url_vars = _copy_url_vars(_url_vars)
 history.pushState( null, null, _urlvars_ );
 updbg();
 updateAnalytics();
}
function _upd_url_vars(){//собсно, основываясь на переменной "_url_vars", генерируем строку "_urlvars_" для вставки в историю
 _urlvars_ = "?";
 _url_vars.sort();
 for (var i=0; i < _url_vars.length; i++){
  if (i != 0){_urlvars_ = _urlvars_ + "&";}
  _urlvars_ = _urlvars_ + _url_vars[i] + "=" + _url_vars[_url_vars[i]];
 }
}

function updbg(){
 $("#debuginfo").html("");
 var test = "Prew: ";
 for (var i=0; i < prew_url_vars.length; i++){
  test += prew_url_vars[i] + "=" + prew_url_vars[prew_url_vars[i]]+", ";
 }
 addbg(test);
 test = "Cur: ";
 for (var i=0; i < _url_vars.length; i++){
  test += _url_vars[i] + "=" + _url_vars[_url_vars[i]]+", ";
 }
 addbg(test);
}

function addbg(inf){
 $("#debuginfo").append(inf+"<br>");
}
 

Как пользоваться:

addUrlVar("имя_переменной", значение );//добавляем переменную
updateUrl();//обновляем историю/адресную строку
Адресная строка станет из "example.ru" -> "example.ru?имя_переменной=значение"

removeUrlVar("имя_переменной");//удаление переменной
getUrlVar("имя_переменной", "строка");
//добываем текущее значение переменной, 
//и если второй параметр указан - переменная ищется не в адресной строке, 
//а во втором параметре (использую для поиска переменной в ссылках)
getPrewUrlVar("имя_переменной");//добываем предыдущее значение переменной
Отладочная инфа выводится в div с идентификатором "debuginfo", можете создать его где угодно с какими угодно стилями
Следующий скрипт отвечает за реестр и обработку переменных
//----------------------------------------------------------------------------com
var update_com = [];
function regUpdateCom(params, _com){
 _com.params = params;
 update_com[update_com.length] = _com;
}
var _com_at_start = true;
$(function (e) {
 $( window ).bind( "popstate", function( e ) {
   pageUpdate();
    }); 
 pageUpdate();
 $("body").on("click", "a", function(event){
  var o;
  var param_count, pv, cv, sm, param, val, pval;
  var href = $(this).attr('href').slice(0, $(this).attr('href').indexOf('?'));
  for (var i = 0; i < update_com.length && (href == window.location || href == '/'); i++){
   param_count = update_com[i].params.length;
   var res = [];
   pv = 0, cv = 0, sm = 0;
   //var test = "";
   for (var p = 0; p < param_count; p++){
    //test+="\n ";
    param = update_com[i].params[p];
    val = getUrlVar(param, $(this).attr("href"));
    //alert(param+"="+_url_vars[param]+", ");
    pval = getUrlVar(param);
    if (pval){pv++;}
    res.push(param);
    if (val){cv++; res[param] = val; addUrlVar(param, val);}else{res[param] = pval;}
    //var tsm = "is not sm";
    if (val == pval || (pval && !val)){
     sm++; 
     //tsm = "is sm";
    }
    //test+="param: "+param+", prew: "+pval+", cur: "+val+", "+tsm+", ";
   }
   if (pv == 0 && cv){
    if (update_com[i].on_open){update_com[i].on_open(res);}
    else {update_com[i].on_update(res);}
    updateUrl(); 
    //test+="\n todo: open"; alert (test); 
    o=1; continue;}
   else if (pv && cv && sm < param_count){
    update_com[i].on_update(res); 
    updateUrl(); 
    //test+="\n todo: update"; alert (test); 
    o=1; continue;}
   else if (pv && cv){
    //test+="\n todo: none"; alert (test); 
    o=1; continue;}
  }
  if(o){return false;}
 });
});
function pageUpdate(){
 addbg("<br>page update:");
 var test, tsm, param_count, pv, cv, sm, param, val, pval;
 for (var i = 0; i < update_com.length; i++){
  test = "";
  param_count = update_com[i].params.length;
  var res = [];
  pv = 0, cv = 0, sm = 0;
  for (var p = 0; p < param_count; p++){
   test+="\n ";
   param = update_com[i].params[p];
   pval = getPrewUrlVar(param);
   val = getUrlVar(param);
   if (pval){pv++;}
   if (val){cv++; res.push(param); res[param] = val;}
   tsm = "is not sm";
   if (val == pval){sm++;tsm = "is sm";}
   test+="param: "+param+", prew: "+pval+", cur: "+val+", "+tsm+", ";
  }
  //open | update
  if (pv == 0 && cv){
   if (update_com[i].on_truestart && _com_at_start){update_com[i].on_truestart(res);addbg(test+"on_truestart"+", (pv==0 && cv)");}
   else if (update_com[i].on_open){update_com[i].on_open(res);addbg(test+"on_open"+", (pv==0 && cv)");}
   else{update_com[i].on_update(res);addbg(test+"on_update"+", (pv==0 && cv)");}
  }
  //update
  else if (pv && cv && sm < param_count){update_com[i].on_update(res);addbg(test+"on_update" +", (pv && cv && sm < param_count)");}
  //close | update
  else if (pv && cv == 0){
   if (update_com[i].on_close){update_com[i].on_close();addbg(test+"on_close"+", (pv && cv == 0)");}
   else{update_com[i].on_update(res);addbg(test+"on_update"+", (pv && cv == 0)");}
  }
  //same
  else if (pv && cv && sm == param_count){
   if (update_com[i].on_same){update_com[i].on_same();addbg(test+"on_same"+", (pv && cv && sm == param_count)");}
  }
  //disabled | falsestart
  else if (pv == 0 && cv == 0){
   if (update_com[i].on_falsestart && _com_at_start){update_com[i].on_falsestart();addbg(test+"on_falsestart"+", (pv == 0 && cv == 0)");}
   else if (update_com[i].on_disabled){update_com[i].on_disabled();addbg(test+"on_disabled"+", (pv == 0 && cv == 0)");}
  }
 }
 _com_at_start = false;
}
Пример использования:
regUpdateCom(["имя_переменной"], {//регистрируем наш параметр, и с этого момента все клики по ссылках, содержащие его, будут обрабатываться заданными ниже вашими обработчиками
 //можно регистрировать более одного параметра подрят, с помощью такой записи "["имя_переменной1", "имя_переменной2"]"
 on_update:function(val){//on_update - эта функция вызывается при каждом обновлении адресной строки, будь то это переход по ссылке, или же нажатии на кнопку "назад" в браузере
  //val - массив, содержащий в себе все переменные из адресной строки и их текущие значения
  UpdateContent(val.имя_переменной);//ваша функция, и она очень хорошо умеет загружать данные через аякс :З 

                //val.имя_переменной - тут мы получаем, например, id загружаемого контента
 }, 
 on_falsestart:function(){//on_falsestart - вызывается когда пользователь только-что зашел на ваш сайт, но значения нашей переменной он не задал, тобишь она вызывается "по-дефолту"
  UpdateContent(0);
 }
});
Наши обработчики можно забиндить на следующие события:
on_open - когда предыдущее значение не назначено или равно нулю, но при этом текущее - назначено
on_update - предыдущее значение и текущее - назначены, но они разные
on_same - предыдущее значение назначено, как и текущее, при этом они еще и равны
on_close - предыдущее значение назначено, но текущее - пустое
on_falsestart - предыдущее и текущие значения отсутствуют, вызывается только при первом заходе на страницу
on_disabled - предыдущее значение и текущее - не назначены

 Для обеспечения работы всего этого добра, надо бы присоединить еще один скрипт, имитирующий HistoryAPI из HTML5, ну и плюс еще jQuery который наверняка используется большинством

Вот и все
Почти
Если кому интересно, могу рассказать как все это добро сдружить с поисковиками

Заметка: для вставки кода использую этот ресурс