среда, 12 декабря 2012 г.

SQL Server: удалить Maintenance Plan

Почему-то просто через Management Studio удаление не срабатывало.

Нашлась вот такая последовательность действий:

1. Select the ID with the select statement
select * from sysmaintplan_plans

2. Replace with the selected ID and run the delete statements
delete from sysmaintplan_log where plan_id = ''

delete from sysmaintplan_subplans where plan_id = ''

delete from sysmaintplan_plans where id = ''

3. Delete the SQL Server Jobs with the Management Studio

понедельник, 3 декабря 2012 г.

Slackware 14 x64: Как установить плагин Java на Firefox?

В общем, понадобилось запускать java-апплеты, а в браузере, похоже, поддержка этого дела не была включена. Поступил так:

1. Загрузил с http://www.java.com/ru/download/ архив .tar.gz для Linux x64.

2. Следуя инструкции разархивировал этот архив в /usr/java. Получилась конструкция /usr/java/jre1.7.0_09/...

3. Нашел, откуда запускается FireFox (команда which firefox указала на символическую ссылку, которая, в свою очередь, привела к /usr/lib64/firefox-15.0/firefox)

4. По этому пути должна была обнаружиться папка plugins. Но её не оказалось, так что пришлось создать.

5. Зашел в папку plugins и создал символическую ссылку выполнив команду:
ln -s /usr/java/jre1.7.0_09/lib/amd64/libnpjp2.so

6. Перезапустил браузер.

Как утянуть видео с youtube ?

Вот этот сервис помог: http://www.savevid.com/.

вторник, 27 ноября 2012 г.

Ubuntu 12.10: ru.archive.ubuntu.com

Что-то сегодня весь вечер не удается установить обновления. Рядом с часами горит значок о том, что аж 25 доступно, но при скачивании получаем ошибку.
Похоже, не удается соединиться с http://ru.archive.ubuntu.com. Пришлось в /etc/apt/sources.list убрать из адресов ru. После этого команда sudo aptitude update отработала нормально.

воскресенье, 25 ноября 2012 г.

xubuntu 12.10: чудеса с smbfs

С какого-то перепугу после обновления перестала работать команда mount -t smbfs ... Типа, неизвестный тип файловой системы smbfs, и вообще идите нафиг.
Нет, конечно, mount -t cifs ... взамен помогло, но всё равно немного странно: а как же обратная совместимость?

понедельник, 19 ноября 2012 г.

четверг, 15 ноября 2012 г.

СПГС

Как обычно, всё началось с мелочи. А именно - понадобился перевод песни Moby из фильма "Угнать за 60 секунд". Поиск по русскоязычному интернету выдавал диковатые варианты (см. например, тут), но, по правде говоря, собственных идей, что означают все эти "Bring Sally up" не было. Может, какая-то идиома...

К счастью, есть англоязычный интернет. В котором буквально за пару минут выяснилось, что не только переводы убоги, но и оригинальный текст безбожно перевран. "Green sally up" - тоже, конечно, непонятное словосочетание, но, по счастью обнаружился комментарий, расставивший всё на свои места.



Вот уж действительно, надмозги...

понедельник, 5 ноября 2012 г.

Slackware 14 x64: Настраиваем icecast

В общем, хочется раздавать по локалке радио, которое получаю из интернета и слушаю при помощи mpg123:
mpg123 -p http://myproxy http://webcast1.emg.fm:55655/retro64.mp3

1. С сайта www.icecast.org скачиваю и устанавливаю icecast-2.3.3. Вроде, без проблем, но для того, чтобы получать звук из /dev/dsp (*), похоже, этого мало - нужен еще ices.

2. Хорошо, пытаюсь установить ices-2.0.2. Ему требуется какой-то libshout.

3. Стаскиваю оттуда же и устанавливаю libshout. Это, оказывается, нужно делать хитро, а именно:
./configure --prefix=/usr/local
make
sudo make install
Но все равно не помогает, попытка продолжить устанавливать ices выдает всё то же:
configure: /usr/bin/pkg-config couldn't find libshout. Try adjusting PKG_CONFIG_PATH

4. Выясняется, что у меня в PKG_CONFIG_PATH сплошные lib64, и требуется команда:
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH

5. ices наконец собирается, но запускаться отказывается примерно с такой ошибкой:
ices: error while loading shared libraries: libshout.so.3: cannot open shared object file: No such file or directory.
Проверяю, что путь, по которому лежит libshout.so.3, находится в /etc/ld.so.conf, и выполняю команду:
sudo /sbin/ldconfig

6. Дальше беру из /usr/local/share/ices и /usr/local/share/icecast/doc понравившиеся конфиги (мне подошли ices-oss.xml и icecast-minimal.xml), правлю их под себя и запускаю:
icecast -c icecast-config.xml &
ices ices-config.xml &

*) как этот звук туда попадает - отдельный позор (просто соединил шнурком аудиовыход и микрофонный вход ^_^)

пятница, 2 ноября 2012 г.

Надо будет попробовать 8-)

Пицца на сковороде за 10 минут


Цитата:

Ингредиенты:
4 ст.л. сметаны
4 ст.л. майонеза
2 яйца
9 ст.л. муки (без горки, в ущерб)
сыр

Приготовление:

Тесто получается жидкое, как сметана, его вылить на сковороду смазаную маслом и уже сверху положить любую начинку (томат, колбаса, солёные огурчики, оливки, помидоры и др.)
Залить майонезом, и сверху толстый слой сыра.
Очень рекомендую тооолстый слой сыра.
Ставим сковороду на плиту, буквально на несколько минут, огонь большой не делайте
Сковороду сразу накрываю крышкой, как только сыр немного расплавился, пицца готова.

http://vk.com/wall153399928_844

Курицу режем на порционные куски, складываем в глубокую сковороду (не солим и не перчим) так, чтобы плотно прилегали друг к другу, посыпаем горстью изюма (лучше черным "королевским"). В миску выливаем бутылку темного пива, всыпаем 1 ст. л. соли (без верха) и 1 ст.л. сахара. Заливаем этим всем курицу (чтобы покрыло полностью мясо) и в духовку на средний огонь на 1-1,2 часа. Вкуснятина...А цвет!

http://forum.gorod.dp.ua/showthread.php?t=141118

пятница, 7 сентября 2012 г.

Онлайн построитель графиков:

http://www.reshalki.ru/yasam/graph.htm

воскресенье, 12 августа 2012 г.

SMS+python - закрывая тему

Наконец-то руки дошли разобраться с получением списка SMS-сообщений из моего Huawei E1550. Всё это дело (и отправку, и прием) свёл в один маленький модуль с претенциозным названием smsutils.py.

Пользоваться им совсем просто:
#!/usr/bin/python
# -*- coding: UTF-8 -*-

import smsutils

# отправляем SMS
smsutils.SendSMS('Привет, мир!', '79031234567', '/dev/ttyUSB0')

# читаем SMS с карты
result = smsutils.GetSMS('/dev/ttyUSB0')

for r in result:
    print r[0], ' ', r[1], ' ', r[2], '\n', r[3]

Исходный код smsutils.py таков:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
#
# Набор вспомогательных функций для работы с SMS
#
# большое спасибо:
#     http://www.varesano.net/blog/fabio/serial%20rs232%20connections%20python
#     http://www.dreamfabric.com/sms/

import serial, datetime, time, random


# Преобразование номера телефона в международном формате в формат SMS
#
# Исходная строка, содержащая телефон в международном формате 79130123456,
# дополняется справа символом F                                                            - 79130123456F,
# разбивается на пары символов                                                             - 79 13 01 23 45 6F,
# в каждой паре символы меняются местами                                                   - 97 31 10 32 54 F6,
# слева приписывается идентификатор международного формата (91)                            - 91 97 31 10 32 54 F6,
# слева приписывается количество цифр в телефоне, т.е. 11 в шестнадцатеричном формате (0B) - 0B 91 97 31 10 32 54 F6
#
# Возвращаемое значение - строка, содержащая закодированный номер телефона 0B919731103254F6
#
def PhoneNumberToSMS(number):
    number += 'F'
    result = '0B' + '91'
    i = 0
    while i < len(number):
        result += number[i+1] + number[i]
        i += 2
    return result


