#target "Indesign"
/** CompactTXT - Скрипт для заполнения выбранных фреймов текстом.
    Пригодится в случае необходимости занять всё выделенное пространство,
    когда кегль не имеет принципиального значения.
    Автоматически растягивает/уменьшает размер шрифта в фрейме  заполняя доступное пространство.
    Работает в том числе со связанными фреймами.
    Рекомендую назначить на горячую клавишу ALT+C -
    она прекрасно коррелирует с комбинацией CTRL+ALT+C
*/
/**  Copyright (c) 2008-2016, Eugeny Borisov. All rights reserved. */
const CompactTXTerrorMessages = {
    StoryOverflowControl_MinTextPoinSize:  "Ошибка. Текст получается\nменьше минимально допустимого размера.",
    StoryOverflowControl_MaxTextPointSize: "Ошибка. Текст получается\nмаксимально допустимого размера.",
    StoryOverflowControl_InfinityLoop:     "Ошибка. Не получается разместить текст в фрейм - идёт бесконечный цикл"
}
const c_myApp = app;
const c_myDoc = c_myApp.activeDocument;
const c_MinTextPointSize = 0;
const c_MaxTextPointSize = 65536;
const c_InitPointSize = 9;
const c_Max_loopsCount = 999;
var c_loopsCount = 0;
var c_currSelect = c_myDoc.selection;
var c_selCount = c_myDoc.selection.length;
var c_objects2Work = Array();
/** Добавляет элемент в массив, если он уникален */
Array.prototype.addunique = function(element) {
        var len = this.length;
        while (len--) {
            if (this[len] == element) return;
        }
        this.push(element);
    }
    /** Вызывает callback-функцию для каждого элемента массива */
Array.prototype.map = function(callback) {
        var len = this.length;
        while (len--) {
            callback(this[len]);
        }
    }
    /** Добавляет объект в массив, если он уникален */
Object.prototype.addToArrayIfUnique = function(targetArray) {
    targetArray.addunique(this);
}
/** c_objects2Work -- массив объектов с которыми нужно работать */
function CalcObjectForHandle(inputObject) {
    switch (String(inputObject)) {
        case "[object TextFrame]":
        case "[object Text]":
        case '[object InsertionPoint]':
        case '[object Character]':
        case '[object Word]':
        case '[object Line]':
        case '[object Paragraph]':
        case '[object TextColumn]':
            inputObject = inputObject.parentStory;
        case "[object Story]":
            inputObject.addToArrayIfUnique(c_objects2Work);
            break;
        case '[object Group]':
            inputObject.allPageItems.everyItem().addToArrayIfUnique(c_objects2Work);
            break;
        default:
            break;
    }
}
/* Меняет значение на указанный процент с точностью в пять сотых */
function calcNewSize(oldSize, percent) {
    var newSize = oldSize * percent;
    if (newSize > 5) newSize = Math.round(newSize * 20) / 20;
    return newSize;
}
/** Функция, изменяющая размер текста, на указанную единицу (в процентах), пока не поменяется статус overflow
    если процент отрицательный - то ждем overflow==false, если положительный - true,
    Использует значения MinTextPoinSize и c_MaxTextPointSize.
    При выходе за их рамки функция генерирует исключения 'StoryOverflowControl_MinTextPoinSize' и 'StoryOverflowControl_MaxTextPointSize'
    При percent = 0 генерирует исключение 'StoryOverflowControl_zeroPercent';
*/
function StoryOverflowControl(inpStory, percent) {
    percent = (percent / 100) + 1;
    var newLeadingSize;
    if (percent == 1) throw ('Ошибка в ДНК программиста');
    var waitForOverflow = (percent > 1) ? true : false;
    if (inpStory.overflows == waitForOverflow) return true;
    var newPointSize = calcNewSize(inpStory.pointSize, percent);
    newLeadingSize = (inpStory.leading == Leading.auto) ?
        newLeadingSize = Leading.auto : calcNewSize(inpStory.leading, percent);
    var oldSize = newPointSize;
    /** основные "качели" - цил до изменения состояния Overflow */
    while (1) {
        inpStory.pointSize = newPointSize;
        if (newLeadingSize != Leading.auto) {
            inpStory.leading = newLeadingSize;
        }
        if (percent < 1) {
            inpStory.pointSize = newPointSize;
            inpStory.leading = newLeadingSize;
        }
        if (inpStory.overflows == waitForOverflow) return true;
        newPointSize = calcNewSize(inpStory.pointSize, percent);
        if (newLeadingSize != Leading.auto) {
            newLeadingSize = calcNewSize(inpStory.leading, percent);
        }
        if (oldSize == newPointSize) break;
        oldSize = newPointSize;
        c_loopsCount++;
        if (c_loopsCount == c_Max_loopsCount) throw (CompactTXTerrorMessages.StoryOverflowControl_InfinityLoop);
        if (inpStory.pointSize < c_MinTextPointSize) throw (CompactTXTerrorMessages.StoryOverflowControl_MinTextPoinSize);
        if (inpStory.pointSize > c_MaxTextPointSize) throw (CompactTXTerrorMessages.StoryOverflowControl_MaxTextPointSize);
    }
    return false;
}
/** обработка одного текста */
function HandleStory(inputSory) {
    c_loopsCount = 0;
    if (inputSory.leading != Leading.auto) {
        var newLeading = c_InitPointSize * inputSory.leading / inputSory.pointSize;
        inputSory.leading = newLeading;
    }
    inputSory.pointSize = c_InitPointSize;
    StoryOverflowControl(inputSory, +100);
    StoryOverflowControl(inputSory, -50);
    StoryOverflowControl(inputSory, +25);
    StoryOverflowControl(inputSory, -10);
    StoryOverflowControl(inputSory, +5);
    StoryOverflowControl(inputSory, -2.5);
    StoryOverflowControl(inputSory, +1);
    StoryOverflowControl(inputSory, -0.5);
}
/** головная функция */
function main() {
    try {
        c_currSelect.map(CalcObjectForHandle);
        c_objects2Work.map(HandleStory);
    } catch (err) {
        alert(err);
        exit();
    }
}
/** Возможность инклюда этого скрипта.
    Для того, что бы не выполнялась головная функция необходимо объявить переменную runCompactTXT и установить ей значение false */
try {
    runCompactTXT == undefined;
} catch (err) {
    runCompactTXT = true;
}
if (runCompactTXT) {
    app.doScript(main, ScriptLanguage.JAVASCRIPT, [], UndoModes.FAST_ENTIRE_SCRIPT, 'compactTXT');
}