Think.JS №00

  • Для начинающих: Скрипт «Hello, world!»
  • Начальное ускорение: Примем исходное положение
  • Учим матчасть: Что такое объекты и зачем они в скриптах
  • Полезный скрипт: Машина времени

Содержание

Для начинающих: Скрипт «Hello, world!»

Начальное ускорение: Примем исходное положение

Учим матчасть: Что такое объекты и зачем они в скриптах

Полезный скрипт: Машина времени

Для начинающих

Скрипт «Hello, world!»

Традиционно изучение языка программирования начинается с написания программы, генерирующей «Hello, world!» на стандартном выводе. Не будем нарушать традицию и напишем скрипт, который создаст новый документ и на первой странице поместит текстовый фрейм с этой надписью.

helloWord.jsx
Самый первый скрипт
with (app) {
  var myDoc = documents.add();
  var myPage = myDoc.pages[0];
  var myTextFrame = myPage.textFrames.add();
  with (myTextFrame) {
    geometricBounds = myPage.bounds;
    contents = "Hello, world!";
  }
}

Сохраните текст скрипта в папку скриптов («C:\Program Files\Adobe\Adobe InDesign CS2\Presets\Scripts» по умолчанию) под именем helloWorld.jsx, откройте InDesign и запустите скрипт.

Теперь разберем, что именно делает скрипт.

Cкрипт начинается с оператора «with (app) {}«. В JavaScript этот оператор означает, что все действия внутри фигурных скобок будут относится к объекту, указанному в круглых скобках. У нас это объект app (от application) — собственно InDesign. Оператор with рекомендуется использовать для упрощения записи к свойствам и методам объектов, а также для повышения читабельности программ.

Вторая строка «var myDoc = documents.add();» создает новый документ в коллекции документов (documents.add()) и передает в переменную myDoc
(var — variable) ссылку на него. Теперь для обращения к самому документу достаточно сослаться на переменную myDoc. Заметим сразу, что myDoc указывает именно на созданный документ, а не на активный. Если мы после объявления переменной добавим в коллекцию документов еще несколько, то переменная myDoc все равно будет указывать на тот документ, ссылку на который мы передали переменной при ее создании. К этому документу можно будет обратиться даже в том случае, когда на экране отображается совершенно другой документ.

Третья строка «var myPage = myDoc.pages[0];» передает в переменную myPage ссылку на первую страницу документа myDoc. Есть два варианта обращения к объектам в коллекции (например, к конкретной странице в коллекции страниц документа). Первый следует использовать тогда, когда вы точно знаете номер объекта в коллекции. В этом случае к объекту обращаются по его порядковому номеру, указанному в квадратных скобках — стандартное обращение к элементу массива в JavaScript. Следует заметить два момента: во-первых, квадратные скобки не отделяются точкой; во-вторых, нумерация элементов в массиве всегда начинается с ноля, то есть первому элементу соответствует индекс 0, второму — 1 и т.д. Второй метод можно использовать тогда, когда вы точно знаете имя нужного объекта и это имя является уникальным. В документах InDesign страницы имеют имена (о них речь пойдет позже) соответствующие, как правило, но не всегда, порядковым номерам. Поэтому обратиться к первой странице документа можно было следующим образом: «var myPage = myDoc.pages.item("1");». Запись «item(«1»)» означает, что мы обращаемся к объекту в коллекции, имеющему имя «1» (поскольку имя — это всегда строковая переменная, используются кавычки). Заметим, что имена страниц, если не указанодругое в свойствах нумерации, начинаются с единицы, а не с ноля. В нашем случае применим только первый способ обращения к странице документа, поскольку точно известно, что в документе есть хотя бы одна страница (нельзя создать документ без страниц или удалить единственную).

В четвертой строке «var myTextFrame = myPage.textFrames.add();» скрипт создает на странице myPage новый текстовый фрейм и передает ссылку на него в переменную myTextFrame. Вообще, очень многие объекты InDesign создаются при помощи метода add(). Но не все, и это тоже важно запомнить.

Начиная с пятой строки «with (myTextFrame) {}» скрипт работает с созданным текстовым фреймом. В шестой строке «geometricBounds = myPage.bounds;» мы указываем, что геометрические размеры (geometricBounds) текстового фрейма должны соответствовать размеру страницы myPage (bounds). О специфике определения размеров мы поговорим позже. В седьмой строке «contents = "Hello, world!«;» мы задаем значение свойству contents (содержимое) текстового фрейма. Любой объект InDesign, содержащий текст, имеет такое свойство.