# Преобразование строки символов в формат SMS
#
# Каждый двухбайтовый юникодный символ в строке разбивается на пару байт,
# и формируется новая строка, состоящая из шестнадцатеричных представлений этих байтов
#
# Возвращаемое значение - строка, содержащая строку символов в формате SMS
#
def TextToSMS(text):
    b = text
    result = ''
    i = 0
    while i < len(b):
        o = ord(b[i])
        result += ("%0.2X" % (o/256)) + ("%0.2X" % (o%256))
        i += 1
    return result


# Восстановление номера телефона в международном формате в из формата SMS
#
# Исходная строка, содержащая закодированный телефон 9731103254F6
# разбивается на пары символов           - 97 31 10 32 54 F6,
# в каждой паре символы меняются местами - 79 13 01 23 46 6F,
# убирается символ F                     - 79130123456
#
# Возвращаемое значение - строка, содержащая номер телефона 79130123456
#
def SMSToPhoneNumber(data):
    result = ""
    i = 0
    while i < len(data):
        result += data[i+1] + data[i]
        i += 2
    return result[:-1]


# Восстановление строки символов в из формата SMS
#
# Исходная строка разбивается на четверки символов, которые преобразуются в целые числа
# и формируется строка, состоящая из соответствующих этим числам символов
#
# Возвращаемое значение - раскодированная строка
#
def SMSToText(text):
    result = u''
    i = 0
    while i+3 < len(text):
        result += unichr(int(text[i] + text[i+1] + text[i+2] + text[i+3],16))
        i += 4
    return result


# преобразование целого числа в строку из нулей и единиц, соответствующую его двоичной записи
# (использовалось для отладки)
def ByteToBitsString(byte, n):
    result = ''
    for i in range(0,n):
        if byte & (1 << i) != 0:
            result = '1' + result
        else:
            result = '0' + result
    return result


# Восстановление строки из её семибитного кода
#
# На вход подается закодированная строка  - 4DEA10
# эта строка разбивается на пары символов - 4D EA 10,
# каждая пара трактуется как шестнадцатиричное представление байта - 0x4D 0xEA 0x10 = 01001101 11101010 00010000
# из первого байта 01001101 берутся семь младших битов 1001101 и преобразуются в соответствующий символ ASCII - M
# оставшийся бит 0 дополняется слева шестью младшими битами второго байта 101010: 1010100 - T
# оставшиеся два бита 11 дополняются слева пятью младшими битами третьего байта 10000: 1000011 - С
# и т.д.
#
# Возвращаемое значение - раскодированная строка MTC
#
def Decode7bit(text):
    result = ''

    bytes = [int(text[i*2:i*2+2],16) for i in range(0,len(text)/2)]

    symbol = 0
    bits   = 0
    n      = 0

    while n < len(bytes):

        if bits == 7:
            result += chr(symbol)
            symbol = 0
            bits   = 0
        else:
            symbol += (bytes[n] & (0x7F >> bits)) << bits
            result += chr(symbol)
            symbol = (bytes[n] & (0x7F << (7-bits))) >> (7-bits)
            bits   = (8-7) + bits
            n += 1

    if bits > 0 and symbol != 0:
        result += chr(symbol)

    return result


# Класс для работы с часовыми поясами (см следующую функцию)
class smsTZ(datetime.tzinfo):
    hours = 0
    def __init__(self, h):
        self.hours = h
    def utcoffset(self, dt):
        return datetime.timedelta(hours=self.hours)
    def dst(self, dt):
        return datetime.timedelta(0)

# Восстановление даты и времени из их представления SMS
#
# На вход подается закодированная строка  - 11113131516461
# эта строка разбивается на пары символов - 11 11 31 31 51 64 61,
# в каждой паре символы меняются местами  - 11 11 13 13 15 46 16,
# получившиеся строки трактуются как шестнадцатиричные представления байтов - 0x11 0x11 0x13 0x13 0x15 0x46 0x16
# эти байты представляют собой соответственно год, месяц, день, часы, минуты, секунды, часовой пояс
# часовой пояс представляется как количество четвертей часа, т.е. 0x16 = GMT+4 (седьмой бит отвечает за знак)
#
# Возвращаемое значение - дата и время 2011-11-13 13:15:46+04:00
#
def SMSToTimeStamp(text):
    year   = int(text[1] + text[0]) + 2000
    month  = int(text[3] + text[2])
    day    = int(text[5] + text[4])
    hour   = int(text[7] + text[6])
    minute = int(text[9] + text[8])
    second = int(text[11] + text[10])
    tz     = int(text[13] + text[12])
    tz = ( (tz & 0x7F) if (tz & 0x80 == 0) else -(tz & 0x7F) ) / 4
    return datetime.datetime(year, month, day, hour, minute, second, 0, smsTZ(tz))


# Обмен с последовательным портом
def str_send (ser, textline):
    ser.write(textline)

    out = ''
    # let's wait one second before reading output (let's give device time to answer)
    N = 10
    while N > 0:
        time.sleep(1)
        while ser.inWaiting() > 0:
            out += ser.read(1)

        if ('OK' in out) or ('ERROR' in out) or ('>' in out):
            N = 1

        N -= 1

    return out


# отправка пин-кода в открытый порт
def SendPINToPort(ser, pin):
    str_send(ser, 'AT+CPIN="%s"\r' % (pin))


# отправка пин-кода модему
def SendPIN(serial_name, pin):

    # подключаемся к порту
    ser = serial.Serial(serial_name, 115200, timeout=1)
    ser.open()

    # отправляем пин-код
    SendPINToPort(ser, pin)

    # закрываем порт
    ser.close()


# отправка SMS-сообщения
def SendSMS(msg, phone, serial_name, pin=None):

    # если нечего или некуда отправлять, выходим
    if msg == '' or len(phone) != 11:
        return

    # декодируем сообщение в utf-8
    message = msg.decode('utf-8')

    # разрезаем сообщение на кусочки по 66 символов
    chunks = []
    if len(message) > 70:
        while len(message) > 66:
            chunks.append(message[:66])
            message = message[66:]
    if len(message) > 0:
        chunks.append(message)

    # инициализируем служебную информацию
    SMS_SUBMIT_PDU = "11"
    CSMS_reference_number = ""

    # если сообщение требует конкатенации SMS, то подправляем служебную информацию
    # и генерируем четырехсимвольный номер сообщения
    if len(chunks) > 1:
        SMS_SUBMIT_PDU = "51"
        CSMS_reference_number = "%0.4X" % random.randrange(1,65536)

    # подключаемся к порту
    ser = serial.Serial(serial_name, 115200, timeout=1)
    ser.open()

    # устанавливаем формат передачи сообщения - PDU
    status = str_send(ser, 'AT+CMGF=0\r')

    # если в ответ пришел текст, содержащий SIM PIN REQUIRED, значит, модему нужен пин-код
    if 'SIM PIN' in status:
        SendPINToPort(ser, pin)
        str_send(ser, 'AT+CMGF=0\r')


    # отсылаем сообщение по кусочкам
    i = 1
    for chunk in chunks:

        # кодируем кусочек
        emessage = TextToSMS(chunk)

        # если сообщение состоит из нескольких кусочков, то в каждом кусочке надо указать
        # номер сообщения, количество кусочков и порядковый номер кусочка (1,2,3 и т.д.)
        if CSMS_reference_number != "":
            emessage = "06" + "08" + "04" + CSMS_reference_number + \
            ("%0.2X" % len(chunks)) + ("%0.2X" % i) + emessage

        # готовим строку для отправки в порт
        sms =                             \
            "00" +                        \
            SMS_SUBMIT_PDU +              \
            "00" +                        \
            PhoneNumberToSMS(phone) +     \
            "00" +                        \
            "08" +                        \
            "AA" +                        \
            "%0.2X" % (len(emessage)/2) + \
            emessage

        # подготавливаем модем - передаем ему длину отправляемой строки
        str_send(ser, 'AT+CMGS=' + str(len(sms)/2-1) + '\r')

        # отправляем строку
        str_send(ser, sms + '\x1A')

        i += 1

    # закрываем порт
    ser.close()



