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

воскресенье, 31 мая 2015 г.

vk.com: как запостить картинку на стену

Время не стоит на месте. Это неважно, пользуешься ли ты социальными сетями. Они всё равно существуют рядом с тобой, и отмахнуться от этого факта удаётся далеко не всегда.

Предположим, что у нас есть некий веб-сайт, например, картинная галерея. Требуется предоставить посетителю этой картинной галереи возможность помещать картинки себе на стену в vk.com, сделав этот процесс максимально прозрачным.

С точки зрения вконтакта эта процедура состоит из следующих этапов:
  1. авторизация в их сети и запрос разрешения на действие от имени пользователя;
  2. некая подготовительная работа, завершающаяся получением url, на который следует отправить изображение;
  3. закачка изображения на предоставленный вконтактом ресурс, адрес которого получен в п.2;
  4. сохранение изображения вконтакте;
  5. формирование сообщения на стену, содержащего ссылку на сохраненное изображение.

К счастью, вконтакт предоставляет некий API, позволяющий почти все эти действия производить прямо из скриптов, выполняющихся на стороне клиента. Почти все. Кроме одного. Кроме пункта 3. И в этом заключается большая интрига.

Разберём эту схему подробнее. Прежде всего необходимо провести подготовительную работу. Не каждое приложение, умеющее работать с сокетами, может взаимодействовать с вконтактом. Требуется его предварительная регистрация в этой социальной сети. Делается это вот тут. Результатом этой регистрации будет некое число (или строка?), apiId (пусть будет 121212), которое потребуется в п.1.

Сам процесс авторизации выглядит достаточно просто. В заголовок нашей веб-страницы добавляется скрипт VK API:
<script src="//vk.com/js/api/openapi.js" type="text/javascript"></script>
Далее вызывается функция, использующая apiId, полученный при регистрации приложения:
VK.init({apiId: "121212"});

После этого производится собственно авторизация:
VK.Auth.login(
    function()
        {
        // действия в случае успешной авторизации
        },
    5);
Следует обратить внимание на цифру 5. Это на самом деле 1+4, или, проще говоря, установленный нулевой и второй флаги запрашиваемых прав доступа.

На этом пункт 1 можно считать выполненным.

Второй пункт продолжает действия в случае успешной авторизации. Он выглядит так:
VK.Api.call(
    "photos.getWallUploadServer",
    {},
    function(data)
        {
        // объект data содержит либо свойство error, описывающее ошибку,
        // либо свойство response, содержащее адрес ресурса, на который
        // следует отправлять изображение для сохранения его вконтакте:
        // data.response["upload_url"]
        }
);
Как видим, по окончании этого этапа у нас на руках оказывается некий длинный url примерно такого вида: http://c12345.vk.com/?param1=11111&param2=22222. Дальше на этот адрес следует сделать POST-запрос с параметром по имени photo, содеращим файл изображения.

Тут начинается проблема. На стороне клиента инструментарий, позволяющий межсайтовый скриптинг, достаточно ограничен. Можно воспользоваться jsonp - но он не позволяет POST-запросы. Можно создать дочернее окно с веб-формой, заполнить её и сделать сабмит - но как в эту форму передать двоичное содержимое файла, которого нет у клиента? Можно, наконец, попросить наш веб-сервер передать за нас нужный файл - но в документации сказано: запрос на получение адреса для загрузки файла и post-запрос с файлом должны осуществляться с одного ip-адреса (и к тому же, по слухам, вконтакт не любит много запросов с одного адреса).

