пятница, 6 июля 2012 г.

Разработка приложений для Windows 8

по материалам доклада "Обзор платформы разработки для Windows 8 – Windows Runtime" (http://www.msdevcon.ru/material/1010)


На прошедшей в мае конференции окончательно прояснилась ситуация с особенностями разработки приложений под Windows 8.

Все, кто был напуган перспективой переходить на JavaScript и заново учиться разрабатывать приложения под Windows 8, могут слегка расслабиться и вздохнуть спокойно. Конечно изменения произойдут, но не настолько летальные для разработчиков, как предполагалось. Если вы пишете на C# , то новая библиотека интерфейсов программирования WinRT потребует особого внимания, но не более чем просто изучения новых неймспейсов и объектов, новых приемов разработки.


Не стоит прогибаться под изменчивый мир...

Что же все таки произошло с платформой Windows? Если смотреть на вещи глобально, то необходимо признать, что Microsoft решительно прогнулась под веб, но с хитрецой.

В Microsoft решили идти в ногу со временем (а точнее - с вебом) и прикрывшись звучными лозунгами и утверждениями о своей силе, мощи, непоколебимости, пустив огромное облако звездной пыли в глаза пользователей и разработчиков об гибкости, кросплатформенности, универсальности, комфортности использования, безопасности , провозгласили преход к новому плиточному контентоно-ориентрованному интерфейсу METRO (вот вам и окна в окнах окон :)) и разработке на HTML5+JavaScript. При этом так же осталась возможность писать приложения на всем подряд остальном, на чем только можно :), хоть на С++, хоть на С#.

Все это разношерстное безобразие разнообразие технологий разработки объединили под единым знаменем Windows Runtime.





Адская смесь Windows Runtime

Что такое Windows RT? В Microsoft преподносят новую библиотеку, как некий симбиоз подходов разработки .NET и COM.




Как можно догадаться, метаданные необходимы и используются при разработке на С#. Кроме того, в связи с переходом к новому, с точки зрения платформы, языку разработки - JavaScript, инженеры Microsoft "прикрутили" метаданные и к этому новому языку. Само ядро WinRT, по словам докладчика, базируется на нативном коде, что собственно говоря есть весьма логично и закономерно. То есть - разработка приложений на С++, по идее, должна быть наиболее эффективной в сравнении с С# и JavaScript с точки зрения производительности. 




В общем, если все действительно так гладко, как утверждает докладчик - то я лично только порадуюсь. Но, честно признаться, до конца не верится, что косяков и накладок не будет, особенно с учетом использования JavaScript.  Каким образом будут обстоять дела на самом деле - покажет время.

пятница, 8 июня 2012 г.

Взгляд изнутри на картридж RockShox Motion Control.
Оригинал с удаленной регулировкой компрессии PopLock, без флудгейта с кастомным регулятором. От обычного картриджа отличается, насколько я понимаю, упрощенным топкепом.
Все детали оригинальные, кроме регулятора.




понедельник, 14 мая 2012 г.

Парсер-анализатор химических формул. C#

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

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


Модель
    Функциональная схема может иметь следующий вид:


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

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


public enum LexemClass { undefined, element, number, separator, factor, valence, atom };

public class Lexem
{
     private string str_set;
     private LexemClass lex_class;
       
     public string Set
     {
         get { return this.str_set; }
         set { this.str_set = value; }
     }
     public LexemClass Type
     {
         get { return lex_class; }
         set { lex_class = value; }
     }
}


    Здесь LexemClass - перечисление, содержащее набор предопределенных классов лексем.
  В процессе лексического анализа выделенным из входной последовательности лексемам присваивается определенный класс, который является идентификатором типа лексемы. Тип лексемы является ключевым "свойством", в прямом и переносном смыслах этого слова, класса С# Lexem, которое в дальнейшем используется непосредственно для  преобразования набора лексем в HTML разметку.
    В рамках семантики химических формул я выделил следующие классы лексем:
  1. element - химический элемент в составе формулы;
  2. number - количество молекул вещества;
  3. separator - разделитель (круглые и квадратные скобки, ",");
  4. factor - знак умножения;
  5. valence - валентность атомов элемента;
  6. atom - количество атомов;
  7. undefined - рабочий класс по умолчанию.
    Класс Lexem определяет непосредственно лексему, как объект модели предметной области. Содержит два члена-переменных str_set и lex_class типа LexemClass, взаимно однозначно определяющих лексему и ее тип, и два свойства доступа к приватным переменным Set и Type.