# чтение SMS сообщений с сим-карты
#
# возвращаемое значение: список, состоящий из кортежей, каждый из которых содержит
# номер слота на сим-карте, в котором находится сообщение (0-19),
# номер телефона отправителя,
# дату отправления сообщения,
# текст сообщения
#
def GetSMS(serial_name, pin=None):

    result = []

    # подключаемся к порту
    ser = serial.Serial(serial_name, 115200, timeout=1)
    ser.open()

    # устанавливаем формат передачи сообщения - PDU
    status = str_send(ser, 'AT+CMGF=0\r')

    # если в ответ пришел текст, содержащий SIM PIN REQUIRED, значит, модему нужен пин-код
    if 'SIM PIN' in status:
        SendPINToPort(ser, pin)
        str_send(ser, 'AT+CMGF=0\r')

    # запрашиваем список сообщений (4 - все сообщения)
    messages = str_send(ser, 'AT+CMGL=4\r')

    if 'ERROR' not in messages:

        strings = messages.split('\n')

        i = 0

        while i < len(strings):

            if '+CMGL: ' in strings[i]:

                message_header = strings[i][7:]
                message_body = strings[i+1]

                offset = 0

                SMSC_length = int(message_body[offset:offset+2],16)
                offset += 2

                SMSC_address = message_body[offset:offset+2*SMSC_length]
                SMSC_typeOfAddress = SMSC_address[:2]
                SMSC_serviceCenterNumber = SMSToPhoneNumber( SMSC_address[2:] )
                offset += 2*SMSC_length

                SMS_deliverBits = int(message_body[offset:offset+2],16)
                offset += 2

                SMS_senderNumberLength = int(message_body[offset:offset+2],16)
                offset += 2

                SMS_senderNumberType = message_body[offset:offset+2]
                offset += 2

                SMS_senderNumber = message_body[offset:offset+SMS_senderNumberLength+(1 if SMS_senderNumberLength & 1 != 0 else 0) ]
                if SMS_senderNumberType == '91':
                    SMS_senderNumber = SMSToPhoneNumber(SMS_senderNumber)
                if int(SMS_senderNumberType[0],16) & 5 == 5:
                    SMS_senderNumber = Decode7bit(SMS_senderNumber)
                offset += SMS_senderNumberLength+(1 if SMS_senderNumberLength & 1 != 0 else 0)

                TP_protocolIdentifier = message_body[offset:offset+2]
                offset += 2

                TP_dataCodingScheme = int(message_body[offset:offset+2],16)
                offset += 2

                TP_serviceCenterTimeStamp = SMSToTimeStamp(message_body[offset:offset+14])
                offset += 14

                TP_userDataLength = int(message_body[offset:offset+2],16)
                offset += 2

                if SMS_deliverBits & 64 != 0:
                    SMS_userDataHeaderLength = int(message_body[offset:offset+2],16)
                    offset += 2
                    SMS_userDataHeader = message_body[offset:offset+2*SMS_userDataHeaderLength]
                    offset += 2*SMS_userDataHeaderLength

                message_text = None
                if (TP_dataCodingScheme == 0):
                    message_text = Decode7bit(message_body[offset:])
                if (TP_dataCodingScheme & 8 != 0):
                    message_text = SMSToText(message_body[offset:])
                if message_text is None:
                    message_text = message_body[offset:]

                # добавляем в результирующий список кортеж, содержащий
                # номер слота на сим-карте, в котором находится сообщение (0-19),
                # номер телефона отправителя,
                # дату отправления сообщения,
                # текст сообщения
                result.append((message_header.split(',')[0], SMS_senderNumber, TP_serviceCenterTimeStamp, message_text))

                i += 2

            else:

                i += 1

    # закрываем порт
    ser.close()

    return result



# удаление SMS сообщения в слоте с номером slot с сим-карты
def DeleteSMS(serial_name, slot, pin=None):

    # подключаемся к порту
    ser = serial.Serial(serial_name, 115200, timeout=1)
    ser.open()

    # удаляем сообщение
    status = str_send(ser, 'AT+CMGD=%s\r' % (slot))

    # если в ответ пришел текст, содержащий SIM PIN REQUIRED, значит, модему нужен пин-код
    if 'SIM PIN' in status:
        SendPINToPort(ser, pin)
        str_send(ser, 'AT+CMGD=%s\r' % (slot))

    # закрываем порт
    ser.close()

суббота, 21 июля 2012 г.

Ubuntu: плюс Nikon CoolPix S2600

Пару часов назад прикупил вышеозначенный прибор. Сразу же появилась проблема: убунта отказалась монтировать это устройство. Полез гуглить. На первом же форуме кто-то радостно рассказал, что он перезагрузился - и всё заработало. Ну, перезагрузился. Не помогло. Команда dmesg дает следующее:
[ 1995.995577] usb 1-5: USB disconnect, device number 14
[ 1996.268085] usb 1-5: new high-speed USB device number 15 using ehci_hcd
[ 1996.416299] hub 1-0:1.0: unable to enumerate USB device on port 5
[ 1996.872072] usb 3-5: new full-speed USB device number 5 using ohci_hcd
[ 1997.072085] usb 3-5: not running at top speed; connect to a high speed hub
Что-то ни одного слова Nikon или Camera не наблюдается. libgphoto2 установлен, но на их сайте в списке поддерживаемых камер мой девайс не значится.

Решение можно было бы назвать неожиданным, если бы я какое-то решение всё же ожидал. Оказывается, есть gphoto2 - специальный клиент для цифровых камер.

Посмотреть содержимое фотика можно командой:
gphoto2 -L

Скачать все файлы на компьютер:
gphoto2 -P

Скачать выбранные файлы на компьютер:
gphoto2 -p N1-N2
(номера файлов можно подсмотреть в первой из вышеперечисленных команд)

Удалить все файлы:
gphoto2 -D

Удалить выбранные файлы:
gphoto2 -d N1-N2
(номера файлов можно подсмотреть в первой из вышеперечисленных команд)

А сам фотоаппарат оказался средненьким. Фотографии какие-то нечеткие. Может, брак, а может - "что вы хотели за эту цену"...

воскресенье, 8 июля 2012 г.

Вспомнил и нагуглил тут матрицу профессиональной квалификации программиста. За прошедшие несколько лет (м-м, пять, что ли) немножко сдвинулся вправо. С нулевого уровня на первый. И, боюсь, это - уже предел.

суббота, 30 июня 2012 г.

bash - автозавершение команды из истории

Оказывается, это делается довольно просто. Нужно в файл ~/.inputrc вписать следующее:
"\e0A": history-search-backward
"\e[A": history-search-backward
"\e0B": history-search-forward
"\e[B": history-search-forward
"\e0C": forward-char
"\e[C": forward-char
"\e0D": backward-char
"\e[D": backward-char
После этого в командной строке можно набрать несколько символов, нажать стрелку "вверх" и получить автозавершение команды из истории.

вторник, 26 июня 2012 г.

Debian: осваиваю git

Решил давеча приобщиться к прекрасному. Ну в самом деле, кругом XXI век, а я до сих пор как школьник все свои проекты храню в папочках и раз в неделю архивирую. Захотелось по-взрослому, чтобы была система контроля версий, доступ к репозиторию по http и прочее - в общем, сам пока не знаю толком, чего мне доселе не хватало.

Первый же заплыв привел на github.com, а от него - к git. Командная строка, всё такое черно-серое, юниксоидное, бесплатное - словом, понравилось. Единственная проблема: как поднять репозиторий, доступный по http (у гитхаба какие-то ограничения, да и вообще прикольно же).

К счастью, нашлась толковая инструкция. Вкратце последовательность действий такова:

1. Конфигурируем apache2. Добавляем в /etc/apache2/sites-available/default следующие строки:
SetEnv GIT_PROJECT_ROOT /путь-до-репозитория
SetEnv GIT_HTTP_EXPORT_ALL

