Scripts collection
Discuss and announce AkelPad plugins
- Author
- Message
-
Offline
- Posts: 1291
- Joined: Thu Nov 16, 2006 11:53 am
- Location: Kyiv, Ukraine
Поскольку под AkelPad такого ещё не было, выкладываю прототип GoToAnything (переход к файлу/строке в файле/поиск текста) для AkelPad. Вдохновлено угадайте каким редактором, в связи с чем предлагается горячая клавиша Ctrl+P.
Начиная с версии 0.4, скрипт поддерживает Избранное (Favourites) и Историю Открытия Файлов (Recent Files History).
GoToAnything.js
Скрипт уже увеличился настолько, что не влазит в сообщение. Поэтому скачайте его тут:
https://github.com/d0vgan/AkelPad-Scrip ... in/Scripts
Давайте обсуждать скрипт в соседней теме:
http://akelpad.sourceforge.net/forum/vi ... php?t=2151
Начиная с версии 0.4, скрипт поддерживает Избранное (Favourites) и Историю Открытия Файлов (Recent Files History).
GoToAnything.js
Скрипт уже увеличился настолько, что не влазит в сообщение. Поэтому скачайте его тут:
https://github.com/d0vgan/AkelPad-Scrip ... in/Scripts
Давайте обсуждать скрипт в соседней теме:
http://akelpad.sourceforge.net/forum/vi ... php?t=2151
-
Offline
- Posts: 40
- Joined: Thu May 05, 2022 5:38 am
Меню для цветовых тем.
Code: Select all
// http://akelpad.sourceforge.net/forum/viewtopic.php?p=35922#p35922
// Version: 1.5 (2023.05.07)
// Author: dothen
//
// *** Menu for color themes. ***
//
// Required to include: ShowMenu.js
//
// Usage:
// "ColorThemeMenu" Call("Scripts::Main", 1, "ColorThemeMenu.js") Icon("%a\AkelFiles\Plugs\Coder.dll", 5)
// "ColorThemeMenu" Call("Scripts::Main", 1, "ColorThemeMenu.js", `-posx=%bl -posy=%bb -mode=1`) Icon("%a\AkelFiles\Plugs\Coder.dll", 5)
//
// Аргументы:
// -posx -posy Координаты позиции меню (по умолчанию координаты курсора мыши),
// %bl и %bb переменные плагина ToolBar, он вставит на их место координаты кнопки.
// -mode 1 - Упрямое меню. 0 - Нормальное меню (по умолчанию 0).
//
// Описание:
// Выгружает на диск встроенные темы добавляя набор переменных от Infocatcher
// (https://github.com/Infocatcher/AkelPad_coder/blob/master/_colors.txt).
// Выгружает на диск пользовательские темы.
// Загружает с диска пользовательские темы.
// Выгрузка тем выполняется в папки \Coder\themes\user\ и \Coder\themes\internal\ с перезаписью файлов.
// Файлы с темами для загрузки в AkelPad надо сложить в папку \Coder\themes\
// Править и удалять темы можно через диалог настроек плагина Coder.
// Меню показывает пользовательские темы и файлы цветовых тем.
// Скрипт можно использовать как дополнение к PluginText.js от KDJ
// (http://akelpad.sourceforge.net/files/plugs/Scripts/KDJ.zip)
//
// Ctrl + Shift + LClick в меню удаляет выбранную тему из Coder.ini без подтверждения.
// Ctrl + LClick в подменю "Импорт" открывает выбранный файл для редактирования.
// Подсветка файлов с темами:
//
//Files:
//*.akel-theme
//
//QuotesRE:
//0 '^[^\s]+ "#([A-F\d]{6}|[A-F\d]{3})"$' "\0=(0,\1,0)" 0 0
//Syntax
if (!GetSyntaxFile()) WScript.Quit();
//Include
if (!AkelPad.Include("ShowMenu.js")) WScript.Quit();
//HighLight
if (!AkelPad.IsPluginRunning("Coder::HighLight"))
if (AkelPad.Call("Coder::HighLight") == -1 /*UD_FAILED*/) WScript.Quit();
//Variables
var oSys = AkelPad.SystemFunction();
var oSet = AkelPad.ScriptSettings();
var oFSO = new ActiveXObject("Scripting.FileSystemObject");
var sDirThemes = AkelPad.GetAkelDir(4) + "\\Coder\\themes\\";
var sDirThemesI = sDirThemes + "internal\\";
var sDirThemesU = sDirThemes + "user\\";
var sFileExt = ".akel-theme";
var sCurThemeName;
var aFileList = GetFileList();
var aThemeList = GetThemeList();
var nPosMenuX;
var nPosMenuY;
var nRunAgain;
var bUpdate = false;
var nPosMenuX = AkelPad.GetArgValue("posx", POS_CURSOR)
var nPosMenuY = AkelPad.GetArgValue("posy", POS_CURSOR)
var nRunAgain = AkelPad.GetArgValue("mode", 0)
while (RunMenu())
{
if (bUpdate)
{
aThemeList.splice(0,aThemeList.length);
aThemeList = GetThemeList();
bUpdate = false;
}
WScript.Sleep(100);
}
function RunMenu()
{
var aItems = [];
var nChecked = 0;
var nFileListLength = aFileList.length;
var nThemeListLength = aThemeList.length;
var nDisabled = (nFileListLength == 0) ? MF_DISABLED | MF_GRAYED : MF_NORMAL;
var i = -1;
var c = 0;
sCurThemeName = GetCurTheme();
for(c = 0; c < nThemeListLength; c++) // Список тем
{
nChecked = (aThemeList[c] == sCurThemeName) ? MF_NORMAL | MF_CHECKED | MF_USECHECKBITMAPS : MF_NORMAL;
aItems[++i] = [aThemeList[c], nChecked, i];
}
aItems[++i] = ["", MF_SEPARATOR];
aItems[++i] = ["Экспорт", MF_SUBMENU];
aItems[++i] = ["Выгрузить свои темы", MF_NORMAL, i];
aItems[++i] = ["Выгрузить встроенные темы", MF_NORMAL | MF_LAST, i];
aItems[++i] = ["Импорт", MF_SUBMENU];
for(c = 0; c < nFileListLength; c++) // Список файлов
aItems[++i] = [aFileList[c], MF_NORMAL, i];
aItems[++i] = ["", MF_SEPARATOR];
aItems[++i] = ["Загрузить все темы", nDisabled | MF_LAST, i];
aItems[++i] = ["", MF_SEPARATOR];
aItems[++i] = ["Настроить...", MF_NORMAL, i];
//Show menu
var nItem = ShowMenu(aItems, nPosMenuX, nPosMenuY);
if (nItem == -1)
return false; // Ничего не выбрано.
if (nItem < nThemeListLength)
{
if (Ctrl() && Shift())
{
DeleteColorTheme(aThemeList[nItem]); // Удалить выбранную из меню цветовую тему.
bUpdate = true;
}
else if (!Ctrl() && !Shift())
ActivateColorTheme(aThemeList[nItem]); // Активировать выбранную из меню цветовую тему.
return nRunAgain;
}
if (nItem == nThemeListLength + 2)
{
ExportUserThemes(); // Выгрузить пользовательские темы в папку \Coder\themes\user\
return false;
}
if (nItem == nThemeListLength + 3)
{
ExportInternalThemes(); // Выгрузить встроенные темы в папку \Coder\themes\internal\
return false;
}
if ((nItem > nThemeListLength + 3) && (nItem < i - 3) )
{
if (Ctrl() && !Shift())
{
AkelPad.OpenFile(sDirThemes + aItems[nItem][0]); // Открыть выбранный файл для редактирования.
return false;
}
if (!(Ctrl() || Shift()))
{
ImportUserThemes(aItems[nItem][0]); // Загрузить тему из выбранного файла и активировать.
bUpdate = true;
return nRunAgain;
}
return false;
}
if (nItem == i - 2)
{
ImportUserThemes(""); // Загрузить все цветовые темы из папки \Coder\themes.
bUpdate = true;
return nRunAgain;
}
if (nItem == i)
{
ColorThemeSettings(); // Открыть диалог настроек плагина Coder.
return false;
}
WScript.Echo("RunMenu Error");
return false;
}
function Ctrl()
{
return Boolean(oSys.Call("User32::GetKeyState", 0x11 /*VK_CONTROL*/) & 0x8000);
}
function Shift()
{
return Boolean(oSys.Call("User32::GetKeyState", 0x10 /*VK_SHIFT*/) & 0x8000);
}
function DeleteColorTheme(sThemeName)
{
var sThemeList;
var aThemeList;
if (oSet.Begin("Coder", 0x21 /*POB_PLUGS|POB_READ*/))
{
sThemeList = oSet.Read("VarThemeList", 3 /*PO_STRING*/);
oSet.End();
}
aThemeList = sThemeList.split("|").sort();
for (i = 0; i < aThemeList.length; ++i)
{
if (aThemeList[i] == sThemeName)
{
aThemeList.splice(i, 1);
break;
}
}
sThemeList = aThemeList.join("|");
if (oSet.Begin("Coder", 0x22 /*POB_PLUGS|POB_SAVE*/))
{
oSet.Delete("/" + sThemeName);
oSet.Write("VarThemeList", 3 /*PO_STRING*/, sThemeList);
if (sThemeName == sCurThemeName)
{
if (aThemeList.length == 0)
sThemeName = "Default";
else sThemeName = aThemeList[0];
oSet.Write("VarThemeActive", 3 /*PO_STRING*/, sThemeName);
}
oSet.End();
}
RestartPlugin();
}
function ActivateColorTheme(sThemeName)
{
AkelPad.Call("Coder::Settings", 5, sThemeName);
}
function ColorThemeSettings()
{
AkelPad.Call("Coder::Settings");
}
function GetCurTheme()
{
var sThemeName = "";
var lpBuf;
if (lpBuf = AkelPad.MemAlloc(256))
{
AkelPad.CallW("Coder::Settings", 20, 0, lpBuf, 256);
sThemeName = AkelPad.MemRead(lpBuf, 1 /*DT_UNICODE*/);
AkelPad.MemFree(lpBuf);
}
return sThemeName;
}
function RestartPlugin()
{
var bHighLight = AkelPad.IsPluginRunning("Coder::HighLight");
var bCodeFold = AkelPad.IsPluginRunning("Coder::CodeFold");
var bAutoComplete = AkelPad.IsPluginRunning("Coder::AutoComplete");
for(var i = 0; i < 2; i++)
{
if (bHighLight)
AkelPad.Call("Coder::HighLight");
if (bCodeFold)
AkelPad.Call("Coder::CodeFold");
if (bAutoComplete)
AkelPad.Call("Coder::AutoComplete");
}
}
function GetFileList()
{
var aList = [];
var sFileName;
var sFile_Ext;
if (ThemesFolderExists())
{
var Folder = oFSO.GetFolder(sDirThemes);
var Files = new Enumerator(Folder.Files);
for (; !Files.atEnd(); Files.moveNext())
{
sFileName = Files.item().Name;
sFile_Ext = "." + AkelPad.GetFilePath(sFileName, 4 /*CPF_FILEEXT*/);
if (sFile_Ext == sFileExt)
aList.push(sFileName);
}
}
return aList.sort();
}
function ExportUserThemes()
{
var sThemeList = "";
var sThemeText = "";
if (MsgBox(sDirThemesU) == 2 /*IDCANCEL*/) return;
if (oFSO.FolderExists(sDirThemes) == false)
oFSO.CreateFolder(sDirThemes);
if (oFSO.FolderExists(sDirThemesU) == false)
oFSO.CreateFolder(sDirThemesU);
if (oSet.Begin("Coder", 0x21 /*POB_PLUGS|POB_READ*/))
{
sThemeList = oSet.Read("VarThemeList", 3 /*PO_STRING*/);
if (sThemeList != "")
{
var aList = sThemeList.split("|");
var nLen = aList.length;
for(var i = 0; i < nLen; i++)
{
sThemeText = oSet.Read("/" + aList[i], 20 /*PO_BINARYSTRING*/);
if (sThemeText != "")
AkelPad.WriteFile(sDirThemesU + aList[i] + sFileExt, sThemeText, -1, 1251, false);
}
MsgBoxOk(sDirThemesU);
}
oSet.End();
}
}
function ThemesFolderExists()
{
return oFSO.FolderExists(sDirThemes);
}
function ImportUserThemes(sFile)
{
if (ThemesFolderExists())
{
if (sFile == "") // Загрузить все файлы.
{
var nLen = aFileList.length;
for(var i = 0; i < nLen; i++)
WriteThemeToIniFile(aFileList[i]);
RestartPlugin();
}
else // Загрузить файл.
{
WriteThemeToIniFile(sFile);
RestartPlugin();
AkelPad.Call("Coder::Settings", 5 /*DLLA_CODER_SETVARTHEME*/, sFile.replace(sFileExt,""));
}
}
}
function ReadThemeFile(sFullFileName)
{
var sText = AkelPad.ReadFile(sFullFileName);
sText = sText.replace(/\x0A/g, "\r"); // Newline format - CR (Macintosh)
sText = sText.replace(/\x0D{2,}/g, "\r"); // Удалить пустые строки.
sText = sText + String.fromCharCode(0); // Дописать в конец нули.
return sText;
}
function WriteThemeToIniFile(sFileName)
{
var sThemeText = ReadThemeFile(sDirThemes + sFileName);
var sThemeName = sFileName.replace(sFileExt,"");
if (oSet.Begin("Coder", 0x21 /*POB_PLUGS|POB_READ*/))
{
var sThemeList = oSet.Read("VarThemeList", 3 /*PO_STRING*/);
oSet.End();
}
if (oSet.Begin("Coder", 0x22 /*POB_PLUGS|POB_SAVE*/))
{
if (sThemeList == "")
sThemeList = sThemeName;
else if (sThemeList.search(sThemeName) == -1)
sThemeList = sThemeList + "|" + sThemeName;
oSet.Write("VarThemeList", 3 /*PO_STRING*/, sThemeList); // VarThemeList=Active4DEx|BespinEx|...|ZenburnEx
oSet.Write("/" + sThemeName, 2 /*PO_BINARY*/, sThemeText); // /BespinEx=4F004B00...22000D0000000000
oSet.End();
}
}
function MsgBox(sDir)
{
var nChoice = AkelPad.MessageBox(
AkelPad.GetMainWnd(),
"\nФайлы в папке\n" + sDir + "\nбудут перезаписаны.\n\nПродолжить?",
"Экспорт цветовых тем", 65 /*MB_ICONASTERISK | MB_OKCANCEL*/);
return nChoice;
}
function MsgBoxOk(sDir)
{
AkelPad.MessageBox(
AkelPad.GetMainWnd(),
"\nТемы экспортированы в папку\n" + sDir,
"Экспорт цветовых тем", 64 /*MB_ICONASTERISK | MB_OK*/);
}
function GetThemeList()
{
return GetThemeData(1);
}
function ExportInternalThemes()
{
GetThemeData(2);
MsgBoxOk(sDirThemesI);
}
function WriteFileTheme(sThemeName, sThemeText)
{
sThemeText = ExtraVars() + sThemeText;
sThemeText = sThemeText.replace(/ ([^"]+?)$/gm, ' "$1"'); // Значения переменных взять в кавычки.
AkelPad.WriteFile(sDirThemesI + sThemeName + "Ex" + sFileExt, sThemeText, -1, 1251, true);
}
function GetThemeData(nMode)
{
var sThemeName;
var sThemeText;
var lpThemeCur;
var lpThemePrev;
var lpThemeData;
var aThemeList = [];
var CELL = _X64?8:4;
if (nMode == 2)
{
if (MsgBox(sDirThemesI) == 2 /*IDCANCEL*/) return;
if (oFSO.FolderExists(sDirThemes) == false)
oFSO.CreateFolder(sDirThemes);
if (oFSO.FolderExists(sDirThemesI) == false)
oFSO.CreateFolder(sDirThemesI);
}
var lpVTG = AkelPad.MemAlloc(CELL); // **lppVarThemeGlobal
var lpVTA = AkelPad.MemAlloc(CELL); // **lppVarThemeActive
AkelPad.CallW("Coder::Settings", 24 /*DLLA_CODER_GETVARTHEMEDATA*/, 0, 0, lpVTG, lpVTA);
lpThemeCur = AkelPad.MemRead(lpVTA, 2 /*DT_QWORD*/);
AkelPad.MemFree(lpVTG);
AkelPad.MemFree(lpVTA);
while(lpThemeCur != 0) // Находим первую структуру VARTHEME.
{
lpThemePrev = lpThemeCur;
lpThemeCur = AkelPad.MemRead(lpThemeCur + CELL, 2 /*DT_QWORD*/); // *prev
}
lpThemeCur = lpThemePrev
while(lpThemeCur != 0)
{
sThemeName = AkelPad.MemRead(lpThemeCur + CELL * 5, 1 /*DT_UNICODE*/); // (4+4+12)/4=5 (8+8+24)/8=5 wszVarThemeName[MAX_PATH]
lpThemeData = AkelPad.MemRead(lpThemeCur + CELL * (_X64?71:136), 2 /*DT_QWORD*/); // (4+4+12+520+4)/4=136 (8+8+24+520+8)/8=71 *wpTextData
sThemeText = AkelPad.MemRead(lpThemeData, 1 /*DT_UNICODE*/);
if ((nMode == 1) & (sThemeText == "")) // В пользовательской теме *wpTextData указывает на пустую строку.
aThemeList.push(sThemeName);
if ((nMode == 2) & (sThemeText != ""))
WriteFileTheme(sThemeName, sThemeText);
lpThemeCur = AkelPad.MemRead(lpThemeCur, 2 /*DT_QWORD*/);
}
if (nMode == 1) return aThemeList.sort();
}
/*
AkelPad-4.9.8-src.zip\AkelFiles\Plugs\Coder\Source\Coder.h
typedef struct _VARTHEME {
struct _VARTHEME *next; 4(8)
struct _VARTHEME *prev; 4(8)
STACKVAR hVarStack; 12(24)
wchar_t wszVarThemeName[MAX_PATH]; 520(520) #define MAX_PATH 260 stdlib.h
int nVarThemeNameLen; 4(8)
const wchar_t *wpTextData;
} VARTHEME;
*/
function GetSyntaxFile()
{
var pSyntaxFile = "";
var lpSyntaxFile;
if (lpSyntaxFile = AkelPad.MemAlloc(256 * 2))
{
AkelPad.CallW("Coder::Settings", 16, 0, lpSyntaxFile, 256);
pSyntaxFile = AkelPad.MemRead(lpSyntaxFile, 1 /*DT_UNICODE*/);
AkelPad.MemFree(lpSyntaxFile);
}
return (pSyntaxFile != "");
}
function ExtraVars()
{
var sExtraVars =
"LABEL #64E986\r" +
"OK #339933\r" +
"ERR #CC3333\r" +
"IMPORTANT #CC3333\r" +
"WARN #BB6622\r" +
"DBG #9932CC\r" +
"INFO #3333CC\r" +
"COMM_C #668000\r" +
"KEYWORD #9932CC\r" +
"KEYWORD_ALT #3399CC\r" +
"KEYWORD_SPECIAL #33CC99\r" +
"KEYWORD_NOTSTD #800000\r" +
"GLOBAL_VAR #9932CC\r" +
"GLOBAL_VAR_NOTSTD #800000\r" +
"REGEXP #8000FF\r" +
"PROPERTY #3399CC\r" +
"PROPERTY_ALT #3333CC\r" +
"PROPERTY_NOTSTD #CC3333\r" +
"EVENT #33CC99\r" +
"EVENT_NOTSTD #CC3333\r" +
"BLOCK #8B0000\r" +
"BLOCK_NOTSTD #CC3333\r" +
"OP_ALT #3399CC\r" +
"OP_NOTSTD #8B0000\r" +
"CMD #339933\r" +
"TAG_NS #3399CC\r" +
"TAG_ALT #3399CC\r" +
"TAG_DEPRECATED #992266\r" +
"TAG_NOTSTD #8B0000\r" +
"ATTR_ALT #9932CC\r" +
"ATTR_NS #33CC99\r" +
"ATTR_NOTSTD #800000\r" +
"ENTITY #3399CC\r" +
"IF_NOTSTD #8B0000\r" +
"NUM_ALT #0000FF\r";
return sExtraVars;
}
Last edited by dothen on Sun May 07, 2023 6:56 pm, edited 1 time in total.
-
Offline
- Posts: 1873
- Joined: Mon Aug 06, 2007 1:07 pm
- Contact:
Post by Infocatcher »
Code: Select all
// http://akelpad.sourceforge.net/forum/viewtopic.php?p=36081#p36081
// http://infocatcher.ucoz.net/js/akelpad_scripts/toggleToolbarRows.js
// https://github.com/Infocatcher/AkelPad_scripts/blob/master/toggleToolbarRows.js
// (c) Infocatcher 2022
// Version: 0.1.0 - 2022-08-07
// Author: Infocatcher
//// Toggle multiline toolbar from ToolBar plugin (convert BREAK <-> #BREAK)
// Arguments:
// -toolBarName="ToolBar2" - specify file name of ToolBar plugin
// Usage:
// Call("Scripts::Main", 1, "toggleToolbarRows.js")
// Call("Scripts::Main", 1, "toggleToolbarRows.js", '-toolBarName="ToolBar2"')
var tbPlugName = AkelPad.GetArgValue("toolBarName", "ToolBar");
function _localize(s) {
var strings = {
"ToolBarText data in %P plugin is empty!": {
ru: "Содержимое ToolBarText плагина %P пустое!"
},
"ToolBarText data in %P plugin not recognized:\n%S": {
ru: "Содержимое ToolBarText плагина %P не распознано:\n%S"
},
"Failed to read settings of %P plugin": {
ru: "Не удалось прочитать настройки плагина %P"
},
"Failed to write settings of %P plugin": {
ru: "Не удалось записать настройки плагина %P"
},
"Failed to toggle multiline toolbar from %P plugin: BREAK item not found": {
ru: "Не удалось переключить многострочность панели инструментов плагина %P: элемент BREAK не найден"
}
};
var lng = "en";
switch(AkelPad.GetLangId(1 /*LANGID_PRIMARY*/)) {
case 0x19: lng = "ru";
}
_localize = function(s) {
return strings[s] && strings[s][lng] || s;
};
return _localize(s);
}
function _str(s) {
return _localize(s).replace("%P", tbPlugName);
}
var oSet = AkelPad.ScriptSettings();
var hMainWnd = AkelPad.GetMainWnd();
var isHex = AkelPad.SendMessage(hMainWnd, 1222 /*AKD_GETMAININFO*/, 5 /*MI_SAVESETTINGS*/, 0) == 2 /*SS_INI*/;
if(oSet.Begin(tbPlugName, 0x21 /*POB_READ|POB_PLUGS*/)) {
var tbData = oSet.Read("ToolBarText", 3 /*PO_STRING*/);
oSet.End();
if(
!tbData
|| isHex && (tbData.length % 4 || /[^\dA-F]/i.test(tbData))
) {
error(
tbData
? _str("ToolBarText data in %P plugin not recognized:\n%S")
.replace("%S", tbData.substr(0, 100))
: _str("ToolBarText data in %P plugin is empty!")
);
WScript.Quit();
}
}
else {
error(_str("Failed to read settings of %P plugin"));
}
if(tbData && oSet.Begin(tbPlugName, 0x22 /*POB_SAVE|POB_PLUGS*/)) {
var tbText = isHex ? hexToStr(tbData) : tbData;
var changed;
tbText = tbText.replace(/\r(#?)BREAK\r/g, function(s, commented) {
changed = true;
return "\r" + (commented ? "" : "#") + "BREAK\r";
});
if(changed) {
tbData = isHex ? strToHex(tbText) : tbText;
oSet.Write("ToolBarText", 3 /*PO_STRING*/, tbData);
}
else {
error(_str("Failed to toggle multiline toolbar from %P plugin: BREAK item not found"), 48 /*MB_ICONEXCLAMATION*/);
}
oSet.End();
if(changed && AkelPad.IsPluginRunning(tbPlugName + "::Main")) {
AkelPad.Call(tbPlugName + "::Main");
AkelPad.Call(tbPlugName + "::Main");
}
}
else {
tbData && error(_str("Failed to write settings of %P plugin"));
}
function hexToStr(h) {
return h.replace(/[\dA-F]{4}/ig, function(h) {
var n = parseInt(reorder(h), 16);
return String.fromCharCode(n);
});
}
function strToHex(s) {
return s.replace(/[\s\S]/g, function(c) {
var h = c.charCodeAt(0).toString(16).toUpperCase();
h = "0000".substr(h.length) + h;
return reorder(h);
});
}
function reorder(h) { // LE <-> BE
var b1 = h.substr(0, 2);
var b2 = h.substr(2);
return b2 + b1;
}
function error(msg, icon) {
AkelPad.MessageBox(hMainWnd, msg, WScript.ScriptName, icon || 16 /*MB_ICONERROR*/);
}
Toggle multiline toolbar from ToolBar plugin (convert BREAK <-> #BREAK).


-
Offline
- Posts: 17
- Joined: Wed Feb 16, 2022 2:25 pm
В сценарий PluginText.js (автор KDJ) добавил поддержку работы с различными кодировками (сохранение - только UTF-16 или UTF-8 ) и форматами новой строки (сохранение - только в CR или CRLF) файлов .akelmenu.
В оригинальном сценарии сохранять и загружать файлы можно только в UTF-16 LE, CR.
Прежде всего это полезно при работе с Git или Mercurial, которые не поддерживают UTF-16 (сравнивают как бинарные файлы, и с ними потом неудобно работать) и переводы строк в стиле Mac (CR, \r).
Поскольку KDJ перестал посещать этот форум, просто выкладываю модифицированный вариант сценария.
PluginTextEx.js
В оригинальном сценарии сохранять и загружать файлы можно только в UTF-16 LE, CR.
Прежде всего это полезно при работе с Git или Mercurial, которые не поддерживают UTF-16 (сравнивают как бинарные файлы, и с ними потом неудобно работать) и переводы строк в стиле Mac (CR, \r).
Поскольку KDJ перестал посещать этот форум, просто выкладываю модифицированный вариант сценария.
PluginTextEx.js
-
Offline
- Posts: 348
- Joined: Mon Jun 03, 2019 2:33 am
Re: Scripts collection
Иногда хочется посмотреть скрипт, но лень искать его в списке скриптов, скопировал имя скрипта (двойной клик -> Ctrl+C), кликаем пункт и скрипт уже открыт.
Code: Select all
// "Открыть скрипт" Call("Scripts::Main", 1, "OpenScript.js")
var pHelp = "Удерживая Ctrl нажмите пункт меню или кнопку, далее в открытом окне выделите имя скрипта двойным кликом (без .js), скопируйте и только потом используйте этот пункт, чтобы отрыть скрипт в AkelPad"
var pName = AkelPad.GetClipboardText(true);
// if (pName.indexOf("\n") = -1) {
// разрешил относительный путь folder/name в регвыре
if ((pName == "") || (pName.lengt > 150) || (pName.search(/[\n:*?"<>|]/) > -1)) {
WScript.Echo('Буфер обмена не содержит имени файла. ' + pHelp);
WScript.Quit();
}
var path = 'AkelFiles\\Plugs\\Scripts\\' + pName;
if (path.slice(-3) != '.js') {path += '.js';}
var fso = new ActiveXObject("Scripting.FileSystemObject");
if (fso.FileExists(path)) {
AkelPad.OpenFile(path);
} else {
WScript.Echo(path + '\n\nФайл не существует. ' + pHelp);
}
-
Offline
- Posts: 348
- Joined: Mon Jun 03, 2019 2:33 am
Re: Scripts collection
Stemming (RU) - удаляет окончания слов для генерации списка слов поискового запроса. Например я сделал поле ввода для поиска слов по справке. Мне нужно иметь только слова имеющиеся в справке. Я получаю список всех слов, но там есть такие как "мяч, мячу, мячом, мячами, мячи" и т.д. при этом для поискового запроса требуется только "мяч" чтобы найти все его вхождения в справке.
Обновил, добавил поддержку "ё", точнее восстановление "ё" из оригинального слова.
Обновил, оптимизировал вариант с "ё".
Обновил, добавил перевод в нижний регистр и сообщение о завершении обработки.
Проверка на отсутствие гласных в слове исключает абревиатуры.
70 тыс. строк очень медленно обрабатывает, 1 минута 10 сек. (онлайн-сервисы обрабатывают мгновенно).
Обновил, добавил поддержку "ё", точнее восстановление "ё" из оригинального слова.
Обновил, оптимизировал вариант с "ё".
Обновил, добавил перевод в нижний регистр и сообщение о завершении обработки.
Проверка на отсутствие гласных в слове исключает абревиатуры.
70 тыс. строк очень медленно обрабатывает, 1 минута 10 сек. (онлайн-сервисы обрабатывают мгновенно).
Code: Select all
// Стеминг он же Стиммер (удаляет окончания слов оставляя базу), предназначен для генерации списка слов поискового запроса.
// модернизация для AkelPad от AZJIO (14.02.2024)
// алгоритм использовал по ссылке: https://popsul.ru/blog/2012/06/post-10.html
// но он работает с одним словом, пришлось дополнить его захватом русских слов из текста циклом обработки каждого слова,
// и добавить удаление дубликатов и сортировку.
var pAllText=AkelPad.GetTextRange(0, -1);
pAllText=pAllText.toLowerCase()
pAllText = Stemmer(pAllText)
pAllText = RemoveDuplicates(pAllText) // удаление дубликатов и сортировка
pAllText = pAllText.replace(/^[^\n]{0,3}\n/gm, ""); // удаление строк длинной меньше 4-х символов
AkelPad.SetClipboardText(pAllText);
WScript.Echo("готово, результат в буфере обмена");
function RemoveDuplicates(text) {
oDict = new ActiveXObject("Scripting.Dictionary");
oDict.CompareMode = 1;
var ares = [];
var result = "";
var i;
var size;
var arr = text.split('\n'); // получаем массив слов
for (i in arr) {
if (oDict.Exists(arr[i]))
oDict.Item(arr[i]) += 1;
else
oDict.Item(arr[i]) = 1;
};
ares = (new VBArray(oDict.Keys())).toArray();
ares = ares.sort();
result = ares.join("\n")
return result;
};
function Stemmer(pAllText) {
var RVRE= /^(.*?[аеиоуыэюя])(.*)$/i;
var PERFECTIVEGROUND_1= /([ая])(в|вши|вшись)$/gi;
var PERFECTIVEGROUND_2= /(ив|ивши|ившись|ыв|ывши|ывшись)$/i;
var REFLEXIVE= /(с[яь])$/i;
var ADJECTIVE= /(ее|ие|ые|ое|ими|ыми|ей|ий|ый|ой|ем|им|ым|ом|его|ого|ему|ому|их|ых|ую|юю|ая|яя|ою|ею)$/i;
var PARTICIPLE_1= /([ая])(ем|нн|вш|ющ|щ)$/gi;
var PARTICIPLE_2= /(ивш|ывш|ующ)$/i;
var VERB_1= /([ая])(ла|на|ете|йте|ли|й|л|ем|н|ло|но|ет|ют|ны|ть|ешь|нно)$/gi;
var VERB_2= /(ила|ыла|ена|ейте|уйте|ите|или|ыли|ей|уй|ил|ыл|им|ым|ен|ило|ыло|ено|ят|ует|уют|ит|ыт|ены|ить|ыть|ишь|ую|ю)$/i;
var NOUN= /(а|ев|ов|ие|ье|е|иями|ями|ами|еи|ии|и|ией|ей|ой|ий|й|иям|ям|ием|ем|ам|ом|о|у|ах|иях|ях|ы|ь|ию|ью|ю|ия|ья|я)$/i;
var DERIVATIONAL= /.*[^аеиоуыэюя]+[аеиоуыэюя].*ость?$/i;
var DER= /ость?$/i;
var SUPERLATIVE= /(ейше|ейш)$/i;
var I= /и$/i;
var P= /ь$/i;
var NN= /нн$/i;
var start;
var rv;
var wParts;
var temp;
var rure = /[а-яё]+/gi;
var myArray;
var result = "";
while ((myArray = rure.exec(pAllText)) != null) // цикл перечисления всех русских слов
{
myArray[0] = myArray[0].replace(/ё/gi, "е");
wParts = myArray[0].match(RVRE); // проверяем совпадение гласных в тексте
if (!wParts) {
// return word; // если нет гласных, возвращаем текст как есть назад
continue
}
start = wParts[1];
rv = wParts[2];
temp = rv.replace(PERFECTIVEGROUND_2, "");
if (temp == rv) {
temp = rv.replace(PERFECTIVEGROUND_1, '$1');
}
if (temp == rv) {
rv = rv.replace(REFLEXIVE, "");
temp = rv.replace(ADJECTIVE, "");
if (temp != rv) {
rv = temp;
temp = rv.replace(PARTICIPLE_2, "");
if (temp == rv) {
rv = rv.replace(PARTICIPLE_1, '$1');
}
} else {
temp = rv.replace(VERB_2, "");
if (temp == rv) {
temp = rv.replace(VERB_1, '$1');
}
if (temp == rv) {
rv = rv.replace(NOUN, "");
} else {
rv = temp;
}
}
} else {
rv = temp;
}
rv = rv.replace(I, "");
if (rv.match(DERIVATIONAL)) {
rv = rv.replace(DER, "");
}
temp = rv.replace(P, "");
if (temp == rv) {
rv = rv.replace(SUPERLATIVE, "");
rv = rv.replace(NN, 'н');
} else {
rv = temp;
}
result += start + rv + "\n";
};
return result;
};
Code: Select all
// Стеминг он же Стиммер (удаляет окончания слов оставляя базу), предназначен для генерации списка слов поискового запроса.
// модернизация для AkelPad от AZJIO (14.02.2024)
// алгоритм использовал по ссылке: https://popsul.ru/blog/2012/06/post-10.html
// но он работает с одним словом, пришлось дополнить его захватом русских слов из текста циклом обработки каждого слова,
// и добавить удаление дубликатов и сортировку.
// Восстанавливает "ё" - если строка содержит ё (включаем флаг restore и перед тем как добавить слово в результат берём из оргинала
// число символов по результату и получаем обрезанное но с "ё").
var pAllText=AkelPad.GetTextRange(0, -1);
pAllText=pAllText.toLowerCase()
pAllText = Stemmer(pAllText)
pAllText = pAllText.replace(/^[^\n]{0,3}\n/gm, ""); // удаление строк длинной меньше 4-х символов
AkelPad.SetClipboardText(pAllText); // возвращаем результат в буфер обмена
WScript.Echo("готово, результат в буфере обмена");
function Stemmer(pAllText) {
var RVRE= /^(.*?[аеиоуыэюя])(.*)$/i;
var PERFECTIVEGROUND_1= /([ая])(в|вши|вшись)$/gi;
var PERFECTIVEGROUND_2= /(ив|ивши|ившись|ыв|ывши|ывшись)$/i;
var REFLEXIVE= /(с[яь])$/i;
var ADJECTIVE= /(ее|ие|ые|ое|ими|ыми|ей|ий|ый|ой|ем|им|ым|ом|его|ого|ему|ому|их|ых|ую|юю|ая|яя|ою|ею)$/i;
var PARTICIPLE_1= /([ая])(ем|нн|вш|ющ|щ)$/gi;
var PARTICIPLE_2= /(ивш|ывш|ующ)$/i;
var VERB_1= /([ая])(ла|на|ете|йте|ли|й|л|ем|н|ло|но|ет|ют|ны|ть|ешь|нно)$/gi;
var VERB_2= /(ила|ыла|ена|ейте|уйте|ите|или|ыли|ей|уй|ил|ыл|им|ым|ен|ило|ыло|ено|ят|ует|уют|ит|ыт|ены|ить|ыть|ишь|ую|ю)$/i;
var NOUN= /(а|ев|ов|ие|ье|е|иями|ями|ами|еи|ии|и|ией|ей|ой|ий|й|иям|ям|ием|ем|ам|ом|о|у|ах|иях|ях|ы|ь|ию|ью|ю|ия|ья|я)$/i;
var DERIVATIONAL= /.*[^аеиоуыэюя]+[аеиоуыэюя].*ость?$/i;
var DER= /ость?$/i;
var SUPERLATIVE= /(ейше|ейш)$/i;
var I= /и$/i;
var P= /ь$/i;
var NN= /нн$/i;
var start;
var rv;
var wParts;
var temp;
// var rure = /[а-яё]+/gi;
var rure = /[а-яё]{4,}/gi;
var myArray;
var result = "";
var orig = "";
var restoreIO = 0;
var lenw;
oDict = new ActiveXObject("Scripting.Dictionary");
oDict.CompareMode = 1;
var ares = [];
while ((myArray = rure.exec(pAllText)) != null) // цикл перечисления всех русских слов
{
if (/ё/i.test(myArray[0])) {
orig = myArray[0]
myArray[0] = myArray[0].replace(/ё/gi, "е");
restoreIO=1
}
wParts = myArray[0].match(RVRE); // проверяем совпадение гласных в тексте
if (!wParts) {
// return word; // если нет гласных, возвращаем текст как есть назад
continue
}
start = wParts[1];
rv = wParts[2];
temp = rv.replace(PERFECTIVEGROUND_2, "");
if (temp == rv) {
temp = rv.replace(PERFECTIVEGROUND_1, '$1');
}
if (temp == rv) {
rv = rv.replace(REFLEXIVE, "");
temp = rv.replace(ADJECTIVE, "");
if (temp != rv) {
rv = temp;
temp = rv.replace(PARTICIPLE_2, "");
if (temp == rv) {
rv = rv.replace(PARTICIPLE_1, '$1');
}
} else {
temp = rv.replace(VERB_2, "");
if (temp == rv) {
temp = rv.replace(VERB_1, '$1');
}
if (temp == rv) {
rv = rv.replace(NOUN, "");
} else {
rv = temp;
}
}
} else {
rv = temp;
}
rv = rv.replace(I, "");
if (rv.match(DERIVATIONAL)) {
rv = rv.replace(DER, "");
}
temp = rv.replace(P, "");
if (temp == rv) {
rv = rv.replace(SUPERLATIVE, "");
rv = rv.replace(NN, 'н');
} else {
rv = temp;
}
temp = start + rv
if (restoreIO) {
lenw = temp.length
temp = orig.substring(0, lenw)
restoreIO=0
}
if (oDict.Exists(temp)) {
oDict.Item(temp) += 1;
} else {
oDict.Item(temp) = 1;
}
};
ares = (new VBArray(oDict.Keys())).toArray();
ares = ares.sort();
result = ares.join("\n");
return result;
};
-
Offline
- Posts: 43
- Joined: Sat Jul 05, 2008 11:30 am
- Location: Odesa, Ukraine
Re: Scripts collection
Code: Select all
// https://akelpad.sourceforge.net/forum/viewtopic.php?t=240&p=36478#p36478
// description: Pass selected text to the default browser
// version: 2024-09-14
// author: ewild
//// based on: InternetRequest.js (v2.2, 2015.04.01) by VladSh
//// https://akelpad.sourceforge.net/forum/viewtopic.php?t=204&p=7303#p7303
// Examples ("$text" is a selected text placeholder):
//// predefined url:
// "Translate: Google: Auto > Ua" Call("Scripts::Main",1,"InternetRequestFix.js","https://translate.google.com/sl=auto&tl=uk&text=$text")
// "Translate: DeepL : Auto > Ua" Call("Scripts::Main",1,"InternetRequestFix.js","https://www.deepl.com/translator#auto/uk/$text")
//// just a text*:
// "Google Search for selected text in default browser" Call("Scripts::Main",1,"InternetRequestFix.js","$text")
// *note: if a valid URL is recognized within the selected text at the beginning it will be opened in the default browser instead
pArgLine=AkelPad.GetArgLine();
if (pArgLine) {
pSelText=AkelPad.GetSelText();
pSelText=pSelText.replace(/ +/g," ");
pSelText=pSelText.replace(/^\s+|\s+$/gm,"");
if (pArgLine.indexOf('deepl') > -1) {
pSelText=pSelText.replace(/[\x2f\x7c]/g,"");}
pSelText=encodeURIComponent(pSelText);
if (pSelText) {
pLink = WScript.Arguments(0).replace('$text',pSelText);
objShell = new ActiveXObject("WScript.Shell");
if (pLink.search('^(http:|https:|www.|ftp:|notes:)') == -1)
pLink = 'https://www.google.com/search?q=' + pLink;
objShell.Run('"'+pLink+'"');}
}
The script is purposed to pass a selected text to the default browser.
- If there's a predefined URL, the selected text will be processed by the receiving service, e.g. Google Translate, DeepL, etc.
- If a valid URL is recognized within the selected text at the beginning, it will be opened in the default browser as such.
- If the selected text is just a text it will be passed as a search query to Google Search.
The main difference between the two is the way they handle selected text that a. contains double quotes ["], b. contains multiple lines, c. is simply a text and cannot be recognized as a [part of] valid URI.
- if the selected text contains double quotes ["]:
- The original script drops off everything after the very first double quote it meets.
+ The customized script will replace double quotes ["] with single quotes ['] to pass the selected text as a whole. - if the selected text contains multiple lines:
- The original script glues the lines together without any separation which makes it difficult to properly process it further by the receiving services (translators, search engines, etc).
+ The customized script will join the lines via blank space(s) to pass the selected text as a whole readable line. - if the selected text is just a text (and cannot be recognized as a [part of] valid URI):
- The original script adds 'http:\\' prefix to the selected text and then your browser tries to process all that as a URL, which makes no sense and fails.
+ The customized script will wrap the selected text into a valid request, for instance, as a Google Search request for the selected text as a whole.
- The selected text is now being preprocessed with the encodeURIComponent() function within the script itself that brings the following positive effects:
- multi-line text is now being passed as such (with line breaks); the receiving service would now process that properly (including keeping the multi-line structure intact, both within the input and output*); separate preprocessing of the line breaks in the script is no longer required.
- double quotes are being passed as such; the receiving service would now process that properly (keeping the double quotes intact); separate preprocessing of the double quotes in the script is no longer required.
- If the receiving service is DeepL, slash and pipe characters ("/", \x2f; "|", \x7c, respectively) have to be removed from the text since the service cannot digest them within the externally passed URI (the service would drop off everything starting from the first "/" on; and would hang on when meets "|").
* For Google Translate it may look as though the line breaks are gone within the translated text section, however, if you copy-paste the text from there back to AkelPad, the line breaks would be kept in place.**
** This might somehow not always be the case. For instance, if the target language is Russian, the line breaks are actually gone.
Last edited by ewild on Sat Dec 21, 2024 9:19 pm, edited 1 time in total.
-
Offline
- Posts: 43
- Joined: Sat Jul 05, 2008 11:30 am
- Location: Odesa, Ukraine
Re: Scripts collection
Code: Select all
// https://akelpad.sourceforge.net/forum/viewtopic.php?t=240&p=36486#p36486
// version: 2024-09-15
// author: ewild
// Description: URL download with wget into the Downloads folder
// Usage (in URL menu): "URL download : wget" Call("Scripts::Main",1,"downloadWget.js","%u")
WshShell = new ActiveXObject("WScript.Shell");
$key = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders';
$name = '{374DE290-123F-4565-9164-39C4925E467B}';
$downloads = WshShell.RegRead($key+'\\'+$name);
$url = AkelPad.GetArgLine();
$a = AkelPad.GetAkelDir();
$bin = $a+'\\..\\Get\\wget.exe'; // See: Note
$command = '-S -N --no-if-modified-since --no-hsts -U=Mozilla --referer="'+$url+'" "'+$url+'" -P '+$downloads;
WshShell.Exec($bin+' '+$command);
// Note:
// Put there the actual path to the wget executable:
// - relative path to the AkepPad directory as in the script :: $bin = $a+'\\..\\Get\\wget.exe';
// - or literal path, something like :: $bin = 'd:\\Tools\\wget.exe';
The script uses wget.exe tool to download a URL into the Downloads folder.
Notes:
The script's intended use is from the URL menu.
The Downloads folder location is being retrieved from the Windows* registry.
A user has to put their actual path to the wget executable there in the script:
- relative to the AkepPad directory like in the script :: $bin = $a + '\\..\\Get\\wget.exe';
- or a literal one, something like :: $bin = 'd:\\Tools\\wget.exe'
wget.exe -S -N --no-if-modified-since --no-hsts -U=Mozilla --referer="$url" "$url" -P $downloads
This is pretty suitable for most user cases, but, of course, is not universal.
So feel free to modify it according to your specific needs, if any.
* This is a thing for more or less modern Windows.
Windows XP does not have a designated "Downloads" folder by default.
In such a case, a user might prefer to set a destination folder manually, for instance, putting a literal path for the $downloads variable, something of a kind:
- $downloads = 'd:\\Path\\to\\My\\Custom\\Downloads\\Location\\';
- $downloads = "%USERPROFILE%\\Downloads";
-
Offline
- Posts: 40
- Joined: Thu May 05, 2022 5:38 am
Re: Scripts collection
Вертикальная разметка.
Code: Select all
// https://akelpad.sourceforge.net/forum/viewtopic.php?p=36535#p36535
// Version: 1.1 (2024.11.09)
// Author: dothen
//
//
// Description(1033): Shows the vertical markup.
// Description(1049): Показывает вертикальную разметку.
//
// Usage:
// "VMarkup1" Call("Scripts::Main", 1, "VerticalMarkup.js") Icon(37)
// "VMarkup2" Call("Scripts::Main", 1, "VerticalMarkup.js", `-skip=10 -fill=1 -color=0x00C4CFCF`) Icon(37)
// "ClearVMarkup" Call("Scripts::Main", 1, "VerticalMarkup.js", `-clear=true`) Icon(37)
// Аргументы:
// -skip Число символов между линиями > 0 (по умолчанию 8).
// -fill Ширина линии в символах >= 0, если 0 то ширина = 1px (по умолчанию 0).
// -color Цвет линии в hex виде: 0x00BBGGRR (по умолчанию 0x00676767).
// -clear Если true то восстановить изначальный фон и левый отступ (по умолчанию false).
//
// Описание:
// Скрипт устанавливает фоновое изображение в виде вертикальных линий.
// Левый отступ автоматически устанавливается равным нулю.
// Имеет смысл применять с моноширинным шрифтом.
var hWndEdit = AkelPad.GetEditWnd();
if (!hWndEdit) WScript.Quit();
var nSkip = AkelPad.GetArgValue("skip", 8);
var nFill = AkelPad.GetArgValue("fill", 0);
var nColor = AkelPad.GetArgValue("color", 0x00676767);
var ClearBk = AkelPad.GetArgValue("clear", false);
if (nSkip < 1 || nFill < 0)
{
WScript.Echo("Warning: -skip=" + nSkip + " -fill=" + nFill);
WScript.Quit();
}
var oSys = AkelPad.SystemFunction();
var hMainWnd = AkelPad.GetMainWnd();
if (ClearBk)
{
RestoreBackground();
RestoreMarginLeft();
WScript.Quit();
}
SetMarginLeft(0);
SetBackgroundImage();
function SetBackgroundImage()
{
var nHeight = AkelPad.SendMessage(hWndEdit, 3188 /*AEM_GETCHARSIZE*/, 0 /*AECS_HEIGHT*/, null);
var nWidth = AkelPad.SendMessage(hWndEdit, 3188 /*AEM_GETCHARSIZE*/, 1 /*AECS_AVEWIDTH*/, null);
var lpaecolors = AkelPad.MemAlloc(68);
AkelPad.MemCopy(lpaecolors, 0x00000008 /*AECLR_BASICBK*/, 3 /*DT_DWORD*/);
AkelPad.SendMessage(hWndEdit, 3231 /*AEM_GETCOLORS*/, 0, lpaecolors);
var BasicBk = AkelPad.MemRead(_PtrAdd(lpaecolors, 12), 3 /*DT_DWORD*/);
var hDC = oSys.Call("Gdi32::CreateCompatibleDC", 0);
var hBitmap = oSys.Call("Gdi32::CreateBitmap", nWidth * (nSkip + nFill), nHeight, 1, 32, 0);
var hBmOld = oSys.Call("Gdi32::SelectObject", hDC, hBitmap);
oSys.Call("Gdi32::SelectObject", hDC, oSys.Call("Gdi32::GetStockObject", 18 /*DC_BRUSH*/));
oSys.Call("Gdi32::SelectObject", hDC, oSys.Call("Gdi32::GetStockObject", 19 /*DC_PEN*/));
oSys.Call("Gdi32::SetDCPenColor", hDC, BasicBk);
oSys.Call("Gdi32::SetDCBrushColor", hDC,BasicBk);
oSys.Call("Gdi32::Rectangle", hDC, 0, 0, nWidth * nSkip, nHeight);
if (nFill == 0)
{
oSys.Call("Gdi32::SetDCPenColor", hDC, nColor);
oSys.Call("Gdi32::MoveToEx", hDC, nWidth * nSkip - 1, 0, null);
oSys.Call("Gdi32::LineTo", hDC, nWidth * nSkip - 1, nHeight);
}
else
{
oSys.Call("Gdi32::SetDCPenColor", hDC, nColor);
oSys.Call("Gdi32::SetDCBrushColor", hDC, nColor);
oSys.Call("Gdi32::Rectangle", hDC, nWidth * nSkip, 0, nWidth * (nSkip + nFill), nHeight);
}
oSys.Call("Gdi32::SelectObject", hDC, hBmOld);
oSys.Call("Gdi32::DeleteDC", hDC);
AkelPad.MemFree(lpaecolors);
AkelPad.SendMessage(hWndEdit, 3391 /*AEM_SETBACKGROUNDIMAGE*/, hBitmap, 128);
}
function RestoreBackground()
{
var pBkImageFile = AkelPad.SendMessage(hMainWnd, 1223 /*AKD_GETFRAMEINFO*/, 140 /*FI_BKIMAGEFILE*/, 0);
var hBkImageBitmapCur = AkelPad.SendMessage(hWndEdit, 3390 /*AEM_GETBACKGROUNDIMAGE*/, 0, 0);
var hBkImageBitmapOld = AkelPad.MemRead(_PtrAdd(pBkImageFile, 524 /*MAX_PATH * 2 + sizeof(int)*/), 2 /*DT_QWORD*/);
if (hBkImageBitmapCur != hBkImageBitmapOld)
AkelPad.SendMessage(hWndEdit, 3391 /*AEM_SETBACKGROUNDIMAGE*/, hBkImageBitmapOld, 128);
}
function RestoreMarginLeft()
{
var oSet = AkelPad.ScriptSettings();
var lpBuffer;
var nMarginLeftFromIni = 0;
if (oSet.Begin("", 0x41 /*POB_READ|POB_PROGRAM*/))
{
lpBuffer = oSet.Read("MarginsEditRect", 2 /*PO_BINARY*/);
nMarginLeftFromIni = AkelPad.MemRead(lpBuffer, 3 /*DT_DWORD*/);
AkelPad.MemFree(lpBuffer);
oSet.End();
}
SetMarginLeft(nMarginLeftFromIni);
}
// Based on Instructor's code
// https://akelpad.sourceforge.net/forum/viewtopic.php?p=36551#p36551
function SetMarginLeft(nMarginLeft)
{
//Get margins
var lpMargins = AkelPad.SendMessage(hMainWnd, 1223 /*AKD_GETFRAMEINFO*/, 79 /*FI_RECTMARGINS*/, 0);
var nMarginLeftCur = AkelPad.MemRead(_PtrAdd(lpMargins, 0), 3 /*DT_DWORD*/);
var lpRect;
var lpFrameInfo;
if (nMarginLeftCur != nMarginLeft)
{
if (lpRect = AkelPad.MemAlloc(16 /*sizeof(RECT)*/))
{
oSys.Call("kernel32::RtlMoveMemory", lpRect, lpMargins, 16 /*sizeof(RECT)*/);
//New left margin
AkelPad.MemCopy(_PtrAdd(lpRect, 0) /*offsetof(RECT, left)*/, nMarginLeft, 3 /*DT_DWORD*/);
//Set margins
lpFrameInfo = AkelPad.MemAlloc(_X64?16:8 /*sizeof(FRAMEINFO)*/);
AkelPad.MemCopy(_PtrAdd(lpFrameInfo, 0) /*offsetof(FRAMEINFO, nType)*/, 26 /*FIS_RECTMARGINS*/, 3 /*DT_DWORD*/);
AkelPad.MemCopy(_PtrAdd(lpFrameInfo, _X64?8:4) /*offsetof(FRAMEINFO, dwData)*/, lpRect, 2 /*DT_QWORD*/);
AkelPad.SendMessage(hMainWnd, 1220 /*AKD_SETFRAMEINFO*/, lpFrameInfo, 0);
AkelPad.MemFree(lpRect);
}
}
}
-
Offline
- Posts: 5
- Joined: Tue Nov 12, 2024 10:28 pm
Re: Scripts collection
MathCad "на минималках". Математические вычисления непосредственно в редактируемом тексте.
[+] Добавил подсветку вычислений средствами плагина Coder, если он установлен.
[+] Добавил возможность копировать результат вычислений в буфер обмена.
[+] Добавил подсветку вычислений средствами плагина Coder, если он установлен.
[+] Добавил возможность копировать результат вычислений в буфер обмена.
Code: Select all
//
// Description(1049): Математические вычисления непосредственно в редактируемом тексте.
// Description(1033): Mathematical calculations directly in the edited text.
//
// https://akelpad.sourceforge.net/forum/viewtopic.php?p=36562#p36562
// Author: xOleg
// Version: 3.4
//
// MathCad "на минималках". При запуске скрипт вытянет из текста выражение слева от
// курсора или под курсором, посчитает, и вставит ответ здесь же, в тексте. Выделять
// текст не обязательно, но возможно, если нужно точно указать границы выражения.
//
// После вычисления тело расчета (в MathCad'е это называется регионом) будет подсвечено
// средствами плагина "Coder" (если установлен) и окружено фигурными скобками.
//
// Когда нужна повышенная точность, можно поставить двойное равенство (==).
// {PI} = 3.1415; {PI} == 3.14159265; {E} = 2.7182; {E} == 2.71828183
//
// Между цифрами и операторами допустим один пробел. Два и более пробела,
// соответственно, отделяют вычисление от остального текста. Разряды в цифрах можно
// разделить апострофами (') или пробелами.
// {50'000 + 30'000} = 80'000; {40 000 - 10 000} = 30'000
//
// У больших чисел (от миллиона) дробная часть отбрасывается (по умолчанию, и если
// нет дробей в левой части от тысячи; зависит от настроек). Если она вдруг
// понадобится, есть возможность указать повышенную точность через двойное равенство.
// Десятичная точка оставлена как индикатор.
// {5'000'000 / 1.3} = 3'846'153.; {1'000'000.1234} = 1'000'000.1234
//
// Поддерживается отображение результата в "денежном" ($=) и "процентном" (%=)
// форматах, при которых ответ в любом случае будет содержать два десятичных знака.
// В левой части выражения (той, что будет в фигурных скобках) возможны
// комментарии на кириллице.
// {[350'000 руб. * .21% годовых] / 12 месяцев} $= 6125.00 руб.
//
// Допустимы многострочные вычисления. Выражение в этом случае необходимо выделить.
// {Категория А: 30'000.00 руб. + 20'000.00 + 10'000.00 + …
// Категория Б: 20'000.00 руб. + 10'000.00 + …
// Категория В: 10'000.00 руб.
// } $= 100'000.00 руб.
//
// При необходимости текст можно здесь же поправить и пересчитать.
//
// Для того чтобы откатить вычисления, т.е. произвести операцию, противоположную
// основному функционалу, можно вызвать скрипт с аргументом seltype=-1.
//
// Всегда хотел утереть нос Аллену Раздову ;-)
//
// Вызов горячими клавишами (примеры):
// Посчитать (рекомендую Ctrl + .):
// Call("Scripts::Main", 1, "MathCalc.js", `-selcolor=turquoise -seltype=2`)
// Call("Scripts::Main", 1, "MathCalc.js", `-selcolor=off -sepdigits=0`)
// Call("Scripts::Main", 1, "MathCalc.js", `-seltype=2`)
// Посчитать + скопировать в буфер обмена (Ctrl + Shift + .):
// Call("Scripts::Main", 1, "MathCalc.js", `selcolor=orange -clipboard=2`)
// Call("Scripts::Main", 1, "MathCalc.js", `-clipboard`)
// Откатить (Ctrl + Shift + ,):
// Call("Scripts::Main", 1, "MathCalc.js", `-selcolor=off -seltype=-1`)
// Call("Scripts::Main", 1, "MathCalc.js", `-seltype=-1`)
//
// Аргументы ((*) - по умолчанию):
// -selcolor - Подсветка вычислений средствами плагина "Coder", если установлен.
// color (turquoise, orange, etc) - Будет использован стандартный цвет (*).
// #RRGGBB (#9BFFFF, #FFCD9B, etc) - Будет использован код цвета.
// off - Подсветка отключена.
// -clipboard - Скопировать результат вычисления также и в буфер обмена.
// 2 - Вычисления с копированием в буфер и без подсвечены раздельно (*).
// 1 - Одинаковая подсветка для всех операций.
// -sepdigits - Указывает, как разделить разряды цифр в результате расчета.
// 1 - Всегда разделять апострофами (') разряды для больших чисел (*).
// 0 - Разделить, если апострофы уже есть в левой части выражения.
// -bigones - Задает, отбрасывать ли дробную часть у больших чисел.
// 1 - У результата от миллиона дробная часть удаляется (*).
// 0 - Формат в соответствии с общими настройками.
// -seltype - Определяет, как скрипт выделит тело выражения после расчета.
// 2 - Вычисленное выражение будет помещено в фигурные скобки (*).
// 1 - Выделит только подсветкой (Coder'а или системной).
// 0 - Без выделения (крайне не советую).
//
var pCalcCharsRegExp = "([\{\}\\[\\]\|\(\)\*\/\+\\-$€'%\\w\,\.\…\=]+[\xA0 ]?)+|[\\r\\n]";
var pSelColor = AkelPad.GetArgValue("selcolor", "turquoise");
var nSepDigits = Number(AkelPad.GetArgValue("sepdigits", "1"));
var nClpType = Number(AkelPad.GetArgValue("clipboard", "0"));
var nBigOnes = Number(AkelPad.GetArgValue("bigones", "1"));
var nSelType = Number(AkelPad.GetArgValue("seltype", "2"));
if (pSelColor.toLowerCase() == "off") pSelColor = "";
if (!AkelPad.GetEditWnd()) WScript.Quit();
var nSelStart = nCrtStart = nExpStart = AkelPad.GetSelStart();
var nSelEnd = AkelPad.GetSelEnd();
var pSelText = pSelDzen = AkelPad.GetSelText();
var pDefColor = "#9BFFFF", nId = 11; /*turquoise*/
if (nClpType == 2)
{ pDefColor = "#FFCD9B"; nId = 12; /*orange*/
}
var pEqlOper = " = ", pExpTail = pDefTail = " ", bSelect = false;
var pEvlText, nEvlText;
// ----------------
// Извлекаем математическое выражение, если не выделено
if (!pSelText)
{ AkelPad.TextFind(0, pCalcCharsRegExp, 0x00180000 /*FRF_REGEXP|FRF_UP*/);
nSelStart = AkelPad.GetSelStart(); AkelPad.SetSel(nSelStart, nSelStart);
AkelPad.TextFind(0, pCalcCharsRegExp, 0x00080001 /*FRF_REGEXP|FRF_DOWN*/);
nExpStart = AkelPad.GetSelStart();
}
else // Обрабатываем, если таки выделено
{ AkelPad.SetSel(nSelStart, nSelStart);
AkelPad.TextFind(0, "[^\\r\\n]*\\S", 0x00080001 /*FRF_REGEXP|FRF_DOWN*/);
nSelStart = AkelPad.GetSelStart();
AkelPad.SetSel(nSelStart, nSelStart);
AkelPad.TextFind(0, "\\S", 0x00080001 /*FRF_REGEXP|FRF_DOWN*/);
nExpStart = AkelPad.GetSelStart();
if (nExpStart < nSelEnd)
{ AkelPad.SetSel(nSelStart, nSelEnd);
}
else
{ AkelPad.SetSel(nCrtStart, nSelEnd);
WScript.Quit();
}
} // Запоминаем параметры выделения
nSelStart = AkelPad.GetSelStart(); nSelEnd = AkelPad.GetSelEnd();
pSelText = pSelDzen = AkelPad.GetSelText();
// Сохраняем, какое у нас равенство
pEqlOper = pSelText.match(/([\$\€\%]?[\=]+)/g);
pEqlOper = " " + (pEqlOper ? pEqlOper[pEqlOper.length - 1] : "=") + " ";
if (pSelColor) // Устанавливаем цвет подсветки
{ switch (pSelColor.toLowerCase())
{ case "turquoise": case "бирюзовый": pSelColor = "#9BFFFF"; nId = 11; break;
case "orange": case "оранжевый": pSelColor = "#FFCD9B"; nId = 12; break;
case "yellow": case "желтый": pSelColor = "#FFFF9B"; nId = 13; break;
case "violet": case "фиолетовый": pSelColor = "#BE7DFF"; nId = 14; break;
case "green": case "зеленый": pSelColor = "#88E188"; nId = 15; break;
}
pSelColor = pSelColor.replace(/^[\#]*([0-9A-F]{6})[\S\s]*$/i, "#$1");
if (!pSelColor.match(/^[\#][0-9A-F]{6}$/i))
pSelColor = pDefColor;
} // Чистим захваченную ранее строку
if (nSepDigits) pSelText = pSelText.replace(/(\d)[ ](?=\d)/g, "$1'");
pSelText = pSelText.replace(/(\d)[\,](?=\d)/g, "$1.");
pSelText = pSelText.replace(/[\xA0]/g, " ");
// Отрабатываем "денежный" формат
if (pSelText.match(/[\$]+[\=]+/))
{ pSelText = pSelText.replace(/([\=]+[\s]*[\-]?[\d\'\.]+)[ ]*[а-я]+\./gi, "$1");
switch (true)
{ case Boolean(!pEqlOper.match(/[\$]+[\=]+/)): pExpTail = pDefTail; break;
case Boolean(pSelText.match(/((бук|бкз)\.|буказоид)/i)):
pExpTail = " бкз."; break;
case Boolean(pSelText.match(/(руб)(\.|л)/i)):
pExpTail = " руб."; break;
}
if (pExpTail != pDefTail) pExpTail += pDefTail;
} // Устанавливаем скрытые скобки
pSelText = pSelText.replace(/([\=]+[\s]*[\-]?[\d\'\.]+)([\s]*[\*\/])/g, "$1|$2");
// Убираем предыдущие вычисления
pSelText = pSelText.replace(/[\s]*[$€%]?[\=]+[\s]*([\-]?[\d\#\'\.]+|Error|$)/gi, "");
if (pSelText.match(/[\r\n]+[^\{\r\n]*[\}]/))
pSelText = pSelText.replace(/([\r\n]+)[\s]/g, "$1");
while (pSelText.match(/[\{]([\S\s]*?)[\}]/))
pSelText = pSelText.replace(/[\{]([\S\s]*?)[\s]*[\}]/, "$1");
pSelText = pSelText.replace(/[\s]+([\r\n]+|$)/g, "$1");
pSelText = pSelText.replace(/[\s]*[\#][\s]*$/, "");
// Обрабатываем переносы строк
pSelText = pSelText.replace(/([^\*\/\+\-\:\…\s])[\s]*([\r\n]+)/g, "$1 +$2");
pSelText = pSelText.replace(/([\*\/\+\-])[\s]*([\r\n]+)/g, "$1 …$2");
// Разворачиваем скрытые скобки
pSelText = pSelText.replace(/^([\|]+[\s]*)+/, "");
pSelText = pSelText.replace(/([\|])[\s]*(\d)/g, "$1 * $2");
pSelText = pSelText.replace(/[\s]+([\|])/g, "$1");
while (pSelText.match(/[\|]/))
pSelText = pSelText.replace(/^([\s]*)([\S\s]*?)[\|]+/, "$1($2)");
if (!pSelText.match(/[0-9a-z]/i)) // Выходим, если нечего считать
{ AkelPad.SetSel(nCrtStart, nCrtStart);
WScript.Quit();
} // Удаляем текст пользователя
pEvlText = pSelText.replace(/([\$\€\%а-яё\…_]+[\:\,\.]?[\s]?)+/gi,"");
pEvlText = pEvlText.replace(/(\d)[\' ](?=\d)/g, "$1");
if (pEvlText.match(/\d{4}\.\d/)) nBigOnes = 0; // Настраиваем для больших чисел
pEvlText = pEvlText.replace(/[\[]/g, "("); // Корректируем скобки
pEvlText = pEvlText.replace(/[\]]/g, ")");
try // Считаем...
{ with (Math)
pEvlText = String(nEvlText = Number(eval(pEvlText).toFixed(8)));
if (Math.abs(nEvlText) < 1e-6)
pEvlText = "0";
}
catch (oError)
{ pEvlText = "Error";
}
// Устанавливаем формат вывода
switch (true)
{ case Boolean(pEqlOper.match(/[\=]{2,}/)): break;
case Boolean(pEqlOper.match(/[\$\€\%]+[\=]+/)):
pEvlText = Number(pEvlText).toFixed(8);
pEvlText = pEvlText.replace(/(\.\d{2})\d*$/, "$1");
break;
case nBigOnes && Boolean(pEvlText.match(/^[\-]?\d{7,}[\.]/)):
pEvlText = pEvlText.replace(/(\.\d{0})\d*$/, "$1");
break;
case Boolean(pEvlText.match(/^[\-0]*\.0/)):
pEvlText = pEvlText.replace(/(\.\d{8})\d*$/, "$1");
break;
default:
pEvlText = pEvlText.replace(/(\.\d{4})\d*$/, "$1");
break;
}
// Расставляем апострофы в ответе
if (nSepDigits || pSelText.match(/[\']/))
{ if (pEvlText.match(/[\.]/))
pEvlText = pEvlText.replace(/(\d{2})(\d{3}\.\d*)$/, "$1'$2");
else
pEvlText = pEvlText.replace(/(\d{2})(\d{3})$/, "$1'$2");
while (pEvlText.match(/\d{4}\'/))
pEvlText = pEvlText.replace(/(\d)(\d{3}\')/, "$1'$2");
} // Обрабатываем "дзен"-режим
if (pSelDzen.match(/([\S\s]*[\r\n]){2,}[\s]*[\#][\s]*$/))
{ if (pSelDzen.match(/[\*\/\-\=][\s]*([\r\n]+|$)/)) pEvlText = "Error";
pSelDzen = pSelDzen.replace(/[\s]*[\#][\s]*$/, "");
pSelText = pSelDzen;
} // Проверяем на ошибки операций
if (pEvlText.match(/[a-z]+/i)) { pEvlText = "Error"; pExpTail = pDefTail; }
if (nClpType) // Копируем в буфер обмена
AkelPad.SetClipboardText(pEvlText);
// Форматируем переносы строк
if (pSelText.match(/[\r\n]+/))
{ switch (nSelType)
{ case 2: case 1: case 0:
pSelText += evlPadding(pSelText, pEvlText, pExpTail);
pExpTail += "\r\n";
break;
default:
pSelText = pSelText.replace(/[\…]/g, "");
pDefTail += "\r\n";
break;
}
pSelColor = "";
}
function evlPadding(vExpress, pEval, pTail)
{ var pPadding = "\r\n", nPadLen = 0;
vExpress = vExpress.replace(/[\s]+([\r\n]+|$)/g, "$1");
vExpress = vExpress.match(/[^\r\n]+/g);
for (var i = 0; i < vExpress.length; i++)
if (nPadLen < vExpress[i].length)
nPadLen = vExpress[i].length;
nPadLen -= pEval.length + pTail.length + 6;
for (i = 0; i < nPadLen; i++) pPadding += " ";
return pPadding;
}
// Отрабатываем аргументы
switch (nSelType)
{ case 2:
pSelText = pSelText.replace(/^([\s]*)([\S\s]*)$/, "$1{$2}");
pSelText = pSelText.replace(/([\r\n]+)/g, "$1 ");
pSelText += pEqlOper + pEvlText + pExpTail;
bSelect = false;
break;
case 1:
pSelText += pEqlOper + pEvlText + pExpTail;
bSelect = true;
break;
case 0:
pSelText += pEqlOper + pEvlText + pExpTail;
bSelect = false;
pSelColor = "";
break;
default:
pSelText += pDefTail;
break;
}
// Выводим результат
AkelPad.ReplaceSel(pSelText, -1);
nSelStart = AkelPad.GetSelStart(); nSelEnd = AkelPad.GetSelEnd();
if (pSelColor && AkelPad.IsPluginRunning("Coder::HighLight"))
{ AkelPad.SetSel(nExpStart, nSelEnd - 1);
AkelPad.Call("Coder::HighLight", 2, 0, pSelColor, 1, 0, nId);
AkelPad.SetSel(nSelEnd, nSelEnd);
}
else
{ if (bSelect)
AkelPad.SetSel(nSelStart, nSelEnd);
else
AkelPad.SetSel(nSelEnd, nSelEnd);
}
Last edited by xOleg on Fri Apr 18, 2025 4:28 pm, edited 14 times in total.
-
Offline
- Posts: 5
- Joined: Tue Nov 12, 2024 10:28 pm
Re: Scripts collection
Исправление "на лету" (без выделения, по горячей клавише) текста, набранного в неверной раскладке. Клавиатуру тоже переключит.
Code: Select all
//
// Description(1049): Исправление "на лету" текста, набранного в неверной раскладке.
// Description(1033): You don't need this.
//
// https://akelpad.sourceforge.net/forum/viewtopic.php?p=36613#p36613
// Author: xOleg
// Version: 1.8
//
// Переводит слово под курсором или слева от курсора в иную раскладку. Локальные
// страницы 1049 и 1033. Выделять не обязательно, но возможно, чтобы указать скрипту,
// что исправлять. Клавиатуру тоже переключит. Удобно повесить на какую-нибудь
// комбинацию с Backspace. Например, Shift + Backspace, если она у вас не занята.
//
// Вызов горячей клавишей:
// Call("Scripts::Main", 1, "KeybMagic.js")
//
// Примеры:
// Ghbdtn! -> Привет! Array[ш]; -> Array[i];
// 10ю12ю24 -> 10.12.24 "Вщддн" -> "Dolly"
// (Руддщ!) -> (Hello!)
//
var pEngCapsOn = "QWERTYUIOP_\\{\\}ASDFGHJKL\\:\\\"ZXCVBNM\\<\\>\\~\\#\\?";
var pEngCapsOff = "qwertyuiop\\[\\]asdfghjkl\\;\\'zxcvbnm\\,\\.\\`\\|\\/";
var pRusCapsOn = "ЙЦУКЕНГШЩЗ__Х_ЪФЫВАПРОЛД_Ж_ЭЯЧСМИТЬ_Б_Ю_Ё_№\\,";
var pRusCapsOff = "йцукенгшщз_х_ъфывапролд_ж_эячсмить_б_ю_ё\\/\\.";
var pCommon = "\\!\\?\\%\\*\\/\\+\\-\\{\\}\\[\\]\\(\\)0123456789_";
pCommon += "\\\"\\'\\<\\>\\:\\;\\,\\.\\=";
var EngKeyboard = new Keyboard(pEngCapsOn + pEngCapsOff + pCommon, 1033);
var RusKeyboard = new Keyboard(pRusCapsOn + pRusCapsOff + pCommon, 1049);
function Keyboard(pLayout, nLocale) // Клавиатура, как она есть
{ this.layout = pLayout + " "; this.locale = nLocale;
this.explayout = new RegExp("^[" + pLayout + "]+$");
this.expcommon = new RegExp("^[" + pCommon + "]+$");
this.Extend = function() { return new RegExp("^[" + this.layout + "]+$"); }
this.Select = function(nCrtPosition) // Ищем и выделяем, что исправлять
{ var pSelText = "", i = 1, j = 1;
// Сперва ищем до конца слова
do
{ AkelPad.SetSel(nCrtPosition, nCrtPosition + i++);
pSelText = AkelPad.GetSelText();
if (pSelText.length < i - 1) break;
}
while (pSelText.match(this.explayout));
nCrtPosition = nCrtPosition + i - 2;
// Теперь ищем до начала слова
do
{ if (nCrtPosition - j < 0) { j++; break; }
AkelPad.SetSel(nCrtPosition - j++, nCrtPosition);
pSelText = AkelPad.GetSelText();
}
while (pSelText.match(this.explayout));
// Отдаем результат
AkelPad.SetSel(nCrtPosition - j + 2, nCrtPosition);
pSelText = AkelPad.GetSelText();
if (pSelText.match(this.expcommon))
{ AkelPad.SetSel(nCrtPosition, nCrtPosition);
pSelText = "";
}
return pSelText;
}
}
// Блок определений и переменных
// ----------------
var nIsSelect = Number(AkelPad.GetArgValue("isselect", "0"));
if (nIsSelect > 0) nIsSelect = -1;
AkelPad.SetSel(AkelPad.GetSelStart(), AkelPad.GetSelEnd());
if (!AkelPad.GetEditWnd()) WScript.Quit();
var nCrtPlace = AkelPad.GetSelStart();
var pSelText = AkelPad.GetSelText();
var rExp = new RegExp("^[" + pCommon + "\\s\\r\\n]+$");
if(pSelText.match(rExp)) WScript.Quit();
var pSrcLayout = "", pTgtLayout = "";
var nTgtLocale = 1033;
var pNewText = "";
// ----------------
// Пользователь выделил текст. Проверяем на буржуйскую
if (AkelPad.GetSelText()) // раскладку
{ switch (true)
{ case Boolean(pSelText.match(EngKeyboard.Extend())):
nTgtLocale = RusKeyboard.locale; nIsSelect = -1;
pSrcLayout = EngKeyboard.layout;
pTgtLayout = RusKeyboard.layout;
break; // Проверяем на российскую раскладку
case Boolean(pSelText.match(RusKeyboard.Extend())):
nTgtLocale = EngKeyboard.locale; nIsSelect = -1;
pSrcLayout = RusKeyboard.layout;
pTgtLayout = EngKeyboard.layout;
break;
}
}
// Пользователь не выделил текст. Ищем слово слева
if (!AkelPad.GetSelText())
{ AkelPad.TextFind(0, "([^\\xA0\\s]|[\\r\\n])", 0x00180000); //FRF_REGEXP|FRF_UP
nCrtPlace = AkelPad.GetSelEnd();
AkelPad.SetSel(nCrtPlace, nCrtPlace);
// Проверяем на буржуйскую раскладку
switch (true)
{ case Boolean(EngKeyboard.Select(nCrtPlace)):
nTgtLocale = RusKeyboard.locale; nIsSelect = 0;
pSrcLayout = EngKeyboard.layout;
pTgtLayout = RusKeyboard.layout;
break; // Проверяем на российскую раскладку
case Boolean(RusKeyboard.Select(nCrtPlace)):
nTgtLocale = EngKeyboard.locale; nIsSelect = 0;
pSrcLayout = RusKeyboard.layout;
pTgtLayout = EngKeyboard.layout;
break;
}
}
// Исправляем...
pSelText = AkelPad.GetSelText();
for (var i = 0; i < pSelText.length; i++)
{ pNewText += pTgtLayout.charAt(pSrcLayout.indexOf(pSelText.charAt(i)));
}
// Корректируем...
rExp = new RegExp("[Х](.*?)Ъ(.?([^А-Я]|$))", "g");
while (pNewText.match(rExp)) pNewText = pNewText.replace(rExp, "{$1}$2");
rExp = new RegExp ("Ъ(.?([^А-Я]|$))", "g");
while (pNewText.match(rExp)) pNewText = pNewText.replace(rExp, "}$1");
rExp = new RegExp("[х](.*?)ъ(.?([^а-я]|$))", "g");
while (pNewText.match(rExp)) pNewText = pNewText.replace(rExp, "[$1]$2");
rExp = new RegExp ("ъ(.?([^а-я]|$))", "g");
while (pNewText.match(rExp)) pNewText = pNewText.replace(rExp, "]$1");
rExp = new RegExp("[Э](.*?[а-я]+[^\\s]?[\\\"]*)Э");
while (pNewText.match(rExp)) pNewText = pNewText.replace(rExp, "\"$1\"");
rExp = new RegExp ("([а-я]+[^\\s]?[\\\"]*)Э");
while (pNewText.match(rExp)) pNewText = pNewText.replace(rExp, "$1\"");
pNewText = pNewText.replace(/([а-я]+[\}\]\)\"\.]*)Ж/g, "$1:");
pNewText = pNewText.replace(/([а-я][\}\]\)\"]+)ж/g, "$1;");
pNewText = pNewText.replace(/([а-я][\}\]\)\"]+)б/g, "$1,");
pNewText = pNewText.replace(/([а-я][\}\]\)\"]+)ю/g, "$1.");
// Заменяем...
if (pNewText)
{ AkelPad.SendMessage(AkelPad.GetEditWnd(), 0x50, 0, nTgtLocale);
AkelPad.ReplaceSel(pNewText, nIsSelect);
}
if(!nIsSelect)
AkelPad.SetSel(nCrtPlace, nCrtPlace);
Last edited by xOleg on Thu Apr 10, 2025 9:49 pm, edited 1 time in total.
-
Offline
- Posts: 43
- Joined: Sat Jul 05, 2008 11:30 am
- Location: Odesa, Ukraine
Re: Scripts collection
Sum all numbers in a text/selection and place the resulting expression = sum under the text.
Mode one: sum numbers respecting the minus sign in front of a number as a part of the expression (default).
Mode two: sum all numbers regardless of the minus sign.
Video illustration: https://i.imgur.com/eRtKa55.mp4
Mode one: sum numbers respecting the minus sign in front of a number as a part of the expression (default).
Mode two: sum all numbers regardless of the minus sign.
Video illustration: https://i.imgur.com/eRtKa55.mp4
Code: Select all
// https://akelpad.sourceforge.net/forum/viewtopic.php?t=240&p=36657#p36657
// description: sum all numbers in a text/selection and put the resulting expression = sum under the text
// * mode one: numbers with the minus sign are negative (default)
// * mode two: numbers are positive regardless of the minus sign
// version: 2024-12-24
// author: ewild
// examples:
// "Sum in-text numbers" Call("Scripts::Main",1,"mathText.js")
// "Sum in-text numbers (all as positive)" Call("Scripts::Main",1,"mathText.js","+")
$start=AkelPad.GetSelStart();
$end=AkelPad.GetSelEnd();
if ($start == $end){AkelPad.SetSel(0,-1);$cr='';} else {$cr='\r';}
$text=AkelPad.GetSelText();
$ArgLine=AkelPad.GetArgLine();
if ($text.match(/\d/g)){ // do math if at least one digit exists in a text/selection
if ($ArgLine){ // make sum expression regardless of the minus sign
$math=$text.match(/\d+(\.\d+)?/g).join('+'); // extract and join numbers
$math=$math.replace(/^[0]+(\d)/g,"$1").replace(/([+])[0]+(\d)/g,"$1$2");} // remove meaningless zeros
if (!$ArgLine){ // make sum expression retaining the minus sign
$math=$text.match(/-?\d+(\.\d+)?/g).join('+').replace(/[+][-]/g,"-");
$math=$math.replace(/^[-]*[0]+(\d)/g,"$1").replace(/([+-])[0]+(\d)/g,"$1$2");}
$text=$text+'\r'+$math+' = '+eval($math)+$cr; // add the expression and the calculated sum to the text
AkelPad.ReplaceSel($text);
AkelPad.SetSel($start,$start);
}
-
Offline
- Posts: 276
- Joined: Mon Jun 20, 2011 8:33 am
- Location: Электросталь
Re: Scripts collection
Поправка к скрипту AkelPadManualSettings.js от KDJ: добавлен новый параметр ScrollPastEOF
Code: Select all
--- orig/AkelPadManualSettings.js Tue Jul 19 18:00:00 2016
+++ fixd/AkelPadManualSettings.js Tue May 27 19:10:30 2025
@@ -2 +2 @@
-// Version: 2016-07-19
+// Version: 2016-07-19 fix 1
@@ -11,0 +12,3 @@
+//
+// Fixes:
+// 27.05.2025 - Added: ScrollPastEOF option
@@ -752,0 +756,62 @@
+var oScrollPastEOF =
+{
+ Def :0,
+ Wnd :[],
+ IDTXT:2001,
+ IDVAL:2002,
+ IDDEF:2003,
+
+ Initialize:function()
+ {
+ this.Val = SendMessage(hMainWnd, 1222 /*AKD_GETMAININFO*/, 119 /*MIS_SCROLLPASTEOF*/, 0);
+ this.Ini = this.Val;
+
+ this.Wnd[this.IDTXT]={C:'STATIC', S:0x50000000, X:140, Y:15, W:640, H:13, Txt:'Vertical scroll beyond last line. As a percentage of the editing area height:',
+ Rus:'Вертикальная прокрутка за пределы последней строки. В процентах от высоты области редактирования:'};
+ this.Wnd[this.IDVAL]={C:'COMBOBOX', S:0x50210003, X:140, Y:35, W: 50, H:21};
+ this.Wnd[this.IDDEF]={C:'BUTTON', S:0x50010003, X:140, Y:66, W:640, H:16, Txt:'By default: 0 - turned off.',
+ Rus:'По умолчанию: 0 - отключено.'};
+ },
+
+ Apply:function()
+ {
+ SendMessage(hMainWnd, 1219 /*AKD_SETMAININFO*/, 119 /*MIS_SCROLLPASTEOF*/, this.Val);
+ },
+
+ SetVal:function()
+ {
+ SendMessage(this.Wnd[this.IDVAL].HWND, 0x014E /*CB_SETCURSEL*/, SendMessage(this.Wnd[this.IDVAL].HWND, 0x0158 /*CB_FINDSTRINGEXACT*/, -1, this.Val.toString()), 0);
+ SendMessage(this.Wnd[this.IDDEF].HWND, 241 /*BM_SETCHECK*/, this.Val == this.Def, 0);
+ },
+
+ InitDialog:function()
+ {
+ for (var i = 0; i < 101; ++i)
+ SendMessage(this.Wnd[this.IDVAL].HWND, 0x0143 /*CB_ADDSTRING*/, 0, i.toString());
+ if (this.Ini > 100)
+ SendMessage(this.Wnd[this.IDVAL].HWND, 0x0143 /*CB_ADDSTRING*/, 0, this.Ini.toString());
+ },
+
+ Command:function(nID, nCode, hWnd)
+ {
+ if (nID == this.IDVAL)
+ {
+ if (nCode == 1 /*CBN_SELCHANGE*/)
+ {
+ bChanged = 1;
+ SendMessage(hWnd, 0x0148 /*CB_GETLBTEXT*/, SendMessage(hWnd, 0x0147 /*CB_GETCURSEL*/, 0, 0), lpText);
+ this.Val = parseInt(AkelPad.MemRead(lpText, 1 /*DT_UNICODE*/));
+ SendMessage(this.Wnd[this.IDDEF].HWND, 241 /*BM_SETCHECK*/, this.Val == this.Def, 0);
+ }
+ else if (nCode == 8 /*CBN_CLOSEUP*/)
+ bCloseCB = 0;
+ }
+ else if ((nID == this.IDDEF) && SendMessage(hWnd, 240 /*BM_GETCHECK*/, 0, 0))
+ {
+ bChanged = 1;
+ this.Val = this.Def;
+ this.SetVal();
+ }
+ }
+};
+
@@ -1472,0 +1538 @@
+ ["ScrollPastEOF", oScrollPastEOF],
Code: Select all
MIME-Version: 1.0
Content-Type: application/octet-stream; name="AkelPadManualSettings.7z"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="AkelPadManualSettings.7z"
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
LTtH3iHP2wEVBgEAIAAAAAAA