public class FormulaParser
{
private string str_formula, buffer;
private char symbol;
private int i;
private LexemClass temp_defined_type;
private List<Lexem> LexemsList;

public string Formula
{...}

public List<Lexem> Lexems
{...}

public FormulaParser(string str_input)
{
str_formula = str_input;
temp_defined_type = LexemClass.undefined;
...
}

public void ParseFormula()
{
int formula_length = Formula.Length;

while(i<formula_length)
{
// Если буква
symbol = Convert.ToChar(Formula[i]);
if (char.IsLetter(symbol))
{
buffer+=symbol.ToString();
if(i!=formula_length-1)
{
if(!char.IsLetter(Convert.ToChar(Formula[i+1])))
{
Lexems.Add(new Lexem{Set=buffer, Type=LexemClass.element});
buffer=string.Empty;
}
}
else
Lexems.Add(new Lexem { Set = buffer, Type=LexemClass.element});
}

// Если цифра
else if (char.IsDigit(symbol))
{
buffer+=symbol.ToString();
if (i!=formula_length-1)
{
if (!char.IsDigit(Convert.ToChar(Formula[i + 1])) &&
    !char.Equals(Convert.ToChar(Formula[i + 1]), ','))
{
Lexems.Add(new Lexem{Set = buffer,Type =
Enum.Equals(temp_defined_type, LexemClass.undefined) ?
LexemClass.atom : (temp_defined_type)});

temp_defined_type = LexemClass.undefined;
buffer = string.Empty;
}
}
else
{
Lexems.Add(new Lexem{Set = buffer,Type =
Enum.Equals(temp_defined_type, LexemClass.undefined) ?
LexemClass.number : (temp_defined_type)});
temp_defined_type = LexemClass.undefined;
}
}

// Если ","
else if (char.Equals(symbol, ','))
{
// если предыдущая лекскма - элемент -> это разделитель
if (Lexems.Last().Type.Equals("element"))
Lexems.Add(new Lexem { Set = symbol.ToString(),
Type = LexemClass.separator });
else
buffer += symbol.ToString();
}

// Если "[" или "("
...

// Если "]" или ")"
else if (symbol.Equals(']') || symbol.Equals(')'))
...

// Если "+"
else if (symbol.Equals('+'))
{
buffer += symbol.ToString();
if (i != formula_length - 1)
{
if (!char.Equals(Convert.ToChar(Formula[i + 1]), '+'))
{
buffer = buffer.Length>1?(buffer.Length.ToString() + "+"):("+");
Lexems.Add(new Lexem { Set = buffer, Type = LexemClass.valence });
buffer = string.Empty;
}
}
}

// Если "∙"
...

// Не забываем об инкрементном счетчике
i++;
}
}
}


    Класс FormulaParser помимо свойств Formula, Lexems и членов-переменных содержит конструктор, который в качестве параметра получает объект типа String - исходную формулу для дальнейшей обработки. Словарь лексем будет содержаться во внутренней переменной LexemsList  типа List<Lexem>Обработка выполняется в методе ParseFormula().
    Принцип работы лексического анализатора заключается в последовательной проверке каждого символа входной строки с поэтапным выделением и записью в словарь лексем.
    Рассмотрим некоторые части внутренней реализации метода ParseFormula(). Итак, в методе выполняется проверка массива символов строки. Наиболее важные и емкие части кода - это проверка букв и цифр входной последовательности.
    Если i-й символ является буквой - метод начинает формировать лексему с классом element заполняя внутреннюю член-переменную buffer. Решение о занесении лексемы в коллекцию принимается на основе изменения типа i+1-го символа входной последовательности или по достижению ее конца.
    Как можно увидеть, данная реализация предусматривает выделение частей формулы в виде совокупности химических элементов, а не выделение каждого отдельного химического элемента в отдельную лексему словаря. Для достижения подобного результата можно доработать алгоритм выделения лексем опираясь на регистр символов входной строки.
     Если i-й символ является цифрой - опять заполняется переменная buffer. Если следующий символ не является цифрой или знаком "," (для дробных значений) - принимается решение о занесении значения переменной buffer в список лексем. Тип лексемы определяется в зависимости от того, каким значением инициализирована вспомогательная переменная temp_defined_type.
     Если i-й символ является символом ",". "," может быть либо разделителем между химическими элементами, либо - частью дробного числа. Здесь я использовал несколько иной подход к формированию списка лексем. Решение о занесении n-ой лексемы в список принимается на основе типа класса предыдущего n-1 элемента этого списка.
   Последняя часть кода - если символ является знаком "+". В этом случае формируется лексема с классом valence. Как и прежде, решение о занесении лексемы в коллекцию принимается на основе изменения типа i+1-го символа входной последовательности. Перед инициализацией нового элемента коллекции LexemsList в переменной buffer формируется строка, которая состоит из длинны самой строки данных и символа "+". 