ScriptAliasMatch \
 "(?x)^/git/(.*/(HEAD | info/refs | objects/(info/[^/]+ | [0-9a-f]{2}/[0-9a-f]{38} | \
                    pack/pack-[0-9a-f]{40}\.(pack|idx)) | \
                    git-(upload|receive)-pack))$" \
                    "/usr/lib/git-core/git-http-backend/$1"

<Directory /usr/lib/git-core/>
    Options +ExecCGI
    Order Allow,Deny      
    Allow from all
</Directory>

<Location /git>
    AuthType Basic
    AuthName "Private Git Access"
    AuthUserFile /etc/apache2/authusers
    Require valid-user
</Location>

2. Добавляем в файл с разрешенными пользователями нового пользователя по имени username командой:
htpasswd -c /etc/apache2/authusers username

3. Перезапускаем апач командой:
sudo /etc/init.d/apache2 restart

4. Настраиваем репозиторий. Создаем внутри каталога /путь-до-репозитория очередную папочку для проекта myProject, переходим в неё и выполняем волшебную команду:
git init --bare

5. Раздаем на каталог myProject права для учетной записи, под которой работает апач:
sudo chown -R www-data myProject
sudo chgrp -R www-data myProject

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

1. Устанавливаем и настраиваем git на клиентской машине. Вводим имя пользователя (username), его e-mail (это будет отображаться при просмотре истории коммитов) и, если нужно, прокси:
git config --global user.name username
git config --global user.email mymail@mymail.ru
git config --global http.proxy http://myproxy:8888

2. Можно еще указать, чтобы git кэшировал на какое-то время пароли:
git config --global credential.helper 'cache --timeout=3600'
(честно говоря, у меня не сработало; приходится каждый раз при выполнении команд git push, git pull вводить пароль)

3. Клонируем нужный проект в локальный каталог командой:
git clone http://username@host/git/myProject

4. Меняем в проекте всё, что нам надо, и скармливаем изменения git-у:
git add мой-измененный-файл

5. Коммитим изменения в проекте:
git commit -m "описание изменений"

6. Заливаем измененный проект обратно на сервер:
git push origin master

Кстати, вот тут лежит хороший учебник.

пятница, 11 мая 2012 г.

Windows XP: Не удалось выполнить действие из-за неправильной установки клиента почты по умолчанию

Две машины. На одной всё хорошо (ну как всё...), на другой при попытке в excel-е кликнуть на гиперссылку, содержащую e-mail, получаем вышеуказанную ошибку.

Оказалось, в реестре почему-то испортился раздел HKLM\Software\Classes\mailto.
На здоровой машине, у которой почтовым клиентом числится OE, этот раздел должен выглядеть так:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\mailto]
@="URL:Протокол MailTo"
"EditFlags"=hex:02,00,00,00
"URL Protocol"=""

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\mailto\DefaultIcon]
@=hex(2):25,00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,46,00,69,00,6c,00,65,\
  00,73,00,25,00,5c,00,4f,00,75,00,74,00,6c,00,6f,00,6f,00,6b,00,20,00,45,00,\
  78,00,70,00,72,00,65,00,73,00,73,00,5c,00,6d,00,73,00,69,00,6d,00,6e,00,2e,\
  00,65,00,78,00,65,00,2c,00,2d,00,32,00,00,00

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\mailto\shell]

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\mailto\shell\open]

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\mailto\shell\open\command]
@=hex(2):22,00,25,00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,46,00,69,00,6c,\
  00,65,00,73,00,25,00,5c,00,4f,00,75,00,74,00,6c,00,6f,00,6f,00,6b,00,20,00,\
  45,00,78,00,70,00,72,00,65,00,73,00,73,00,5c,00,6d,00,73,00,69,00,6d,00,6e,\
  00,2e,00,65,00,78,00,65,00,22,00,20,00,2f,00,6d,00,61,00,69,00,6c,00,75,00,\
  72,00,6c,00,3a,00,25,00,31,00,00,00

среда, 9 мая 2012 г.

SQL Server 2005 + Win2008: Login failed (Error: 18456, State: 11)

После установки SQL Server 2005 на Windows 2008 со смешанным режимом аутентификации выяснилось, что подцепиться с доменной, даже самой крутой учеткой не удается. Решение помогло такое:
create login [domain\user] from windows
go
sp_addsrvrolemember [domain\user], 'sysadmin'
go

вторник, 8 мая 2012 г.

Ubuntu 12.04: Skype magic

Команда запуска выглядит так:
LD_PRELOAD=/usr/lib/i386-linux-gnu/libv4l/v4l1compat.so /usr/bin/skype
Иначе видео не работает. Причем, даже на 64-битной версии - всё равно i386.

UPD 2013-08-15: Давно не пользовался скайпом, а тут понадобился - и обнаружилась неприятность: звук с какой-то реверберацией, микрофон не работает... В общем, оказывается, его даво пора было обновить. Под ubuntu 13.04 этот процесс выглядит так:
1. Подключаем репозиторий:
# add-apt-repository "deb http://archive.canonical.com/ $(lsb_release -sc) partner"
# apt-get update
2. Устанавливаем Skype:
# apt-get install skype && sudo apt-get -f install

пятница, 27 апреля 2012 г.

Ubuntu 12.04: русская раскладка клавиатуры

Обновился до Xubuntu 12.04. Оказалось, теперь не могу печатать по-русски. После гугления "xubuntu russian keyboard layout ctrl+shift" выяснилось, что нужно проделать следующее:
Right-click on a panel, select add item and add the keyboard layout switcher.
(of course xfce4-xkb-plugin – xkb layout switch plugin for the Xfce4 panel must be installed and you need to add the desired layout in /etc/X11/xorg.conf, e.g. Option “XkbLayout” “us, de, fr,ru” in the keyboard section lets me (and you too?) switch between these layouts)

Если по-русски: кликнуть правой клавишей мыши на панель xfce (тоже, кстати, нетривиальная задача - найти, где эта панель не закрыта дочерними элементами), выбрать пункт "добавить новые элементы", в появившемся диалоговом окне найти пункт "раскладки клавиатуры". В уголке экрана появится флажок, в контекстном меню которого будет пункт "Свойства", и там уже можно выбрать нужный способ переключения раскладок.

При этом должен быть установлен модуль xfce4-xkb-plugin (установить его можно командой sudo aptitude install xfce4-xkb-plugin).

Почему в предыдущей версии всё работало и без переключателя раскладки клавиатуры на панели задач - тайна, покрытая мраком.

Ну и флэшплеер опять заглючил. Когда ж его починят-то?..

P.S. Хочу обратно ScrollLock в качестве индикатора раскладки! Вот это не помога-а-ает:
1. Open /etc/X11/xorg.conf
2. Find and modify the following:
Section "InputDevice"
    Identifier  "Keyboard0"
    Driver      "keyboard"
    Option      "CoreKeyboard"
    Option "XkbRules" "xorg"
    Option "XkbModel" "pc105"
    Option "XkbLayout" "us, ru"
    Option "XkbOptions" "grp:ctrl_shift_toggle,grp_led:scroll"
EndSection

среда, 25 апреля 2012 г.

kdenlive: как создать DVD-диск?

Два вечера оттачивал свои mad skillz в искусстве создания видеофайлов с помощью kdenlive. В общем-то задача проста: есть кучка клипов в разных форматах, надо их запихнуть в DVD. Решается она тоже не очень сложно: запускаем kdenlive, добавляем в проект клипы, потом перетаскиваем эти клипы на временную шкалу внизу и нажимаем кнопку "Выполнить сборку". Однако возникает пара тонкостей:

1. Клипы имеют разную громкость. Чтобы её более-менее выровнять, на временной шкале кликаем правой клавишей нужный клип и в контекстном меню выбираем "Добавить эффект - Коррекция звука - Громкость (по ключевым кадрам)". Там громкостью клипа можно рулить.

2. Потребовались субтитры. В kdenlive с этим как-то совсем бедненько. В дерево проектов добавляем клип титров и там пишем, что нужно. Дальше кидаем этот клип на вторую дорожку под исходным клипом и мучаем появившийся между этими клипами желтый прямоугольник перехода до достижения нужного эффекта.