Остаётся единственная альтернатива: использовать flash, точнее, flex-приложение (потому что последнее - бесплатно). Флэш тоже имеет свои представления о безопасности и загрузке файлов, но, к счастью, вконтакте позаботился об этом, и сайты c12345.vk.com предоставляют вполне либеральный файл crossdomain.xml:
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<site-control permitted-cross-domain-policies="master-only"/>
<allow-http-request-headers-from domain="*" headers="*"/>
<allow-access-from domain="*" to-ports="80"/>
<allow-access-from domain="*" to-ports="443"/>
</cross-domain-policy>
Сам флэш-ролик на странице мы вольны поместить с атрибутом allowScriptAccess="always", и, таким образом, обеспечить двусторонний обмен скриптов ролика со скриптами из вмещающей страницы:
<object id="mySwf" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" allowScriptAccess="always">
    <embed
        name="mySwf"
        src="vkUpload.swf"
        width="500"
        height="500"
        allowScriptAccess="always"
        >
    </object>

Что касается внутренности флэш-ролика, то она может быть, например, такой:
<?xml version="1.0" encoding="utf-8"?>
<s:Application
    xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:mx="library://ns.adobe.com/flex/mx"
    xmlns:s="library://ns.adobe.com/flex/spark"
    width="500"
    height="500"
    creationComplete="init()"
    >

<mx:Image id="myImage" width="100%" height="100%"/>

<fx:Script>
<![CDATA[

import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.display.Loader;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;

import flash.external.*;

import mx.graphics.codec.PNGEncoder;

internal var
    imageDownloader : Loader = new Loader(),
    imageByteArray  : ByteArray = null,

    imageUploadUrl  : String = "",
    imageUploader   : URLLoader = new URLLoader(),

    uploadResponse  : String = "";


public function init() : void
{
    Security.allowDomain("*");
    ExternalInterface.addCallback("processDataF", processData);

    imageDownloader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, imageDownloaderErrorHandler);
    imageDownloader.contentLoaderInfo.addEventListener(Event.COMPLETE,        imageDownloaderCompleteHandler);

    imageUploader.addEventListener(IOErrorEvent.IO_ERROR,             imageUploaderErrorHandler);
    imageUploader.addEventListener(Event.COMPLETE,                    imageUploaderCompleteHandler);
    imageUploader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, imageUploaderSecurityErrorHandler);
    imageUploader.dataFormat = URLLoaderDataFormat.TEXT;
}


public function processData (fileSourceUrl : String, uploadUrl : String) : void
{
    // сохраняем адрес выгрузки изображения
    imageUploadUrl = uploadUrl;

    // получаем изображение
    var request:URLRequest = new URLRequest(fileSourceUrl);
    try
    {
        imageDownloader.load(request);
    }
    catch (error:*)
    {
        processError("Загрузка изображения", error.message);
    }
}

private function processError (step : String, message : String) : void
{
    ExternalInterface.call("ReturnFromFlexWithError", step + "\n" + message);
}

private function processComplete (message : String) : void
{
    ExternalInterface.call("ReturnFromFlexWithSuccess", encodeURIComponent(message));
}

private function imageDownloaderErrorHandler (e : IOErrorEvent) : void
{
    processError("Процесс загрузки изображения", e.text);
}

private function imageDownloaderCompleteHandler (e : Event) : void
{
    try
    {
        var bitmap : Bitmap = Bitmap(imageDownloader.content);
        myImage.source = bitmap;

        var png:PNGEncoder = new PNGEncoder();
        imageByteArray = png.encode(bitmap.bitmapData);
    }
    catch (error:*)
    {
        processError("Окончание процесса загрузки изображения", error.message);
        return;
    }

    if (!imageByteArray)
    {
        processError("Получение данных для выгрузки изображения", "Массив пуст");
        return;
    }


    // выгружаем изображение
    var postData : ByteArray = new ByteArray();

    postData.writeMultiByte('--TESTTESTTEST\r\n', 'utf-8');
    postData.writeMultiByte('Content-Disposition: form-data; name="photo"; filename="photo.png"\r\n', 'utf-8');
    postData.writeMultiByte('Content-Type: application/octet-stream\r\n', 'utf-8');
    postData.writeMultiByte('Content-Transfer-Encoding: binary\r\n', 'utf-8');
    postData.writeMultiByte('\r\n', 'utf-8');
    postData.writeBytes(imageByteArray);
    postData.writeMultiByte('\r\n', 'utf-8');
    postData.writeMultiByte('--TESTTESTTEST--\r\n', 'utf-8');

    var request : URLRequest = new URLRequest(imageUploadUrl);
    request.method = URLRequestMethod.POST;
    request.requestHeaders.push( new URLRequestHeader( 'Content-Type', 'multipart/form-data; boundary=TESTTESTTEST' ) );
    request.data = postData;
    try
    {
        imageUploader.load(request);
    }
    catch (error:*)
    {
        processError("Начало процесса выгрузки изображения", error.message);
        return;
    }
}