public static class HTMLBuilder
{
public static string GenerateHTML(List<Lexem> LexemsList)
{
     string str_HTML="<span style=\"color:Black;font-size:11pt;\">";
            foreach (var lexem in LexemsList)
            {
                switch (lexem.Type)
                {
                    case LexemClass.atom:
                        str_HTML += "<sub>" + lexem.Set + "</sub>";
                        break;
                    case LexemClass.element:
                    case LexemClass.separator:
                    case LexemClass.number:
                    case LexemClass.factor:
                        str_HTML += lexem.Set;
                        break;
                    case LexemClass.valence:
                        str_HTML += "<sup>" + lexem.Set + "</sup>";
                        break;
                }
            }
            str_HTML += "</span></p>";
            
            return str_HTML;
        }
}

    В завершении самая простая часть работы - обработка коллекции лексем.
    Для генерации HTML-разметки можно использовать специальные классы C#, как, например, класс TagBuilder (http://msdn.microsoft.com/en-us/library/system.web.mvc.tagbuilder.aspx). В данном случае я делал разметку вручную в виду ее тривиальности.
    Статический класс HTMLBuilder имеет один метод - GenerateHTML(), который принимает в качестве аргумента коллекцию типа List<Lexem> и возвращает строку со сформированной разметкой.
   Работа метода GenerateHTML() заключается в последовательной проверке свойства Type всех элементов коллекции List<Lexem>. В зависимости от значения этого свойства для каждого элемента из списка лексем формируется свой вариант HTML-разметки.
    В данном случае для перевода атомного числа и валентности в подстрочный и соответственно надстрочный регистры используются теги HTML <sub></sub> и <sup></sup>.
Пример


четверг, 3 мая 2012 г.

Кастомный слайдер на jQuery. Часть 2.

    Продолжение...
    Итак, продолжаем эпопею со слайдером.


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


Мой вариант.
Демо тут - https://sites.google.com/site/groundfored/home/slider

jQuery скрипт
    Начнем со скрипта, который притерпел значительные изменения по сравнению с вариантом из первой части в связи с расширением функциональности слайдера.

function RefreshSlider(index) {
    $('div.ImageContainer .SliderImage').stop(true).hide();

// Останавливаем текущую анимацию контента и очищаем очередь всех последующих анимаций.
    $('div.ContentContainer .SliderContent').stop(true).hide();
    $('div.ContentContainer .SliderContent').css('top', '300px');

    // Запускаем очередь анимации изображений с определенной позиции.
    ImagesAnimation($('div.ImageContainer .SliderImage').eq(index));

    // Запускаем очередь анимации контента с определенной позиции.
    ContentAnimation($('div.ContentContainer .SliderContent').eq(index),
                             $('div.SliderMenu li').eq(index));
};


    RefreshSlider(index) - функция, которая отвечает за "обновление" контента и скроллера изображений. Она имеет единственный управляющий параметр - index (индекс пункта меню) и вызывается всякий раз, когда пользователь осуществляет навигацию по меню или стрелкам слайдера.

function ImagesAnimation(imageItem) {
    imageItem.fadeIn(300).delay(3300).fadeOut(300,function () {
        ImagesAnimation(imageItem.GetSibling());
    });
};

    Немного изменил эту функцию по сравнению с первым вариантом, который был кривовато на скорую руку написан. Теперь рекурсивный вызов в колбеке для .fadeOut().


