Показаны сообщения с ярлыком HTML. Показать все сообщения
Показаны сообщения с ярлыком HTML. Показать все сообщения

Динамическая загрузка скриптов

понедельник, 27 апреля 2009 г.,

Например, на веб-сайте в папке Script есть файл JScript.js:
function Test()
{
    alert("TEST");
}
В какой-то момент времени требуется из Silverlight вызвать загрузку .js-файла и выполнение функции Test. При этом загрузка файла должна происходить после каждого его изменения. Это можно сделать следующим образом в Silverlight:
ScriptHelper.Invoke("/Scripts/JScript.js?guid=" + Guid.NewGuid(), () =>
{
    HtmlPage.Window.Invoke("Test");
});

В метод Invoke передается путь к файлу. Guid используется для того, чтобы файл не кешировался браузером.
Принцип работы метода: создается тег SCRIPT; добавляется на веб-страницу в тег HEAD; происходит перехват события onreadystatechanged и в его обработчике проверяется состояние SCRIPT.readyState; если loaded, то вызвать метод в Silverlight.
Ниже код класса с extension method'ами:
public static class ScriptHelper
{
    public static HtmlElement FindByUri(string uri)
    {
        foreach (var script in HtmlPage.Document.GetElementsByTagName("script"))
        {
            if (string.Equals((string)script.GetProperty("src"), uri, StringComparison.OrdinalIgnoreCase))
                return (HtmlElement)script;
        }
        return null;
    }

    public static ScriptReadyState ReadyState(this HtmlElement scriptTag)
    {
        return (ScriptReadyState)Enum.Parse(typeof(ScriptReadyState), (string)scriptTag.GetProperty("readyState"), true);
    }

    public enum ScriptReadyState
    {
        Uninitialized = 0,
        Loading = 1,
        Loaded = 2,
        Interactive = 3,
        Complete = 4
    }

    public static void OnLoaded(this HtmlElement scriptTag, Action callback)
    {
        if ((int)scriptTag.ReadyState() > 1)
        {
            callback();
        }
        else
        {
            // обработчик событий
            EventHandler<HtmlEventArgs> eh = (s, e) =>
            {
                // проверить событие и состояние и вызвать handler
                if (e.EventType == "readystatechange" && (int)scriptTag.ReadyState() > 1)
                    callback();
            };

            // подключить обработчик
            scriptTag.AttachEvent("onreadystatechange", eh);
        }
    }

    // handler - в IE вызовется раньше, чем скрипт будет выполнен;
    public static void Invoke(string scriptUri, Action callback)
    {
        HtmlElement script = FindByUri(scriptUri);
        if (script == null)
        {
            // создать тег и добавить на страницу в тег HEAD;
            script = Create(scriptUri);
            var head = (HtmlElement)HtmlPage.Document.GetElementsByTagName("head").FirstOrDefault();
            head.AppendChild(script);
        }

        script.OnLoaded(callback);
    }

    public static HtmlElement Create(string scriptUri)
    {
        // создать тег скрипта
        var js = HtmlPage.Document.CreateElement("script");
        js.SetProperty("type", ScriptType);
        js.SetProperty("src", scriptUri);
        return js;
    }

    public static string ScriptType = "text/javascript";
}

Если js-функции не существует, то получим ошибку ... см. здесь.
Код работает в IE.

Парсинг HTML'а

понедельник, 20 апреля 2009 г.,

Парсить html, конечно, можно с помощью регулярных выражений (Regex), но ... например, требуется получить содержимое всех тегов, у которых атрибут id начинается с подчеркивания или заканчивается цифрой. При этом веб-сервер возвращает невалидный html, который постоянно меняется:
string html = @"Hello,
      <b id='_name'>World</b>!
      </i>
      <i id='_name'>Value1</i> Value2
      </i>
      <i id='id3'>Value3</i><i id='val'>Value4</i>
      </b>";
В такой ситуации использование регулярных выражений практически невозможно.
Решение: загрузить html в HtmlDocument.
public static IHTMLDocument2 Parse(string html)
{
    HTMLDocumentClass doc = new mshtml.HTMLDocumentClass();
    (doc as IHTMLDocument2).write("");  // чтобы создать body и ...
    MshtmlMarkupServices srv = new MshtmlMarkupServices(doc as IMarkupServicesRaw);
    var res = srv.ParseString(html);
    return res.Document;
}
С его помощью поставленная выше задача решается достаточно просто:
IHTMLDocument2 doc = Parse(html);
var linq = from tag in (doc.body.children as IHTMLElementCollection).Cast<IHTMLElement>()
           where tag.id != null && (tag.id.StartsWith("_") || char.IsDigit(tag.id[tag.id.Length-1]))
           select tag;

foreach (IHTMLElement tag in linq)
    System.Diagnostics.Trace.WriteLine(tag.innerHTML);
Для компиляции к проекту надо подключить сборки, которые входят в состав Microsoft Live Writer:
1) C:\Program Files\Windows Live\Writer\WindowsLive.Writer.Mshtml.dll;
2) C:\Program Files\Windows Live\Writer\WindowsLive.Writer.Interop.Mshtml.dll

Сборка C:\Program Files\Microsoft.NET\Primary Interop Assemblies\Microsoft.mshtml.dll не используется, потому что метод IMarkupServices.ParseString импортирован таким образом, что лишает возможности передать в него строку.

DOCTYPE обязателен

вторник, 31 марта 2009 г.,

DOCTYPE определяет тип (X)HTML-документа. Необходимо указать в первой строке, иначе, если DOCTYPE не указан или указан некорректно, браузер переключится в quirk mode - режим обратной совместимости с прежними браузерами. В результате разные браузеры будут отображать документ по-разному.
Возможные типы DOCTYPE указаны в статье: "Recommended DTDs to use in your Web document" и в параграфе "HTML version information".