В следующий раз мы поговорим о том, как передавать в скрипт данные, введенные пользователем, как перемещать текстовый фрейм, как менять его размеры.

Начальное ускорение

Примем исходное положение

Каков стандартный метод изменения свойств объектов, например, класса pageItems? Сначала объект создается методом add (), затем с использованием оператора with (myObject) {} его свойствам присваиваются некие значения. Путь вполне оправданный и проверенный, но не всегда удобный и, главное, не самый быстрый. Дело в том, что каждое изменение объекта занимает некоторое время, сохраняясь при этом в списке Undo. Есть альтернативный способ, с помощью которого можно создать объект с заранее определенными свойствами.

addFormattedObject.jsx
Скрипт демонстрирует возможность создания форматированного документа
with (app) {
  var myDoc = documents.add();
  var myPage = myDoc.pages[0];
  var myRectangle = myPage.rectangles.add({
    geometricBounds: [0, 0, 100,100],
    cornerEffect: CornerEffects.roundedCorner,
    cornerRadius: 10,
    fillColor: "Black",
    fillTint: 25,
    strokeColor: "Black",
    strokeTint: 80,
    strokeWeight: 2
    });
}

Как видим, на этапе создания объекта типа rectangle были заданы 8 свойств. Если бы мы изменяли свойства после создания объекта, то потратили бы время на дополнительных 8 операций. А если требуется создавать десятки или сотни объектов с разными свойствами, то потери времени могут измеряться минутами.

Общий формат записи задания свойств при создании объекта следующий: «add({свойство_1: значение, свойство_2: значение, … свойство_N: значение})«. Для наглядности можно писать каждое свойство в отдельной строке, не забывая про формат записи.

Особо следует отметить, что подобный метод быстрого задания свойств на стадии создания объекта можно применять не только к объектам класса pageItems, но и к стилям (абзаца, знака, объекта, печати), что значительно сократит время работы скрипта.

Учим матчасть

Что такое объекты и зачем они в скриптах

Оговорюсь сразу: настоящей поддержки объектно-ориентированного программирования в JavaScript не реализовано. Поэтому те, кто знаком с С++ могут даже не спрашивать о наследовании и полиморфизме. Из главных преимуществ ООП в JavaScript доступна только инкапсуляция. Те, кому эти слова кажутся непонятными, советую обратиться к специальной литературе по объектно-ориентированному программированию. Быть может, станет ясно, что на практике мы использовали принципы ООП о том вовсе не подозревая, как герой Мольера всю жизнь говорил прозой и не знал об этом.

Начнем с модификации объектов, относящихся к реализации JavaScript в InDesign, а имено с массивов.

Мне всегда очень не нравилось, что к первому и последнему элементу стандартного массива нельзя обратиться также изящно, как, например, к первому и последнему текстовому фрейму в цепочке: «myStory.textFrames.firstItem(); myStory.textFrames.lastItem();«. Приходиться делать обращения, особо неуклюжие в отношении последнего элемента массива: «myArray[0]; myArray[myArray.length - 1]«. Поэтому, когда я узнал о том, как модифицируются объекты, в первую очередь реализовал для объекта Array два собственных метода: firstItem() и lastItem(), и теперь использую этот код во всех скриптах, которые работают с массивами (а работают с ними большинство скриптов).

arrayMethods.jsx
Методы объекта Array
Array.prototype.firstItem = function () {
  return this[0];
}

Array.prototype.lastItem = function () {
  return this[this.length - 1];
}

Определение методов желательно делать не в блоке with (app) {}, поскольку методы могут понадобиться в функциях, объявляемых вне этого блока. Общий вид определения метода таков: «Объект.prototype.имяМетода = function (списокПараметров) {}«. Ключевым здесь является ссылка на прототип (prototype) объекта. Заметим сразу, что не все объекты имеют прототип, поэтому не со всеми объектами можно обращаться так вольно. Но и на такие объекты мы найдем свои методы.