3. И, наконец, собственно создание образа DVD. Если нажать кнопку "Сборка проекта", то появится окошко, в котором по умолчанию стоит назначение "Сборка в файл". Я, наивный вьюноша, выбрал кодек mpeg-2, получил файл *.mpg, который затем попытался скормить "Мастеру создания DVD" (за которым скрывается аппликуха по имени dvdauthor). Мастера стошнило примерно с такими словами:
    WARN: Skipping sector, waiting for first VOBU...
    ERR: SCR moves backwards, remultiplex input
Видимо, этот "мастер" по-человечески понимает только файлы *.vob. Чтобы требуемый VOB создать, нужно сделать примерно следующее: выбрать в свойствах проекта какой-нибудь не сильно крутой профиль видео, например "DV/DVD PAL", а при сборке проекта указать назначение "DVD". Тогда получается VOB, из которого "Мастер создания DVD" шустро делает образ диска *.iso, прожигающийся, в свою очередь, на болванку командой:
growisofs -dvd-compat -Z /dev/dvd=mydisk.iso

Кстати, оказывается, вожделенный VOB из прочих форматов можно получить еще и так:
ffmpeg -i inputfile.mpg -target pal-dvd inputfile.vob

пятница, 20 апреля 2012 г.

Ubuntu 11.10: сконвертировать .MTS в .MKV (например)

Пришлось тут давеча иметь дело с видеофайлом, имеющим разрешение MTS. Гугление показало, что это - по-видимому, специальный формат, генерируемый видеокамерами. Открыть его нормально почему-то не захотел ни один плеер. Даже VLC 2.0 сделал это с неохотой и отказался конвертировать его во что-нибудь более удобоваримое.

Однако, оказывается, есть и для этой штуки конвертер, который называется handbrake. Устанавливается это чудо так:
sudo add-apt-repository ppa:stebbins/handbrake-releases
sudo apt-get update
sudo apt-get install handbrake-cli
sudo apt-get install handbrake-gtk

Кстати, чудо - без кавычек. Исходный стомегабайтный файл, содержащий минуту видео, после перекодирования занял 7(!) мегов.

суббота, 14 апреля 2012 г.

Пасха: стряпаем куличи

1 кг муки, 50 г дрожжей живых в брикетике, 1-1.5 стакана молока, 10 желтков, 3 белка, 250 г сахара, 200 г сливочного масла, 25 г коньяка, соль на кончике ножа, 20 г изюма, пакетик ванильного сахара, 0.5 г шафрана.

0. Если шафран настоящий, то за сутки до священнодействия замочить его в 1 столовой ложке коньяка. Если ненастоящий (куркума), то разбавить в одной столовой ложке воды чайную ложку порошка.

1. В 0.5-1 стакане кипящего молока заварить 100 г муки.
2. Одновременно дрожжи развести в 0.5 стакана слегка тепловатого молока и смешать со 100 г муки, накрыть и оставить на 10 минут.
3. Приготовить дрожжевую смесь: первую смесь соединить со второй, накрыть и поставить в теплое место на час.
4. Отделить желтки от белков, желтки с сахаром и солью растереть добела, половину этой смеси влить в дрожжевую смесь и добавить 250 г муки, вымесить и дать подойти в течение одного часа.
5. Добавить остальные желтки и 500 г муки, хорошо вымесить и добавить взбитые в густую пену белки, опять хорошо вымесить.
6. В готовое тесто влить небольшими порциями теплое (но не горячее!) жидкое масло, хорошо вымесить, добавить коньяк и шафран, опять хорошо вымесить и снова дать подойти.
7. Изюм замочить в горячей воде.
8. Тесто умять, добавить изюм, разложить по формам и дать подняться в третий раз.
9. Выпекать при температуре 200 градусов минут 30-40.

А вот вариантов глазури несколько:

Глазурь №1: семь оставшихся белков взбить с сахаром. Самый экономичный, но не самый стойкий вариант.

Глазурь №2: 500 г сахара, 80 г воды, варить, пока капля, опущенная в воду, не будет собираться в комочек, добавить плитку белого шоколада и 50-70 г сливочного масла, всё хорошо прокипятить, чтобы растворилось в однородную массу. Перед использованием, если застыло, можно повторно слегка разогреть.

Глазурь №3 (на N куличей): 2*N столовых ложек молока разогреть, постепенно всыпать 100*N г сахарной пудры, помешивать до образования однородной массы.

Глазурь №4: 1 стакан сахара, 1 стакан 30% сливок, 1 чайная ложка масла, ванильный сахар. Сливки и сахар варить помешивая до загустения, добавить масло и ваниль и, желательно, не давая остыть мазать куличи.

Итог: пять часов потерянной жизни и шесть крохотных кексиков. Впрочем, здесь важен принцип.

UPD 2016-05-24: Запишу еще рецепт, идеально дополняющий вышеприведенный, потому что использует как раз 7 оставшихся без дела яичных белков.

Ангельский бисквит

7 яичных белков (пару суток их можно или даже нужно подержать в холодильнике - как раз, пока съедаются куличи)
190 г сахара (и как эти граммы отмерять? я взял 1 граненый стакан с горкой)
80 г муки (опять-таки, где-то 2/3 граненого стакана, я приноровился отмерять столовой ложкой, у меня выходит пять ложек с о-о-очень хорошей горкой = один стакан, так вот, примерно 3 таких столовых ложки)
2 чайных ложки лимонного сока
соль в гомеопатических количествах (на кончике ножа)
1 пакетик ванильного сахара (думается, неважно какой именно, в любом случае не килограммовый)

1. Разогреваем духовку до 170-180С.
2. Полстакана сахара, пакетик ванильного сахара, соль и муку смешиваем в миске.
3. Добавляем взбитые с лимоном и оставшимся полстаканом сахара яичные белки.
4. Всё аккуратно перемешиваем - очевидно, чтобы белковая пена не сильно порушилась.
5. Выливаем получившееся тесто в силиконовую форму с дыркой посередине (где-то читал, что дырка важна - в обычной форме бисквит имеет тенденцию в серёдке оседать), запекаем примерно 30 минут.
6. Затем продукт нужно остудить, причем делается это хитро: форма с бисквитом переворачивается вверх тормашками и надевается дыркой на подходящую бутылку, ну, или, как делал я, опирается этой самой дыркой на перевернутую банку.
7. Аккратно отделяем остывший бисквит от формы, украшаем как вздумается.

Справедливости ради тут надо бы добавить пункт восемь - мгновенно съедаем, потому что получается весьма легкая и вкусная штука.

понедельник, 9 апреля 2012 г.

ubuntu 11.10 + kdenlive: SDL модуль от MLT не найден

Поставил сабж , вылезла такая ошибка. Оказывается, после установки надо:
1. Выполнить команду
sudo add-apt-repository ppa:sunab/kdenlive-release && sudo apt-get update && sudo apt-get install kdenlive

2. Зайти в менеджер обновлений, выполнить проверку на наличие обновлений и установить то, что найдется.

пятница, 30 марта 2012 г.

ubuntu 11.10 и flash player 11.2 r202

После вчерашнего обновления заглючил по-черному флэшплеер. Решение пока помогло такое:
1. Удалить Flash player 11
2. Инсталлировать flashplugin-installer при помощи команды sudo apt-get install flashplugin-installer
3. Загрузить Flash Player 11.1.102.63 (174 метра). Там будет архив с плеером для разных операционок, из которого нужно извлечь подходящий libflashplayer.so
4. Скопировать извлеченный libflashplayer.so в папку /usr/lib/flashplugin-installer/

четверг, 29 марта 2012 г.

Для борьбы с обфускацией

Отличный сайт: http://jsbeautifier.org/. Форматирует код javascript в удобочитаемый вид.

понедельник, 26 марта 2012 г.

Всякая всячина о Python

Модули


Q: Как получить список загруженных модулей?
A:
outText = ""
for key in sys.modules.keys():
outText += key + "\n"