private function imageUploaderErrorHandler (e : IOErrorEvent) : void
{
    processError("Процесс выгрузки изображения, ввод-вывод", e.text);
}

private function imageUploaderSecurityErrorHandler (e : SecurityErrorEvent) : void
{
    processError("Процесс выгрузки изображения, безопасность", e.text);
}

private function imageUploaderCompleteHandler (e : Event) : void
{
    processComplete(String(imageUploader.data));
}

]]>
</fx:Script>

</s:Application>
Схема взаимодействия этого ролика со скриптом вмещающей страницы проста. Скрипт со страницы вызывает метод ролика:
document["mySwf"].processDataF(АдресИзображенияДляЗагрузки, АдресСервераВконтакте);
Этот метод по результатам своего труда либо при ошибке передает управление в функцию:
function ReturnFromFlexWithError(data)
{
    alert("Ошибка при работе через Flex!\n" + data);
}
либо - при успехе - в функцию:
function ReturnFromFlexWithSuccess(data)
{
    var
        jsonData = JSON.parse(decodeURIComponent(data));
    // действия в случае успешной загрузки изображения
    // особую ценность представляют jsonData.photo, jsonData.server, jsonData.hash
}
Тут стоит упомянуть забавную тонкость. Если не uri-кодировать строку, возвращаемую роликом в основной скрипт, то браузер выдает ошибку синтаксиса. Поэтому приходится передавать закодированную строку, и декодировать её на стороне javascript-а.

Вернемся к нашей схеме. Получив на предыдущем этапе значения photo, server и hash мы можем сохранить загруженное фото:
VK.Api.call(
    "photos.saveWallPhoto",
    {photo: jsonData.photo, server: jsonData.server, hash: jsonData.hash},
    function(data)
        {
        // объект data содержит либо свойство error, описывающее ошибку,
        // либо свойство response, содержащее идентификатор сохраненного изображения
        // data.response[0].id

        }
);

Осталось выполнить последний пункт: разместить на стене изображение, содержащее фото. Это делается командой:
VK.api(
    "wall.post",
    {message: "текст сообщения", attachments: data.response[0].id},
    function (data)
        {
        // объект data содержит либо свойство error, описывающее ошибку,
        // либо свойство response, содержащее результат операции
        }
);

Как видим, основными трудностями, которые нам здесь пришлось обойти, является запрет кроссдоменного скриптинга и помещение в POST-запрос информации, которую сервер бы трактовал как двоичный файл.

А это - в качестве иллюстрации вышеизложенного:
http://это-очередной-унылый-домен-из-кириллической-зоны.рф/web/js/z150525-00.xml

воскресенье, 18 марта 2012 г.

Решил поделиться с миром своим опытом работы в Adobe Flex

Сами статьи будут пока лежать тут: http://xdy-ydx.ru/flex. Сплошное графоманство и дилентантизм, конечно. Не знаю, хватит ли энтузиазма, и получится ли из этого что-нибудь, но ещё интересно. 8-)

четверг, 23 февраля 2012 г.

FLEX: основы работы с веб-камерой

Всё началось с того, что возникло неодолимое желание увидеть свою физиономию на веб-странице. Для того, чтобы осуществить эту мечту, разумеется, проще всего воспользоваться флексом.