function BottomAnimation(bottomItem) {
    bottomItem.fadeIn(100);
    bottomItem.animate({ 'left': '-=795px' }, 3000, 'easeOutBounce', function () {
        $(this).delay(3000).fadeOut(300, function () { $(this).css('left', '800px'); });
        BottomAnimation($(this));
    });
};

    BottomAnimation(bottomItem) - функция для анимации нижней "слайдер-строки". В моем исполнении этот элемент функционирует несколько иначе чем в оригинале, но мне так больше понравилось. Для цикличности анимации используется рекурсия в колбек-функции для .animate(). Хочу отметить, что я использовал здесь плагин jQuery - Easing для необходимого мне расширения характеристик поведения функции .animate() (http://gsgd.co.uk/sandbox/jquery/easing/)

$(document).ready(
    function () {
        var imageContainer = $('div.ImageContainer');
        var menuItems = $('div.SliderMenu li');
        var index = 0;
        var leftArrow = $('#LeftArrow');
        var rightArrow = $('#RightArrow');

        $('div .ImageContainer .SliderImage').hide();
        $('div.ContentContainer .SliderContent').css('top', '300px');

        BottomAnimation($('.BottomContainer .BottomContent'));
        ContentAnimation($('div.ContentContainer .SliderContent:first'), $('div.SliderMenu li:first'));
        ImagesAnimation($('div.ImageContainer .SliderImage:first'));

        leftArrow.click(function () {
            index = menuItems.filter('.selected').prev().index();
            index = (index != -1) ? index : menuItems.last().index();
            RefreshSlider(index);
        });

        rightArrow.click(function () {
            index = menuItems.filter('.selected').next().index();
            index = (index != -1) ? index : menuItems.first().index();
            RefreshSlider(index);
        });

        menuItems.click(function () {
            index = menuItems.index($(this));
            RefreshSlider(index);
        });
    });

    .ready() - функция  - также несколько изменилась. Добавились 5 переменных для удобства использования в функциях вместо селекторов. В секции "инициализации" к функциям анимации контента и изображений добавилась функция анимации нижней строки.
    За функционирование навигации по меню и стрелкам отвечают реализации в обработчиках событий .click() для соответствующих элементов разметки. Расписывать подробно пояснения к коду внутри обработчиков я не буду. Если вам что-либо не понятно - у меня для вас печальные новости... :) Отмечу лишь, что в конце каждой функции вызывается RefreshSlider(index), о которой было сказано выше.

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

<div id="Slider">
    <div class="SliderBody">
...

    Я использовал стандартную блочную верстку и все обернул в div-ы. Div с классом "SliderBody" - обертка для всего функционального содержимого.

...
<div class="SliderArrow" id="LeftArrow"><p><</p></div>
<div class="SliderArrow" id="RightArrow"><p>></p></div>
<div id="CenterSection">
...

    Далее идут стрелки навигации. Они выполнены просто в виде символов "<" и ">". Правильнее было бы их нарисовать, но, как я писал прежде в первой части, мне в принципе не понятна функциональная необходимость их наличия, поэтому я не стал заморачиваться по этому поводу.
   Следует отметить, что при таком подходе к реализации кнопок потребуется уделить больше внимания страничной разметке и CSS, по сравнению с вариантом, если бы кнопки были рисоваными.
    На данном этапе по моему замыслу тело слайдера конструктивно делится на три части: два блока стрелок и центральная часть между ними с айди "CenterSection".

...
<div class="ContentContainer">
<div class="SliderContent" title="Первая панель">
<h1>...</h1>
<p>...</p>
<a href="..."></a>
</div>

<div class="SliderContent" title="Вторая панель">
...
</div>
...
</div>
...

    Далее по коду - блок с классом "ContentContainer", который содержит в себе наиболее информативную для пользователей составляющую наполнения слайдера - заголовки <h1>, текстовые блоки-абзацы <p> и ссылки <a>. Все перечисленные элементы разметки сгруппированы и размещены в блоках с классом "SliderContent".
    Блок контента является одной из четырех составляющих вышеупомянутого центрального блока слайдера (блока с айди "CenterSection"), другими словами - тело центрального блока конструктивно я разделил на четыре части, о которых будет сказано далее.

...
<div class="ImageContainer">
<div class="SliderImage">
<img src="..." />
</div>

       <div class="SliderImage">
...
</div>
...
</div>
...

    Блок с классом "ImageContainer" - это блок изображений справа. Второй внутренний блок центральной секции слайдера. Тут все просто - конструктивно состоит из div-ов класса "SliderImage", которые содержат элементы <img>.

...
<div class="SliderMenu">
<h2>...</h2>
       <ul>
<li>...</li>
...
       </ul>
</div>
...

   Далее идет третья составляющая часть центрального блока - блок меню "SliderMenu". Внутри содержится заголовок для меню <h2> и элемент списка - <ul>, в котором формируется контекст меню из центральной части слайдера.

...
<div id="BottomLine">
<div class="BottomContainer">
<span>Engineering & Development: </span>
<span class="BottomContent">...<a href="..."> </a></span>
</div>
</div>
...

    Четвертый и последний элемент центрального блока слайдера - "слайдер-строка" о которой я упоминал в первом посте. Возможно можно было обойтись меньшим количеством div-ов, но в данном случае, как видно, число div-ов не критично. Создавая этот элемент, как и в случае со стрелками навигации, я особо не старался, просто сделал, чтоб слайдер максимально соответствовал оригиналу.
    В блоке с классом "BottomContainer" содержатся элементы <span> и <a>.

...
</div>
</div>
</div>


    Ну и в завершении - не забыть закрыть все открытые div-ы.
    Конечно без CSS-стилей "картина" выглядит не полной, но это пост вышел слишком объемным. Возможно CSS отдельно распишу позже.