Q: Как загрузить модуль, который лежит не в папке скрипта?
A:Добавить путь поиска модулей:
sys.path.append('/mypath')
Но это не очень хороший способ. Если в другом проекте нужно будет использовать другой модуль с тем же именем, начнется каша.

Q: Как загрузить модуль с именем, содержащим недопустимые символы?
A:
module=__import__('my-module-name')
my_module_name = getattr(module, 'my-module-name')


Q: Как удалить модуль из списка загруженных?
A:
try:
del sys.modules['my_module_name']
except:
pass


Q: Как загрузить модуль с нетривиальным именем, находящийся в нетривиальном месте?
A:Воспользоваться вот такой милой процедурой:
import imp

def import_module(name, path):

# Fast path: see if the module has already been imported.
try:
return sys.modules[name]
except KeyError:
pass

# If any of the following calls raises an exception,
# there's a problem we can't handle -- let the caller handle it.
fp, pathname, description = imp.find_module(name, path)

try:
return imp.load_module(name, fp, pathname, description)
finally:
# Since we may exit via an exception, close fp explicitly.
if fp:
fp.close()


mod_python


Q: Как получить реальный путь до скрипта на сервере?
A:
def index(req):

import os
from mod_python import apache

req.add_common_vars()
pathname = os.path.dirname(req.subprocess_env['SCRIPT_FILENAME'])
return pathname


Q: Как посмотреть доступный список переменных окружения на сервере?
A: С помощью такого скрипта:
# -*- coding: UTF-8 -*-

from mod_python import apache

def index(req):

req.add_common_vars()

result = ""
for key in req.subprocess_env.keys():
result += key + "=" + req.subprocess_env[key] + "\n"
return result

среда, 21 марта 2012 г.

1С Предприятие 8.1: настройка прокси-сервера

Оказывается, в этой замечательной программе некоторые справочники (банки, курсы валют) можно обновлять по HTTP-протоколу из интернета. Единственная проблема - если контора находится за прокси, нужно этот прокси где-то указать. Оказывается, это делается так: на рабочей станции в каталоге C:\Program Files\1cv81\bin\conf создается файл inetcfg.xml, в нём прописывается следующее:
<InternetProxy 
protocols="http=myProxyServerAddress1:8080 myProxyServerAddress2:8080"
user="proxyUser"
password="proxyPassword"
bypassOnLocal="true"
bypassOnAddresses="127.0.0.1 *. master"
/>
и перезапускается 1С.

Небольшая тонкость: авторизация NTLM с учетной записью, отличной от текущего пользователя, похоже, не срабатывает, т.е. указание user="domain\user" нужного эффекта не дает. Однако, если вообще снести user=... и password=..., то 1С на прокси авторизуется с текущей доменной учетной записью без проблем.

понедельник, 19 марта 2012 г.

Remote Control is disabled

Неожиданно при попытке подконнектиться к удаленной машине при помощи Configuration Manager Remote Control стало после окошка с авторизацией выскакивать сообщение "Remote Control is disabled". Не знаю уж отчего такое произошло, но в моем случае помогла правка реестра:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SMS\Client\Client Components\Remote Control]
"Enabled"=dword:00000001

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

Доступ к папке принтеров из командной строки Windows XP

Как известно, всякие настройки можно вызывать из командной строки. Вот, например, для принтеров:
control.exe printers


Однако, если мы хотим использовать это в связке с командой runas, возникают трудности. Например, с теми же принтерами команда:
runas /user:domain\admin "control.exe printers"
ничего не отображает.

Оказалось, что "Принтеры" - это специальная папка, открывается она при помощи explorer.exe, её можно открыть вообще "в лоб" вот так:
%windir%\Explorer.exe ::{2227A280-3AEA-1069-A2DE-08002B30309D}

А explorer.exe, похоже, по умолчанию не создает отдельный процесс с нужными правами, а либо подживается к уже существующему, либо просто тихо дохнет. Чтобы его от этой вредной привычки отучить, требуется при помощи редактора реестра прописать для админской учетки параметр HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced\SeparateProcess равным 1.
UPD 13-05-28: Есть еще вариант запуска с хитрым ключом:
explorer /separate

То есть последовательность действий такова:

1. запускаем regedit.exe командой:
runas /user:domain\admin regedit.exe
Правим там параметр HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced\SeparateProcess = dword:00000001

2. Получаем на экран папку с принтерами:
runas /user:domain\admin "control.exe printers"
И делаем там своё чёрное админское дело...

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

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

Как подсмотреть список доступных переменных окружения в mod_python

Всё-таки в PHP было удобно. Пишешь файлик, содержащий <?php phpinfo();?>, и - вуаля - всё как на ладони. Для питона конструкция сложнее:
# -*- coding: UTF-8 -*-

from mod_python import apache

def index(req):

req.add_common_vars()

result = ""
for key in req.subprocess_env.keys():
result += key + "=" + req.subprocess_env[key] + "\n"
return result

понедельник, 12 марта 2012 г.

Установить VLC 2.0 в Ubuntu

Давеча выяснилось, что цельнотянутая с инета анимашка не хочет проигрываться. Даже в VLC. Это показалось странным. Видимо, дело в x264, который то ли сильно свежий, то ли плеер сильно старый, в общем, так или иначе, а что-то нужно будет обновить. Оказалось,что вышел VLC 2.0, для установки которого в убунте 11.10 пришлось выполнить следующие команды:
sudo add-apt-repository ppa:n-muench/vlc
sudo apt-get update && sudo apt-get install vlc

И, надо отметить, помогло.

понедельник, 5 марта 2012 г.

Linux: Восстановить разрешение экрана xfce

После запуска и некорректного завершения некоторых игрушек под wine, переключающих разрешение экрана в 800x600, хочется научиться восстанавливать "всё как было", что называется, в один клик.

Оказывается, для этих целей подходит команда xrandr. Будучи запущена без параметров, выводит список доступных разрешений экрана. Чтобы сменить разрешение, нужно выполнить, например:
xrandr -s 1024x768

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

Windows: Служба "Служба профилей пользователей" препятствует входу в систему

Q:При попытке залогиниться на компе с Windows 7 под доменной учеткой выдается сообщение: Служба "Служба профилей пользователей" препятствует входу в систему. Невозможно загрузить профиль пользователя."
A: Помогло следующее (цитата):
Заходим под другой учеткой с наличием администраторских прав. В ресстре идем по ветке HKLM\Software\Microsoft\Windows NT\CurrentVersion\ProfileList\ Там уже раздел с ID нужного нам пользователя (нужный нам раздел можно найти по ключам, указывающим путь к профилям). Оказывается, что правильный раздел был с именем вида <идентификатор>.bak + к тому же существовал раздел с таким же идентификатором, но без .bak, в котором путь к профилю был указан ошибочно. Итак, удаляем раздел с ошибочным путем, переименовываем правильный раздел, удалив в нем .bak и перелогиниваемся уже под той учеткой, под которой войти не удалось.

четверг, 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);
}
}
}


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



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

Фишки Windows

Q: Где посмотреть путь до рисунка рабочего стола?
A: HKEY_CURRENT_USER\Control Panel\Desktop

Q: Как изменить права пользователя на доступ к папке из командной строки?
A: Воспользоваться утилитой cacls (по крайней мере, в WinXP):
cacls C:\WINDOWS\HUH-MUH /T /E /g Пользователи:С

среда, 15 февраля 2012 г.

Википедия, кладезь лулзов

Давеча забрёл на страницу с описанием фильма "Золотой глаз" (интересно, правда ли, что там между любыми двумя статьями не больше шести переходов?). Так вот, господа, читал и плакал...

В 1986 году, ещё во времена холодной войны, два агента английской спецслужбы МИ-6 — агент 006 Алек Травельян и агент 007 Джеймс Бонд — выполняют секретное задание по взрыву химического завода рядом с плотиной в Архангельске. Во время этой операции полковник Советской Армии Аркадий Григорьевич Урумов убивает Травельяна выстрелом из пистолета в голову, из-за чего Бонд вынужден перевести таймер на бомбах с 6 на 3 минуты и сбежать с завода на самолёте за мгновение до взрыва.