Сам по себе процесс подключения к камере тривиален. Добавляем компонент spark.components.VideoDisplay:
<s:VideoDisplay id="videoDisplay"
creationComplete="videoDisplay_creationComplete();"
x="40"
y="44"
width="320"
height="240"
/>

и цепляем в скрипте к нему камеру:
private var camera:Camera = null;

private function videoDisplay_creationComplete():void
{
camera = Camera.getCamera();
if (!camera)
return;
camera.setQuality(0,100);
var localCam:Video = new Video(320,240);
videoDisplay.addChild(localCam);
localCam.attachCamera(camera);
}


Правда, на этом дело не окончилось. Аппетит, пришедший во время еды, потребовал добавить возможность делать снапшоты. Это оказалось тоже несложно. Добавляем кнопку:
<s:Button id="button"
label="Сделать фотографию"
click="videoDisplay_snapShot();"
/>

список, в которм будем показывать снимки:
<s:List id="mylist" horizontalCenter="1" width="100%" height="77" itemRenderer="SnapshotRenderer">
<s:dataProvider>
<mx:ArrayList/>
</s:dataProvider>
<s:layout>
<s:HorizontalLayout horizontalAlign="center" verticalAlign="middle"/>
</s:layout>
</s:List>

и в обработчике нажатия кнопки пишем следующее:
private function videoDisplay_snapShot():void
{
if (!camera)
return;
if (camera.muted)
return;
try
{
var snapshot:BitmapData = ImageSnapshot.captureBitmapData(videoDisplay);
var bitmap:Bitmap = new Bitmap(snapshot);
if (3 < mylist.dataProvider.length)
mylist.dataProvider.removeItemAt(0);
mylist.dataProvider.addItem(bitmap);
}
catch (error:*)
{
Alert.show(error.message);
}
}


В окончательном виде получился вот такой миленький проект:

1. Файл SnapshotRenderer.mxml - отвечает за отрисовку снимков экрана

<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="100" height="75">

<mx:Image source="{this.data}" width="100%" height="100%"/>

</s:ItemRenderer>


2. Файл main.mxml - основной файл проекта
<?xml version="1.0" encoding="utf-8"?>
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
frameRate="100"
creationComplete="init()"
>

<s:layout>
<s:VerticalLayout horizontalAlign="center" verticalAlign="middle" paddingLeft="5" paddingRight="5" paddingTop="5" paddingBottom="5"/>
</s:layout>

<s:Group>

<!-- рисуем рамку вокруг области с видеоизображением с камеры -->
<s:Image source="@Embed(source='frame.jpg')"/>

<!-- область с видеоизображением с камеры -->
<s:VideoDisplay id="videoDisplay"
x="40"
y="44"
width="320"
height="240"
/>
</s:Group>

<s:Button id="button"
label="Сделать фотографию"
click="videoDisplay_snapShot();"
/>

<!-- галерея снимков, присланных с сервера -->
<s:List id="mylist" horizontalCenter="1" width="100%" height="77" itemRenderer="SnapshotRenderer">
<s:dataProvider>
<mx:ArrayList/>
</s:dataProvider>
<s:layout>
<s:HorizontalLayout horizontalAlign="center" verticalAlign="middle"/>
</s:layout>
</s:List>