Служебное слово this обозначает ссылку на конкретный экземпляр данного типа. Допустим, если мы вызываем myArray.lastItem(), то this в этом случае указывает на myArray (который и является экземпляром типа Array).

Хотелось бы отметить один момент, который может стать причиной ошибки. Название метода при объявлении следует использовать, в отличие от функции, без круглых скобок. Иначе интерпретатор завершит работу выполнения скрипта по ошибке.

В самом скрипте теперь можно использовать определенные ранее методы следующим образом:

var myArray = ["один", "два", "три", "четыре"];

alert(myArray.lastItem());

В результате выполнения этих строк скрипт сообщит значение последнего элемента массива myArray, в данном случае «четыре».

Полезный скрипт

Машина времени

Периодически подаются запросы с мест на скрипт, который умеет в смещать время передач в телепрограмме на указанное количество часов. Такой скрипт есть, причем, по моему мнению,он хорошо подходит для демонстрации преобразования типов в JavaScript.

tvTime.jsx
Скрипт для «перевода» времени начала телепередачи на определенное количество часов
var langAlertNoDoc = localize({en: "No documents are open!", ru: "Нет открытых документов!"});
var langAlertNoText = localize({en: "No texts are selected!", ru: "Не выбран текст!"});

var iniIncrease = 4;
var iniTimeDivider = ".";

function increase (myTime) {
  var myArr = myTime.split(iniTimeDivider);
  myHour = Number(myArr[0]) + iniIncrease;
  if (myHour > 24) {
    myHour -= 24;
  }
  if (myHour < 0 ) {
    myHour = 24 + myHour;
  }
  if (String(myHour).length < 2) {
    myHour = "0" + String(myHour);
  }
  var myResult = String(myHour) + iniTimeDivider + myArr[1];
  return myResult;
}

with (app) {
  try {
    var myDoc = activeDocument;
  } catch (error) {
    alert(langAlertNoDoc);
    exit();
  }
  try {
    var myStory = selection[0].parentStory;
  } catch (error) {
    alert(langAlertNoText);
    exit();    
  }
  var myFindString = "^9^9" + iniTimeDivider + "^9^9";
  findPreferences = null;
  changePreferences = null;
  var myTimes = myStory.search(myFindString);
  myTimes.reverse();
  for (var myCounter = 0; myCounter < myTimes.length; myCounter++) {
    myTimes[myCounter].contents = increase(myTimes[myCounter].contents);
  }
  findPreferences = null;
  changePreferences = null;  
}

Предполагается, что время передачи указывается в формате 00.00, причем разделитель между часами и минутами указывается в переменной iniTimeDivider и может быть любой строкой.

Реализация скрипта минималистична. В двух блоках try {} … catch() {} определяется активный документ и выделенный текст. При отсутствии таковых, скрипт выходит с предупреждением о несоответствии условий. Далее формируется строка поиска, которая ищется в выбранном тексте. Результат поиска после необходимой реверсии обрабатывается функцией increase, которую стоит рассмотреть подробно.

В первую очередь из общей строки времени следует получить значение часов. Это делается встроенной функцией объекта String.join(divider). Функция join() «режет» строку на несколько частей в соответствии с указанным разделителем divider (сам разделитель удаляется), возвращая массив подстрок. В результате получится массив, первый элемент которого будет содержать значение часов, а второй — минут. Следует учитывать, что элементы в массиве — строковые, потому значение часов нужно перевести в числовой формат явно указав Number(myArr[0]). Иначе в результате сложения с указанным числом увеличения произойдет неявное преобразование типа Number в String, и вместо, например, 10 + 4 = 14 мы получим «10» + «4» = «104». Полученное число следует проверить на допустимость, поскольку при увеличении времени значение может «перепрыгнуть» за 24 часа, а при уменьшении — через 0. В первом случае от значения следует отнять 24, во втором — к 24 прибавить получившееся значение (поскольку значение меньше нуля, в действительности отнимем модуль значения). Еще одна проверка требуется для соблюдения формата — если при преобразовании получившегося значения в текстовый формат его длина меньше 2 символов, то впереди добавляется 0. Для возвращения отредактированного времени телепередачи собирается строка из полученного нового значения времени, разделителя и числа минут, хранящегося во втором элементе массива.


Олег Бутрин
THINK.JS выпуск № 0 от 2006-10-23

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

Connecting to %s