Прошло 9 лет, Советский Союз распался, холодная война завершилась. Бонд отправляется в Монте-Карло на разведывательную миссию. Он знакомится с Ксенией Онатопп, бывшим советским лётчиком-испытателем, разъезжающей на Феррари с фальшивыми французскими номерами. Ночью она на своей яхте в постели душит любовника — канадского адмирала — и забирает себе его пропуск. Утром Джеймс Бонд проникает на эту яхту и находит тело адмирала. Он отправляется на военную презентацию европейского вертолёта-невидимки «Tiger», которая проходит в Монте-Карло. Там Ксения передаёт пропуск Аркадию Урумову, который к этому времени уже стал генерал-полковником. Они оба проходят на презентацию с помощью краденного пропуска. Затем они убивают обоих пилотов вертолёта, одевают их форму и улетают на нём, несмотря на попытку Бонда остановить их.

Вертолёт прилетает на российскую спутниковую станцию «Северная» в Сибири. Ксения расстреливает почти всех служащих станции, а Урумов активирует новое российское космическое оружие под названием «Золотой глаз». Это оружие представляет собой несколько спутников, которые взрываются над любым нужным местом на Земле, создавая мощные электромагнитные импульсы, выводя из строя всю электронику. Урумов направляет удар на станцию «Северная» и улетает на вертолёте вместе с Ксенией и их сообщником, программистом Борисом Грищенко. Один из служащих станции успевает до своей смерти включить сигнал тревоги, и к «Северной» направляются три «МиГа». Однако «Золотой глаз» уничтожает станцию и «МиГи». Украденный Ксенией и Урумовым вертолёт «Tiger» не чувствителен к электромагнитным импульсам, поэтому они спокойно улетают. За событиями следили люди из МИ-6 через спутник, также уничтоженный импульсом «Золотого Глаза». Позже, использовав другой спутник, они заметят, что на «Северной» была одна выжившая — женщина-программист Наталья Фёдоровна Семёнова, которая выбралась из-под обломков спутниковой тарелки и добралась до ближайшего города на собаках.

Новый М понимает, что в этом деле замешана русская мафия. Она отправляет Бонда в Санкт-Петербург разобраться в этой ситуации. Перед этим Q снабжает Бонда несколькими своими новыми изобретениями: ремнём со стальным тросом, ручкой-гранатой, новыми часами со взрывателем и машиной BMW Z3. Также он отдает Бонду билет авиакомпании British Airways маршрутом Лондон — Санкт-Петербург. В это время в Санкт-Петербурге проходит совет Министерства обороны Российской Федерации во главе с министром обороны Дмитрием Мишкиным. На нём выступает Урумов — командующий Космическими Войсками России. Он говорит, что это нападение осуществили сибирские сепаратисты. Но Мишкин считает,что было бы опрометчиво обвинять во всём этом сибирских сепаратистов, так как двое программистов — уже упоминавшаяся Наталья Симонова и Борис Грищенко — спаслись. Урумов заявляет, что готов начать расследование. Тем временем Бонда в аэропорту Пулково встречает резидент ЦРУ в России Джек Уэйд. Он выводит 007 на российского мафиози, бывшего агента КГБ Валентина Дмитриевича Жуковского, которому Бонд когда-то прострелил ногу. Тот считает, что нападение с «Золотого глаза» — дело рук его соперников-мафиози из группировки «Янус». Агент 007 заключает с Жуковским выгодную для него сделку, его выводят на «Янус». Ксения Онатопп приводит Бонда на свалку. В это время в Санкт-Петербург приезжает Наталья Фёдоровна Симонова, выжившая программистка с «Северной». Она связывается с Грищенко, тот приводит её в собор Смоленской Божьей Матери, где её хватают люди «Януса». Тем временем Бонд на кладбище встречается с главой «Януса», которым оказывается бывший агент 006 МИ-6 и бывший друг Бонда — Алек Травельян. Он рассказывает, как сымитировал свою смерть в Архангельске, после чего вступил в русскую мафию. Но взрыв бомб на химическом заводе, таймеры которых Бонд перевёл с 6 до 3 минут, сильно повредил его лицо. Они разговаривают, после чего Травельян усыпляет Бонда, сажает его связанного вместе с Симоновой в вертолёт-невидимку и запускают ракеты вертолёта по нему самому. Бонду удаётся катапультироваться вместе с Симоновой из вертолёта и выжить. Но в это время на кладбище приезжают военные и арестовывают обоих. Они доставляют их в следственный изолятор. Там Бонд пытается познакомиться с Натальей. Затем их допрашивает министр обороны, но затем приходит Урумов и из пистолета Бонда убивает конвоиров и Дмитрия Мишкина. Затем он хочет застрелить из своего оружия Бонда, но тот нападает на Урумова, и вместе с Натальей пытается сбежать, но солдаты ловят Симонову и сажают в «Волгу» Урумова. Тот едет к Травельяну, но его пытается догнать Бонд, угнавший танк Т-54/55. Он едет на танке по Санкт-Петербургу, разрушая всё вокруг, его пытаются остановить военные и милиционеры, но тщетно. В итоге Урумов привозит Симонову в бронепоезд Травельяна, поезд начинает ехать, но агент 007 выстрелом из танка останавливает его. Бонд заходит в покорёженный от столкновения с танком поезд, где берёт на прицел Травельяна. Но Урумов угрожает пистолетом Наталье, и Бонд убивает Урумова, дав время Ксении и Алеку сбежать и закрыть двери. Они улетают на вертолёте и взрывают поезд, но Джеймсу и Наталье всё же удаётся спастись, и кроме того, им удалось узнать местоположение базы Травельяна — на Кубе.

Бонд и Наталья отправляются на Кубу, где им вновь помогает резидент ЦРУ Джек Уэйд. Бонд и Наталья на подаренном Уэйдом самолёте прочёсывают Кубу, но не могут найти станцию врага, пока их не сбивает ракета. Они выживают, но их хочет добить Ксения Онатопп. Бонд убивает её и сбивает вертолёт врага. Затем они находят спрятанную в озере космическую станцию Травельяна. Вдвоём они проникают на станцию, где их ловят люди Алека. Тревельян рассказывает им о своём плане — перевести все деньги с лондонских банков на свой счёт и затем ударить при помощи спутника электромагнитным импульсом по Лондону, вызвав падение биржи и новую Великую Депрессию. Однако Наталья успела помешать Алеку, сменив коды доступа — спутник «Золотой глаз» сгорает в слоях атмосферы. Бонд взрывает ручку-гранату, ломает антенну станции и скидывает Алека с большой высоты. Затем взрывается станция, при этом погибает Борис Грищенко (После разрушения базы он встаёт и ликует о своей непобедимости, но тут же на него выливается жидкий азот). Джеймс и Наталья спасаются. Дальше их находят люди Уэйда и забирают на базу в Гуантанамо.

вторник, 14 февраля 2012 г.

Многострочный текст в SVG

Понадобился. И оказалось, что SVG не умеет переносить текст! К счастью, в моём случае SVG создаётся при помощи python, а там не очень сложно написать функцию, разбивающую текст на строки длиной не больше заданного количества символов (главное, чтобы резала по пробелам):
def TextToLines(txt, maxLen):

result = []

lines0 = txt.split("\n")
for line in lines0:
chunks = line.split(" ")
newline, l = "", 0
for chunk in chunks:
if newline != "":
l = 1
else:
l = 0
if len(newline.decode("UTF-8")) + l + len(chunk.decode("UTF-8")) > maxLen:
result.append(newline)
newline = ""
if newline != "":
newline += " "
newline += chunk
if newline != "":
result.append(newline)

return result

Соответственно, эта процедура применяется так:
textLines = TextToLines(мойТекст, 66)
innerText, y = "", 15
for line in textLines:
innerText += """<text x="10" y="%(y)d" font-size="10pt" fill="black">%(text)s</text>""" % {"text":line, "y":y}
y += 15

Ну и дальше результат из innerText вставляем в нужное место формирующегося документа.