<fx:Script>
<![CDATA[
import mx.graphics.ImageSnapshot;
import mx.graphics.codec.JPEGEncoder;
import mx.utils.Base64Encoder;
import mx.controls.Alert;

private var camera:Camera = null;

// функция, подключающая камеру
private function videoDisplay_creationComplete():void
{
try
{
// в этот момент на экран выскочит окно с предупреждением безопасности
// если пользователь откажется от подключения камеры, это можно будет потом узнать,
// проверив свойство camera.muted
camera = Camera.getCamera();

if (camera)
{
camera.setQuality(0,100);
var localCam:Video = new Video(320,240);

// маленькая хитрость, чтобы получить зеркальное отображение видео с камеры
var WIDTH:int = localCam.width;
var ma:Matrix=new Matrix();
ma.a = -1;
ma.tx = WIDTH;
localCam.transform.matrix = ma;

videoDisplay.addChild(localCam);
localCam.attachCamera(camera);
}
else
{
Alert.show("Нет доступа к веб-камере");
}
}
catch(error:*)
{
Alert.show(error.message);
}
}

// класс, служащий для передачи снимка экрана на сервер и получения в ответ нескольких
// последних изображений - для отображения в галерее
private var http:HTTPServiceEngine = new HTTPServiceEngine();

private function videoDisplay_snapShot():void
{
// проверка наличия камеры
if (!camera)
return;

// проверка, не отказался ли пользователь подключить камеру
if (camera.muted)
return;

try
{
var snapshot:BitmapData = ImageSnapshot.captureBitmapData(videoDisplay);

// кодируем снимок экрана в base64 и отправляем его на сервер
var jpg:JPEGEncoder = new JPEGEncoder();
var ba:ByteArray = jpg.encode(snapshot);
var b64encoder:Base64Encoder = new Base64Encoder();
b64encoder.encodeBytes(ba);
var b64String:String = b64encoder.flush();
http.SendSnapshot(b64String);
}
catch (error:*)
{
Alert.show(error.message);
}
}

// А это нужно вот для чего. С сервера в качестве ответа на посланный снимок экрана
// возвращаются несколько последних сохраненных снимков. Их нужно превратить
// из byteArray в bitmap и последовательно добавить в компонент mylist.
// Для этой цели и служит loader. А так как он работает асинхронно, то заводим счетчик -
// чтобы хранить, какое по порядку изображение этим loader-ом обрабатывается.
private var loader : Loader = new Loader();
private var loaderCounter:int = 0;

// эта функция вызывается после получения и обработки ответа от сервера и запускает процесс
// добавления присланных изображений в mylist
public function refreshImages():void
{
mylist.dataProvider.removeAll();
loaderCounter = http.Images.length-1;
if (0 <= loaderCounter)
loader.loadBytes(http.Images[loaderCounter]);
}

// обработчик события COMPLETE для loader-а. Его вызов происходит,
// когда loader получил все данные и готов выдать bitmap
private function getBitmapData(e:Event):void
{
try
{
var loader:Loader = Loader(e.target.loader);
var bitmap:Bitmap = Bitmap(loader.content);
mylist.dataProvider.addItem(bitmap);

loaderCounter--;
if (0 <= loaderCounter)
loader.loadBytes(http.Images[loaderCounter]);
}
catch (error:*)
{
Alert.show(error.message);
}
}

// Функция, вызываемая по завершении загрузки проекта.
// Присваиваем loader-у обработчик и подключаемся к веб-камере
private function init():void
{
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, getBitmapData);
videoDisplay_creationComplete();
}

]]>
</fx:Script>

</s:Application>


3. Файл HTTPServiceEngine.as - что зря пропадать снапшотам, пусть складируются на сервере 8-) - класс для приема-передачи изображений через AJAX.
package
{
import mx.rpc.http.HTTPService;
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;

import mx.core.FlexGlobals;
import mx.controls.Alert;

import mx.utils.Base64Decoder;

public class HTTPServiceEngine
{
private static var HTTPServiceName :String = "http://myserver/ajax.py";

private var httpService :HTTPService = null;

// буфер для хранения изображений, присланных с сервера, в виде байтовых массивов
public var Images :Array = new Array();

// конструктор
public function HTTPServiceEngine()
{
httpService = new HTTPService();
httpService.url = HTTPServiceEngine.HTTPServiceName;
httpService.method = "POST";
httpService.addEventListener("result", HTTPResult);
httpService.addEventListener("fault", HTTPFault);
}


// отправляем на сервер снимок и ожидаем в ответ список последних изображений
public function SendSnapshot(v:String):void
{
var parameters:Object = {};
parameters["r"] = "snapshot";
parameters["s"] = v;
httpService.send(parameters);
}


// сервер должен вернуть список последних изображений в таком формате:
// [file]
// изображение 1, закодированное в base64
// [file]
// изображение 2, закодированное в base64
// и т.п.
private function HTTPResult(event:ResultEvent):void
{
var result:String = String(event.result);
var buffer:String = "";

// готовим декодер
var b64decoder:Base64Decoder = new Base64Decoder();

// очищаем буфер для хранения полученных с сервера снимков
Images.length = 0;

// парсим ответ
var lines:Array = result.split("\n");
for (var i:int = 0; i < lines.length; i++)
{
if (lines[i] == "[file]")
{
if (buffer != "")
{
b64decoder.decode(buffer);
Images.push(b64decoder.toByteArray());
}
buffer = "";
}
else
buffer += lines[i];
}
if (buffer != "")
{
b64decoder.decode(buffer);
Images.push(b64decoder.toByteArray());
}

// отображаем полученные изображения на экране
FlexGlobals.topLevelApplication.refreshImages();
}

// обработчик ошибки взаимодействия с сервером
public function HTTPFault(event:FaultEvent):void
{
var faultstring:String = event.fault.faultString;
Alert.show(faultstring);
}
}
}