Не очень изящно, скажем прямо. Может, со временем найдется решение получше.

суббота, 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-)



вторник, 24 января 2012 г.

Путь к папке "Мои документы" содержит недопустимый символ.

Windows Installer ругался, по-видимому, из-за того, что когда-то профиль пользователя перенесли на другой диск. Вот тут сказано прошерстить реестр на предмет кривых путей до папки "Мои документы". Кривой путь нашелся в ветке реестра: HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Пришлось его там поправить, а потом перезапустить процесс explorer.exe

четверг, 19 января 2012 г.

Избавиться от предложения сжать сообщения Outlook Express

Попалась хорошая статья. Вкратце - обнулить параметр Compact Check Count реестра:
HKEY_CURRENT_USER\Identities\{GUID}\Software\Microsoft\Outlook Express\5.0\
(он инкрементируется при каждом закрытии почтовика и по достижении значения 100 начинает выдавать это сообщение)

вторник, 10 января 2012 г.

Как создать гаджет под Windows 7

На самом деле всё подробно расписано здесь. Я даже сподобился написать простенький таймер ^_^

Оказалось, что эти самые гаджеты в простейшем случае представляют из себя обычные приложения html. Это самое приложение зазиповано в архив (только расширение у этого архива - не .zip, а .gadget) В архиве должен быть файл gadget.xml примерно такого вида:
<?xml version="1.0" encoding="utf-8" ?>
<gadget>
<name>funTimer</name>
<namespace>CnRuFeed.Gadgets</namespace>
<version>0.0.0.1</version>
<author name="huh-muh">
<info url="huh-muh.blogspot.com" />
</author>
<copyright>&#169; 2012</copyright>
<description>Timer test</description>
<hosts>
<host name="sidebar">
<base type="HTML" apiVersion="1.0.0" src="funTimer.htm" />
<permissions>Full</permissions>
<platform minPlatformVersion="1.0" />
</host>
</hosts>
</gadget>

Соответственно, всё самое интересное заключено в файле funTimer.htm:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Fun timer</title>
</head>


<script type="text/javascript">
function resizeGadget()
{
if(System.Gadget.docked == true)
{
mainBody.style.width = 250;
mainBody.style.height = 80;
}
else
{
mainBody.style.width = 250;
mainBody.style.height = 80;
}
}

function settingsClosed(event)
{
if(event.closeAction == event.Action.commit)
{
loadSettings();
}
}

var initDate;

function loadSettings()
{
var caption = System.Gadget.Settings.read("caption");
captionLabel.innerHTML = (caption != "") ? caption : "(укажите название таймера)";

var initValue = System.Gadget.Settings.read("init");
initValue = initValue.replace(new RegExp(" ",'g'),".").replace(new RegExp(":",'g'),".");
var dateParts = initValue.split(".");
if (dateParts.length == 5)
initDate = new Date(dateParts[2], (dateParts[1] - 1), dateParts[0], dateParts[3], dateParts[4]);
else
if (dateParts.length == 3)
initDate = new Date(dateParts[2], (dateParts[1] - 1), dateParts[0]);
else
initDate = new Date();
timerLabel.innerHTML = "" + initDate;
}


var progressTimer;
function timerTick()
{
var currDate = new Date();
var dateDiff = Math.abs(currDate.getTime() - initDate.getTime()) / 1000;
var dd = Math.floor(dateDiff / (24*3600));
var hh = Math.floor( (dateDiff - dd*24*3600) / 3600 );
var mm = Math.floor( (dateDiff - dd*24*3600 - hh*3600) / 60 );
var ss = Math.floor( dateDiff - dd*24*3600 - hh*3600 - mm*60 );
timerLabel.innerHTML = "" + dd + "д " + hh + "ч " + mm + "м " + ss + "с";
progressTimer = setTimeout("timerTick()", 1000);
}


document.onreadystatechange = function()
{
if(document.readyState=="complete")
{
System.Gadget.settingsUI = "Settings.htm";
System.Gadget.onSettingsClosed = settingsClosed;
System.Gadget.onUndock = resizeGadget;
System.Gadget.onDock = resizeGadget;
loadSettings();
progressTimer = setTimeout("timerTick()", 1000);
}
}

</script>


<body id="mainBody">
<center><g:text id="captionLabel" width="100%" align="center">Loading...</g:text>
<br/>
<g:text id="timerLabel" width="100%" align="center">Loading...</g:text></center>
</body>

</html>

Для редактирования настроек программы будет использоваться файл Settings.htm:
<html xmlns="http://www.w3.org/1999/xhtml">

<script type="text/javascript">

document.onreadystatechange = function()
{
if(document.readyState=="complete")
{
mainBody.style.width = 300;
mainBody.style.height = 180;
captionBox.value = System.Gadget.Settings.read("caption");
timerBox.value = System.Gadget.Settings.read("init");
}
}

System.Gadget.onSettingsClosing = function(event)
{
if (event.closeAction == event.Action.commit)
{
System.Gadget.Settings.write("caption", captionBox.value);
System.Gadget.Settings.write("init", timerBox.value);
event.cancel = false;
}
}

</script>

<body id="mainBody">
Название таймера:
<br />
<input id="captionBox" type="text" maxlength="50" width="100%" />
<br />
Точка отсчета (дд.мм.гггг[ чч:мм]):
<br />
<input id="timerBox" type="text" maxlength="16" width="100%" />
</body>
</html>

вторник, 3 января 2012 г.

Взаимодействие PHP и MSSQL - новые веяния

Поразительный факт: подкупающие своей привычностью функции mssql_connect, mssql_query и т.п. канули в небытие начиная с версии 5.3.0, по крайней мере в варианте для windows.

Однако, как выяснилось, Microsoft в беде не оставит! Есть вполне работоспособный драйвер версии аж 2.0.1. Для него, правда, требуется нативный клиент SQL Server 2008, но его тоже легко можно скачать по ссылке вот с этой страницы (если, конечно, удастся нужную ссылку на этой странице отыскать ^_^).

Единственная тонкость: драйвер представляет из себя набор dll-ек "на все случаи жизни", и требуется выбрать из них нужную. Перебором получилось прописать в php.ini, закинув предварительно в папку расширений соответствующий файл:
extension=php_pdo_sqlsrv_53_ts_vc9.dll
что, видимо намекает на то, что PHP 5.3.1 компилировался при помощи Visual C++ 9...

В общем, минимально рабочий код оказался таким (лёгкая вариация примера из хелповника, поставляемого вместе с драйвером):
/* Указываем параметры поключения: имя сервера, логин-пароль и базу. */
$serverName = "myserver.mydomain.ru";
$connectionInfo = array( "UID"=>"uid",
"PWD"=>"pwd",
"Database"=>"AdventureWorks");

/* Подключаемся к серверу используя аутентификацию SQL Server. */
$conn = sqlsrv_connect( $serverName, $connectionInfo);
if( $conn === false )
{
echo "Unable to connect.";
die( print_r( sqlsrv_errors(), true));
}

/* Выполняем SQL-запрос. */
$tsql = "SELECT getdate()";
$stmt = sqlsrv_query( $conn, $tsql);
if( $stmt === false )
{
echo "Error in executing query.";
die( print_r( sqlsrv_errors(), true));
}

/* Получаем результаты запроса и выводим их на экран (казалось бы). */
// $row = sqlsrv_fetch_array($stmt);
// echo "Server date: ".$row[0];
// не проканало с ошибкой Object of class DateTime could not be converted to string

/* Получаем результаты запроса и выводим их на экран - а вот так проканало. */
if (sqlsrv_fetch($stmt);)
echo "Server date: ".sqlsrv_get_field( $stmt, 0, SQLSRV_PHPTYPE_STRING( SQLSRV_ENC_CHAR) );

/* Освобождаем ресурсы, выделенные для запроса и подключения. */
sqlsrv_free_stmt( $stmt);
sqlsrv_close( $conn);


В первом приближении, правда, натолкнулся на ошибки, связанные с трудностью преобразования типа DateTime к строке (отсюда и закомментированный код), но начало положено...