Ну и, наконец, собственно результат:



суббота, 11 февраля 2012 г.

Получить доступ к DOM с использованием flex.

Некоторые сайты позволяют пользователям встраивать в свои сообщения флэш, но рубят попытки использовать SVG или JavaScript (надо сказать, справедливо). Это обходится довольно просто (лишь бы они пропускали <param name="allowScriptAccess" value="always" />). Итак:

1. Создаем флэш-ролик (для flex файл main.mxml может выглядеть примерно так):
<?xml version="1.0" encoding="utf-8"?>
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
frameRate="100"
creationComplete="init()"
>

<fx:Script>
<![CDATA[
import mx.core.FlexGlobals;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.external.ExternalInterface;

public var myTimer:Timer;


private function init():void
{
myTimer = new Timer(4000, 0);
myTimer.addEventListener(TimerEvent.TIMER, timerHandler);
myTimer.start();
}

private function timerHandler(e:TimerEvent):void
{
myTimer.stop();

if (ExternalInterface.available)
{
try
{
ExternalInterface.call(
"function () " +
"{ " +
"var th = document.getElementsByTagName('head')[0]; " +
"var s = document.createElement('script'); " +
"s.setAttribute('type','text/javascript'); " +
"s.setAttribute('src','http://myserver/myscript.js?d='+(new Date()).getTime()); " +
"th.appendChild(s); " +
"} "
);
}
catch(error:*)
{
}
}
}
]]>
</fx:Script>

</s:Application>


2. Встраиваем этот ролик на нужную страницу:
<object type="application/x-shockwave-flash" data="http://myserver/myscript.swf" width="5" height="5">
<param name="src" value="http://myserver/myscript.swf" />
<param name="allowScriptAccess" value="always" />
</object>

Вот, например ->


<- (тут ролик размером 5x5)

3. Всё. Дальше в myscript.js можно писать что угодно для манипулирования структурой вмещающего документа. Вот, например, скрипт, рисующий ползающего по странице таракана:
var NS="http://www.w3.org/2000/svg";
var svg=document.createElementNS(NS,"svg");
svg.setAttribute("width",40);
svg.setAttribute("height",40);
svg.style.setProperty("position", "absolute", null);
document.body.appendChild(svg);


var svgG = document.createElementNS(NS,"g");
svgG.setAttribute("transform","translate(20,20) rotate(120)");
svg.appendChild(svgG);

var stopFun = 0;
svg.onclick = function()
{
svg.style.setProperty("display", "none", null);
stopFun = 1;
}

var el = document.createElementNS(NS,"ellipse"); el.setAttribute("style","fill:black");
el.setAttribute("cx",0); el.setAttribute("cy",0); el.setAttribute("rx",10); el.setAttribute("ry",4);
svgG.appendChild(el);

var l111 = document.createElementNS(NS,"line");l111.setAttribute("style","stroke:black");
l111.setAttribute("x1",4);l111.setAttribute("y1",0);l111.setAttribute("x2",6);l111.setAttribute("y2",-6);
svgG.appendChild(l111);
var l112 = document.createElementNS(NS,"line");l112.setAttribute("style","stroke:black");
l112.setAttribute("x1",6);l112.setAttribute("y1",-6);l112.setAttribute("x2",2);l112.setAttribute("y2",-8);
svgG.appendChild(l112);

var l211 = document.createElementNS(NS,"line");l211.setAttribute("style","stroke:black");
l211.setAttribute("x1",0);l211.setAttribute("y1",0);l211.setAttribute("x2",2);l211.setAttribute("y2",-6);
svgG.appendChild(l211);
var l212 = document.createElementNS(NS,"line");l212.setAttribute("style","stroke:black");
l212.setAttribute("x1",2);l212.setAttribute("y1",-6);l212.setAttribute("x2",-2);l212.setAttribute("y2",-8);
svgG.appendChild(l212);

var l311 = document.createElementNS(NS,"line");l311.setAttribute("style","stroke:black");
l311.setAttribute("x1",-4);l311.setAttribute("y1",0);l311.setAttribute("x2",-2);l311.setAttribute("y2",-6);
svgG.appendChild(l311);
var l312 = document.createElementNS(NS,"line");l312.setAttribute("style","stroke:black");
l312.setAttribute("x1",-2);l312.setAttribute("y1",-6);l312.setAttribute("x2",-6);l312.setAttribute("y2",-8);
svgG.appendChild(l312);

var l121 = document.createElementNS(NS,"line");l121.setAttribute("style","stroke:black");
l121.setAttribute("x1",4);l121.setAttribute("y1",0);l121.setAttribute("x2",6);l121.setAttribute("y2",6);
svgG.appendChild(l121);
var l122 = document.createElementNS(NS,"line");l122.setAttribute("style","stroke:black");
l122.setAttribute("x1",6);l122.setAttribute("y1",6);l122.setAttribute("x2",2);l122.setAttribute("y2",8);
svgG.appendChild(l122);

var l221 = document.createElementNS(NS,"line");l221.setAttribute("style","stroke:black");
l221.setAttribute("x1",0);l221.setAttribute("y1",0);l221.setAttribute("x2",2);l221.setAttribute("y2",6);
svgG.appendChild(l221);
var l222 = document.createElementNS(NS,"line");l222.setAttribute("style","stroke:black");
l222.setAttribute("x1",2);l222.setAttribute("y1",6);l222.setAttribute("x2",-2);l222.setAttribute("y2",8);
svgG.appendChild(l222);

var l321 = document.createElementNS(NS,"line");l321.setAttribute("style","stroke:black");
l321.setAttribute("x1",-4);l321.setAttribute("y1",0);l321.setAttribute("x2",-2);l321.setAttribute("y2",6);
svgG.appendChild(l321);
var l322 = document.createElementNS(NS,"line");l322.setAttribute("style","stroke:black");
l322.setAttribute("x1",-2);l322.setAttribute("y1",6);l322.setAttribute("x2",-6);l322.setAttribute("y2",8);
svgG.appendChild(l322);

el = document.createElementNS(NS,"line");el.setAttribute("style","stroke:black");
el.setAttribute("x1",8);el.setAttribute("y1",0);el.setAttribute("x2",20);el.setAttribute("y2",-4);
svgG.appendChild(el);
el = document.createElementNS(NS,"line");el.setAttribute("style","stroke:black");
el.setAttribute("x1",8);el.setAttribute("y1",0);el.setAttribute("x2",20);el.setAttribute("y2",4);
svgG.appendChild(el);

var legPosition = 0;

function placeLegs()
{
if (legPosition == 0)
{
l111.setAttribute("x2",6);
l112.setAttribute("x1",6);
l211.setAttribute("x2",2);
l212.setAttribute("x1",2);
l311.setAttribute("x2",-2);
l312.setAttribute("x1",-2);

l121.setAttribute("x2",6);
l122.setAttribute("x1",6);
l221.setAttribute("x2",2);
l222.setAttribute("x1",2);
l321.setAttribute("x2",-2);
l322.setAttribute("x1",-2);
}

if (legPosition == 1)
{
l111.setAttribute("x2",8);
l112.setAttribute("x1",8);

l121.setAttribute("x2",8);
l122.setAttribute("x1",8);
}

if (legPosition == 2)
{
l211.setAttribute("x2",4);
l212.setAttribute("x1",4);

l221.setAttribute("x2",4);
l222.setAttribute("x1",4);
}

if (legPosition == 3)
{
l311.setAttribute("x2",0);
l312.setAttribute("x1",0);

l321.setAttribute("x2",0);
l322.setAttribute("x1",0);
}
}

function getBodyScrollTop()
{
return self.pageYOffset || (document.documentElement && document.documentElement.scrollTop) || (document.body && document.body.scrollTop);
}

// thanks to http://james.padolsey.com/javascript/get-document-height-cross-browser/
function getDocHeight()
{
var D = document;
return Math.max(
Math.max(D.body.scrollHeight, D.documentElement.scrollHeight),
Math.max(D.body.offsetHeight, D.documentElement.offsetHeight),
Math.max(D.body.clientHeight, D.documentElement.clientHeight)
);
}

function getDocWidth()
{
var D = document;
return Math.max(
Math.max(D.body.scrollWidth, D.documentElement.scrollWidth),
Math.max(D.body.offsetWidth, D.documentElement.offsetWidth),
Math.max(D.body.clientWidth, D.documentElement.clientWidth)
);
}

var roachX = 0;
var roachY = 0;

var destinationX = 0;
var destinationY = 0;

var roachVX = 0;
var roachVY = 0;
var roachVR = 0;

svg.style.setProperty("left", ""+(roachX-20)+"px", null);
svg.style.setProperty("top", ""+(roachY-20)+"px", null);

var progressTimer;

function moveRoach()
{
if (stopFun == 1)
return;
if (Math.abs(destinationX-roachX) + Math.abs(destinationY-roachY) < 20)
{
destinationX = Math.random()*getDocWidth();
var minY = roachY - 1024; if (minY < 0) minY = 0;
var maxY = roachY + 1024; if (maxY > getDocHeight()) maxY = getDocHeight();
destinationY = Math.random()*(maxY-minY)+minY;
//destinationY = Math.random()*getDocHeight();
roachVX = destinationX - roachX;
roachVY = destinationY - roachY;
roachVR = Math.sqrt(roachVX*roachVX + roachVY*roachVY);
if (roachVR > 1)
{
roachVX /= roachVR;
roachVY /= roachVR;
}
roachVX *= 10;
roachVY *= 10;
var alpha = Math.round(Math.atan2(roachVY, roachVX)/Math.PI*180);
svgG.setAttribute("transform","translate(20,20) rotate(" + alpha + ")");
progressTimer = setTimeout("moveRoach()", Math.round(Math.random()*15000));
return;
}
roachX += roachVX;
roachY += roachVY;
svg.style.setProperty("left", ""+(roachX-20)+"px", null);
svg.style.setProperty("top", ""+(roachY-20)+"px", null);
legPosition++;
if (legPosition > 3) legPosition = 0;
placeLegs();
progressTimer = setTimeout("moveRoach()", 100);
}

progressTimer = setTimeout("moveRoach()", 100);

четверг, 9 февраля 2012 г.

Изучаем хирагану ^_^

Ну не удается мне запомнить, что называется, "в лоб" эту японскую азбуку. Возникла мысль сделать какую-нибудь простенькую игрушку, чтобы, так сказать, в танце и песне овладеть этими неправославными знаками. Фигушки, всё равно не помогло. Зато слегка научился работать с flex 8-)