initial commit

This commit is contained in:
Jason Barnabe 2012-04-15 20:56:12 -05:00
commit b019038f57
25 changed files with 2504 additions and 0 deletions

BIN
128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

BIN
19.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

BIN
48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

151
_locales/de/messages.json Normal file
View File

@ -0,0 +1,151 @@
{
"addStyleTitle": {
"message": "Neuer Stil"
},
"appliesAdd": {
"message": "Hinzufügen"
},
"appliesDisplay": {
"message": "Wird angewendet auf: $applies$",
"placeholders": {
"applies": {
"content": "$1"
}
}
},
"appliesDisplayTruncatedSuffix": {
"message": "und mehr"
},
"appliesDomainOption": {
"message": "URLs auf der Domäne"
},
"appliesHelp": {
"message": "Nutzen Sie \"Anwenden auf\" um festzulegen, auf welche URLs der Code in diesem Abschnitt angewendet wird."
},
"appliesLabel": {
"message": "Anwenden auf"
},
"appliesRemove": {
"message": "Entfernen"
},
"appliesSpecify": {
"message": "Genauer..."
},
"appliesToEverything": {
"message": "Alles"
},
"appliesUrlOption": {
"message": "URL"
},
"appliesUrlPrefixOption": {
"message": "URLs beginnend mit"
},
"checkForUpdate": {
"message": "Nach Update suchen"
},
"checkingForUpdate": {
"message": "Suche..."
},
"deleteStyleLabel": {
"message": "Entfernen"
},
"deleteStyleConfirm": {
"message": "Möchten Sie diesen Stil wirklich entfernen?"
},
"disableStyleLabel": {
"message": "Deaktivieren"
},
"editStyleHeading": {
"message": "Stil bearbeiten"
},
"editStyleLabel": {
"message": "Bearbeiten"
},
"editStyleTitle": {
"message": "\"$stylename$\" bearbeiten",
"placeholders": {
"stylename": {
"content": "$1"
}
}
},
"enableStyleLabel": {
"message": "Aktivieren"
},
"findStylesForSite": {
"message": "Stile für diese Seite suchen"
},
"helpAlt": {
"message": "Hilfe"
},
"installUpdate": {
"message": "Update installieren"
},
"manageHeading": {
"message": "Installierte Stile"
},
"manageTitle": {
"message": "Stylish"
},
"noStylesForSite": {
"message": "Für diese Seite wurden keine Stile installiert."
},
"sectionAdd": {
"message": "Neuen Abschnitt hinzufügen"
},
"sectionCode": {
"message": "Code"
},
"sectionHelp": {
"message": "Abschnitte unterteilen den Code eines Stils in verschiedene Teile, die auf verschiedene URLs angewendet werden. So könnte z.B. ein Stil die Startseite einer Website anders gestalten als den Rest der Website."
},
"sectionRemove": {
"message": "Abschnitt löschen"
},
"styleCancelEditLabel": {
"message": "Zurück zur Übersicht"
},
"styleChangesNotSaved": {
"message": "Sie haben Änderungen an diesem Stil noch nicht gespeichert."
},
"styleEnabledLabel": {
"message": "Aktiviert"
},
"styleInstall": {
"message": "Möchten Sie \"$stylename$\" mit Stylish installieren?",
"placeholders": {
"stylename": {
"content": "$1"
}
}
},
"styleMissingName": {
"message": "Geben Sie einen Namen ein."
},
"styleNameLabel": {
"message": "Name"
},
"styleSaveLabel": {
"message": "Speichern"
},
"styleSectionsTitle": {
"message": "Abschnitte"
},
"updateCheckFailBadResponseCode": {
"message": "Update gescheitert - Server meldet Code $code$.",
"placeholders": {
"code": {
"content": "$1"
}
}
},
"updateCheckFailServerUnreachable": {
"message": "Update gescheitert - Server nicht erreichbar."
},
"updateCheckSucceededNoUpdate": {
"message": "Stil ist aktuell."
},
"updateCompleted": {
"message": "Update fertiggestellt."
}
}

226
_locales/en/messages.json Normal file
View File

@ -0,0 +1,226 @@
{
"addStyleLabel": {
"message": "Write new style",
"description": "Label for the button to go to the add style page"
},
"addStyleTitle": {
"message": "Add Style",
"description": "Title of the page for adding styles"
},
"appliesAdd": {
"message": "Add",
"description": "Label for the button to add an 'applies' entry"
},
"appliesDisplay": {
"message": "Applies to: $applies$",
"description": "Text on the manage screen to describe what the style applies to",
"placeholders": {
"applies": {
"content": "$1"
}
}
},
"appliesDisplayTruncatedSuffix": {
"message": "and more",
"description": "Text added to appliesDisplay when there are more sites for the style than are displayed"
},
"appliesDomainOption": {
"message": "URLs on the domain",
"description": "Option to make the style apply to the entered string as a domain"
},
"appliesHelp": {
"message": "Use the 'Applies to' controls to limit what URLs the code in this section applies to.",
"description": "Help text for 'applies to' section"
},
"appliesLabel": {
"message": "Applies to",
"description": "Label for 'applies to' fields on the edit\/add screen"
},
"appliesRegexpOption": {
"message": "URLs matching the regexp",
"description": "Option to make the style apply to the entered string as a regular expression"
},
"appliesRemove": {
"message": "Remove",
"description": "Label for the button to remove an 'applies' entry"
},
"appliesSpecify": {
"message": "Specify",
"description": "Label for the button to make a style apply only to specific sites"
},
"appliesToEverything": {
"message": "Everything",
"description": "Text displayed for styles that apply to all sites"
},
"appliesUrlOption": {
"message": "URL",
"description": "Option to make the style apply to the entered string as a URL"
},
"appliesUrlPrefixOption": {
"message": "URLs starting with",
"description": "Option to make the style apply to the entered string as a URL prefix"
},
"checkAllUpdates": {
"message": "Check all styles for updates",
"description": "Label for the button to check all styles for updates"
},
"checkForUpdate": {
"message": "Check for update",
"description": "Label for the button to check a single style for an update"
},
"checkingForUpdate": {
"message": "Checking...",
"description": "Text to display when checking a style for an update"
},
"deleteStyleLabel": {
"message": "Delete",
"description": "Label for the button to delete a style"
},
"deleteStyleConfirm": {
"message": "Are you sure you want to delete this style?",
"description": "Confirmation before deleting a style"
},
"description": {
"message": "Restyle the web with Stylish, a user styles manager. Stylish lets you easily install themes and skins for Google, Facebook, YouTube, Orkut, and many, many other sites.",
"description": "Extension description"
},
"disableStyleLabel": {
"message": "Disable",
"description": "Label for the button to disable a style"
},
"editStyleHeading": {
"message": "Edit Style",
"description": "Title of the page for editing styles"
},
"editStyleLabel": {
"message": "Edit",
"description": "Label for the button to go to the edit style page"
},
"editStyleTitle": {
"message": "Edit Style $stylename$",
"description": "Title of the page for editing styles",
"placeholders": {
"stylename": {
"content": "$1"
}
}
},
"enableStyleLabel": {
"message": "Enable",
"description": "Label for the button to enable a style"
},
"findStylesForSite": {
"message": "Find more styles for this site.",
"description": "Text for a link that gets a list of styles for the current site"
},
"helpAlt": {
"message": "Help",
"description": "Alternate text for help buttons"
},
"installUpdate": {
"message": "Install update",
"description": "Label for the button to install an update for a single style"
},
"manageHeading": {
"message": "Installed Styles",
"description": "Heading for the manage page"
},
"manageText": {
"message": "Visit <a href='http:\/\/userstyles.org'>userstyles.org<\/a> for pre-made styles. Ask in <a href='http:\/\/forum.userstyles.org'>the forum<\/a> if you need help.",
"description": "Help text on the manage page"
},
"manageTitle": {
"message": "Stylish",
"description": "Title for the manage page"
},
"noStylesForSite": {
"message": "No styles installed for this site.",
"description": "Text displayed when no styles are installed for the current site"
},
"openManage": {
"message": "Manage installed styles.",
"description": "Link to open the manage page."
},
"sectionAdd": {
"message": "Add another section",
"description": "Label for the button to add a section"
},
"sectionCode": {
"message": "Code",
"description": "Label for the code for a section"
},
"sectionHelp": {
"message": "Sections let you define different pieces of code to apply to different sets of URLs in the same style. For example, a single style could change the homepage of a site one way, while changing the rest of a site another way.",
"description": "Help text for sections"
},
"sectionRemove": {
"message": "Remove section",
"description": "Label for the button to remove a section"
},
"styleCancelEditLabel": {
"message": "Back to manage",
"description": "Label for cancel button for style editing"
},
"styleChangesNotSaved": {
"message": "You've made changes to this style without saving.",
"description": "Text for the prompt when changes are made to a style and the user tries to leave without saving"
},
"styleEnabledLabel": {
"message": "Enabled",
"description": "Label for the enabled state of styles"
},
"styleInstall": {
"message": "Install '$stylename$' into Stylish?",
"description": "Confirmation when installing a style",
"placeholders": {
"stylename": {
"content": "$1"
}
}
},
"styleMissingName": {
"message": "Enter a name.",
"description": "Error displayed when user saves without providing a name"
},
"styleNameLabel": {
"message": "Name",
"description": "Label for the name of styles"
},
"styleSaveLabel": {
"message": "Save",
"description": "Label for save button for style editing"
},
"styleSectionsTitle": {
"message": "Sections",
"description": "Title for the style sections section"
},
"styleToMozillaFormat": {
"message": "To Mozilla format",
"description": "Label for the button that converts the code to Mozilla format"
},
"styleToMozillaFormatHelp": {
"message": "The Mozilla format of the code can be used with Stylish for Firefox and can be submitted to userstyles.org.",
"description": "Help info for the button that converts the code to Mozilla format"
},
"updateCheckFailBadResponseCode": {
"message": "Update failed - server responded with code $code$.",
"description": "Text that displays when an update check failed because the response code indicates an error",
"placeholders": {
"code": {
"content": "$1"
}
}
},
"updateCheckFailServerUnreachable": {
"message": "Update failed - server unreachable.",
"description": "Text that displays when an update check failed because the update server is unreachable"
},
"updateCheckSucceededNoUpdate": {
"message": "Style is up to date.",
"description": "Text that displays when an update check completed and no update is available"
},
"updateCompleted": {
"message": "Update completed.",
"description": "Text that displays when an update completed"
}
}

148
_locales/es/messages.json Normal file
View File

@ -0,0 +1,148 @@
{
"addStyleTitle": {
"message": "Añadir Estilo"
},
"appliesAdd": {
"message": "Añadir"
},
"appliesDisplay": {
"message": "Aplica para: $applies$",
"placeholders": {
"applies": {
"content": "$1"
}
}
},
"appliesDisplayTruncatedSuffix": {
"message": "y más"
},
"appliesDomainOption": {
"message": "URL en el dominio"
},
"appliesHelp": {
"message": "Use los controles de \"Aplicar a\" para limitar a cuales URL aplica el código en esta sección."
},
"appliesLabel": {
"message": "Aplica a"
},
"appliesRemove": {
"message": "Eliminar"
},
"appliesSpecify": {
"message": "Especificar"
},
"appliesToEverything": {
"message": "Todo"
},
"appliesUrlOption": {
"message": "URL"
},
"appliesUrlPrefixOption": {
"message": "URL comenzando con"
},
"checkForUpdate": {
"message": "Revisar actualizaciones"
},
"checkingForUpdate": {
"message": "Verificando..."
},
"deleteStyleLabel": {
"message": "Borrar"
},
"deleteStyleConfirm": {
"message": "¿Estás seguro de que quieres eliminar este estilo?"
},
"disableStyleLabel": {
"message": "Desactivar"
},
"editStyleHeading": {
"message": "Editar Estilo"
},
"editStyleLabel": {
"message": "Editar"
},
"editStyleTitle": {
"message": "Editar Estilo $stylename$",
"placeholders": {
"stylename": {
"content": "$1"
}
}
},
"enableStyleLabel": {
"message": "Activar"
},
"findStylesForSite": {
"message": "Encontrar más estilos para este sitio."
},
"helpAlt": {
"message": "Ayuda"
},
"installUpdate": {
"message": "Instalar actualización"
},
"manageHeading": {
"message": "Estilos Instalados"
},
"manageTitle": {
"message": "Stylish"
},
"noStylesForSite": {
"message": "No hay estilos instalados para este sitio."
},
"sectionAdd": {
"message": "Añadir otra sección"
},
"sectionHelp": {
"message": "Las secciones te permiten definir diferentes trozos de código a ser aplicados a diferentes juegos de URL en el mismo estilo. Por ejemplo, un estilo particular puede cambiar la página de inicio de un sitio de una forma, mientras cambia el resto del sitio de otra."
},
"sectionRemove": {
"message": "Eliminar sección"
},
"styleCancelEditLabel": {
"message": "Atrás para manejar"
},
"styleChangesNotSaved": {
"message": "Has realizado cambios a este estilo sin guardar."
},
"styleEnabledLabel": {
"message": "Activado"
},
"styleInstall": {
"message": "¿Instalar \"$stylename$\" en Stylish?",
"placeholders": {
"stylename": {
"content": "$1"
}
}
},
"styleMissingName": {
"message": "Ingrese un nombre."
},
"styleNameLabel": {
"message": "Nombre"
},
"styleSaveLabel": {
"message": "Guardar"
},
"styleSectionsTitle": {
"message": "Secciones"
},
"updateCheckFailBadResponseCode": {
"message": "Actualización fallida - el servidor respondió con el código $code$.",
"placeholders": {
"code": {
"content": "$1"
}
}
},
"updateCheckFailServerUnreachable": {
"message": "Actualización fallida - no se encuentra el servidor."
},
"updateCheckSucceededNoUpdate": {
"message": "El estilo se actualizó."
},
"updateCompleted": {
"message": "Actualización completa."
}
}

55
_locales/te/messages.json Normal file
View File

@ -0,0 +1,55 @@
{
"appliesAdd": {
"message": "చేర్చు"
},
"appliesDisplay": {
"message": "వేటికి వర్తిస్తుంది; $applies$",
"placeholders": {
"applies": {
"content": "$1"
}
}
},
"appliesDisplayTruncatedSuffix": {
"message": "ఇంకా మరిన్ని"
},
"appliesRemove": {
"message": "తొలగించు"
},
"appliesToEverything": {
"message": "అన్నిటికీ"
},
"deleteStyleLabel": {
"message": "తొలగించు"
},
"deleteStyleConfirm": {
"message": "మీరు నజంగానే ఈ శైలిని తొలగించాలనుకుంటున్నారా?"
},
"disableStyleLabel": {
"message": "అచేతనించు"
},
"editStyleLabel": {
"message": "మార్చు"
},
"enableStyleLabel": {
"message": "చేతనించు"
},
"helpAlt": {
"message": "సహాయం"
},
"manageHeading": {
"message": "స్థాపిత శైలులు"
},
"manageTitle": {
"message": "స్టైలిష్"
},
"styleNameLabel": {
"message": "పేరు"
},
"styleSaveLabel": {
"message": "భద్రపరచు"
},
"styleSectionsTitle": {
"message": "విభాగాలు"
}
}

175
_locales/zh/messages.json Normal file
View File

@ -0,0 +1,175 @@
{
"addStyleLabel": {
"message": "编写新的样式"
},
"addStyleTitle": {
"message": "添加样式"
},
"appliesAdd": {
"message": "添加"
},
"appliesDisplay": {
"message": "应用到: $applies$",
"placeholders": {
"applies": {
"content": "$1"
}
}
},
"appliesDisplayTruncatedSuffix": {
"message": "以及更多"
},
"appliesDomainOption": {
"message": "指定域上的URL"
},
"appliesHelp": {
"message": "使用“应用到”来控制这个样式应用到哪些地址上。"
},
"appliesLabel": {
"message": "应用到"
},
"appliesRegexpOption": {
"message": "匹配正则的URL"
},
"appliesRemove": {
"message": "移除"
},
"appliesSpecify": {
"message": "指定站点"
},
"appliesToEverything": {
"message": "所有站点"
},
"appliesUrlOption": {
"message": "地址"
},
"appliesUrlPrefixOption": {
"message": "以指定地址开始"
},
"checkAllUpdates": {
"message": "检查所有样式的更新"
},
"checkForUpdate": {
"message": "检查更新"
},
"checkingForUpdate": {
"message": "检查中..."
},
"deleteStyleLabel": {
"message": "删除"
},
"deleteStyleConfirm": {
"message": "确定要删除这个样式吗?"
},
"description": {
"message": "Stylish一个用户样式管理器帮助您重新定义网页样式。Stylish易于为GoogleFacebookYoutubeOrkut以及其它各类型网站安装样式和主题。"
},
"disableStyleLabel": {
"message": "禁用"
},
"editStyleHeading": {
"message": "编辑样式"
},
"editStyleLabel": {
"message": "编辑"
},
"editStyleTitle": {
"message": "编辑样式 $stylename$",
"placeholders": {
"stylename": {
"content": "$1"
}
}
},
"enableStyleLabel": {
"message": "启用"
},
"findStylesForSite": {
"message": "查找更多适合此站点的样式"
},
"helpAlt": {
"message": "帮助"
},
"installUpdate": {
"message": "安装更新"
},
"manageHeading": {
"message": "已安装的样式"
},
"manageText": {
"message": "访问 <a href='http:\/\/userstyles.org'>userstyles.org<\/a> 查找预定义的样式. 如果您需要帮助,请访问 <a href='http:\/\/forum.userstyles.org'>论坛<\/a>."
},
"manageTitle": {
"message": "Stylish"
},
"noStylesForSite": {
"message": "当前站点上没有已安装的样式"
},
"openManage": {
"message": "管理已安装样式"
},
"sectionAdd": {
"message": "添加新节"
},
"sectionCode": {
"message": "代码"
},
"sectionHelp": {
"message": "样式节允许你定义多个样式段落并将它们应用于不同的站点匹配规则上。例如,一个样式可以更改使用一个方式来更改主页,而此时其它的样式段可以更改站点的其它部分。"
},
"sectionRemove": {
"message": "移除节"
},
"styleCancelEditLabel": {
"message": "返回到管理"
},
"styleChangesNotSaved": {
"message": "您已经修改了此样式,但尚未保存"
},
"styleEnabledLabel": {
"message": "已启用"
},
"styleInstall": {
"message": "安装 '$stylename$' 到 Stylish?",
"placeholders": {
"stylename": {
"content": "$1"
}
}
},
"styleMissingName": {
"message": "请输入名称."
},
"styleNameLabel": {
"message": "名称"
},
"styleSaveLabel": {
"message": "保存"
},
"styleSectionsTitle": {
"message": "样式节"
},
"styleToMozillaFormat": {
"message": "转换为Mozilla格式"
},
"styleToMozillaFormatHelp": {
"message": "用于Firefox上的Stylish的Mozilla格式样式代码可以在 userstyles.org 上提交"
},
"updateCheckFailBadResponseCode": {
"message": "更新失败 - 服务器了返回代码 $code$.",
"placeholders": {
"code": {
"content": "$1"
}
}
},
"updateCheckFailServerUnreachable": {
"message": "更新失败 - 服务器无法访问."
},
"updateCheckSucceededNoUpdate": {
"message": "已经是最新的."
},
"updateCompleted": {
"message": "更新完成."
}
}

View File

@ -0,0 +1,175 @@
{
"addStyleLabel": {
"message": "编写新的样式"
},
"addStyleTitle": {
"message": "添加样式"
},
"appliesAdd": {
"message": "添加"
},
"appliesDisplay": {
"message": "应用到: $applies$",
"placeholders": {
"applies": {
"content": "$1"
}
}
},
"appliesDisplayTruncatedSuffix": {
"message": "以及更多"
},
"appliesDomainOption": {
"message": "指定域上的URL"
},
"appliesHelp": {
"message": "使用“应用到”来控制这个样式应用到哪些地址上。"
},
"appliesLabel": {
"message": "应用到"
},
"appliesRegexpOption": {
"message": "匹配正则的URL"
},
"appliesRemove": {
"message": "移除"
},
"appliesSpecify": {
"message": "指定站点"
},
"appliesToEverything": {
"message": "所有站点"
},
"appliesUrlOption": {
"message": "地址"
},
"appliesUrlPrefixOption": {
"message": "以指定地址开始"
},
"checkAllUpdates": {
"message": "检查所有样式的更新"
},
"checkForUpdate": {
"message": "检查更新"
},
"checkingForUpdate": {
"message": "检查中..."
},
"deleteStyleLabel": {
"message": "删除"
},
"deleteStyleConfirm": {
"message": "确定要删除这个样式吗?"
},
"description": {
"message": "Stylish一个用户样式管理器帮助您重新定义网页样式。Stylish易于为GoogleFacebookYoutubeOrkut以及其它各类型网站安装样式和主题。"
},
"disableStyleLabel": {
"message": "禁用"
},
"editStyleHeading": {
"message": "编辑样式"
},
"editStyleLabel": {
"message": "编辑"
},
"editStyleTitle": {
"message": "编辑样式 $stylename$",
"placeholders": {
"stylename": {
"content": "$1"
}
}
},
"enableStyleLabel": {
"message": "启用"
},
"findStylesForSite": {
"message": "查找更多适合此站点的样式"
},
"helpAlt": {
"message": "帮助"
},
"installUpdate": {
"message": "安装更新"
},
"manageHeading": {
"message": "已安装的样式"
},
"manageText": {
"message": "访问 <a href='http:\/\/userstyles.org'>userstyles.org<\/a> 查找预定义的样式. 如果您需要帮助,请访问 <a href='http:\/\/forum.userstyles.org'>论坛<\/a>."
},
"manageTitle": {
"message": "Stylish"
},
"noStylesForSite": {
"message": "当前站点上没有已安装的样式"
},
"openManage": {
"message": "管理已安装样式"
},
"sectionAdd": {
"message": "添加新节"
},
"sectionCode": {
"message": "代码"
},
"sectionHelp": {
"message": "样式节允许你定义多个样式段落并将它们应用于不同的站点匹配规则上。例如,一个样式可以更改使用一个方式来更改主页,而此时其它的样式段可以更改站点的其它部分。"
},
"sectionRemove": {
"message": "移除节"
},
"styleCancelEditLabel": {
"message": "返回到管理"
},
"styleChangesNotSaved": {
"message": "您已经修改了此样式,但尚未保存"
},
"styleEnabledLabel": {
"message": "已启用"
},
"styleInstall": {
"message": "安装 '$stylename$' 到 Stylish?",
"placeholders": {
"stylename": {
"content": "$1"
}
}
},
"styleMissingName": {
"message": "请输入名称."
},
"styleNameLabel": {
"message": "名称"
},
"styleSaveLabel": {
"message": "保存"
},
"styleSectionsTitle": {
"message": "样式节"
},
"styleToMozillaFormat": {
"message": "转换为Mozilla格式"
},
"styleToMozillaFormatHelp": {
"message": "用于Firefox上的Stylish的Mozilla格式样式代码可以在 userstyles.org 上提交"
},
"updateCheckFailBadResponseCode": {
"message": "更新失败 - 服务器了返回代码 $code$.",
"placeholders": {
"code": {
"content": "$1"
}
}
},
"updateCheckFailServerUnreachable": {
"message": "更新失败 - 服务器无法访问."
},
"updateCheckSucceededNoUpdate": {
"message": "已经是最新的."
},
"updateCompleted": {
"message": "更新完成."
}
}

113
apply.js Normal file
View File

@ -0,0 +1,113 @@
chrome.extension.sendRequest({name:"getStylesToApply"}, function(response) {
response.forEach(applyStyle);
});
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
switch(request.name) {
case "styleDeleted":
removeStyle(request.id);
sendResponse({});
break;
case "styleUpdated":
removeStyle(request.style.id);
//fallthrough
case "styleAdded":
if (request.style.enabled == "true") {
chrome.extension.sendRequest({name: "getStyleApplies", style: request.style, url: location.href}, function(response) {
if (response) {
applyStyle(response);
}
});
}
sendResponse({});
}
});
function removeStyle(id) {
var e = document.getElementById("stylish-" + id);
if (e) {
e.parentNode.removeChild(e);
}
}
function applyStyle(s) {
var style = document.createElement("style");
style.setAttribute("id", "stylish-" + s.id);
style.setAttribute("class", "stylish");
style.setAttribute("type", "text/css");
style.appendChild(document.createTextNode(s.sections.filter(filterSection).map(function(section) {
return section.code;
}).join("\n")));
if (document.head) {
document.head.appendChild(style);
} else {
document.documentElement.appendChild(style);
}
}
function filterSection(section) {
// global
if (!section.urls && !section.urlPrefixes && !section.domains && !section.regexps) {
return true;
}
if (section.urls) {
var found = false;
section.urls.forEach(function(url) {
if (url == location.href) {
found = true;
return;
}
});
if (found) {
return true;
}
}
if (section.urlPrefixes) {
var found = false;
section.urlPrefixes.forEach(function(urlPrefix) {
if (location.href.indexOf(urlPrefix) == 0) {
found = true;
return;
}
});
if (found) {
return true;
}
}
if (section.domains) {
var found = false;
var currentDomains = getDomains(location.href);
section.domains.forEach(function(domain) {
if (currentDomains.indexOf(domain) >= 0) {
found = true;
return;
}
});
if (found) {
return true;
}
}
if (section.regexps) {
var found = false;
section.regexps.forEach(function(regexp) {
if ((new RegExp(regexp)).test(location.href)) {
found = true;
return;
}
});
if (found) {
return true;
}
}
return false;
}
function getDomains(url) {
var d = /.*?:\/*([^\/]+)/.exec(url)[1];
var domains = [d];
while (d.indexOf(".") != -1) {
d = d.substring(d.indexOf(".") + 1);
domains.push(d);
}
return domains;
}

5
background.html Normal file
View File

@ -0,0 +1,5 @@
<html>
<script src="storage.js"></script>
<script src="messaging.js"></script>
<script src="background.js"></script>
</html>

78
background.js Normal file
View File

@ -0,0 +1,78 @@
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
switch (request.name) {
case "getStylesToApply":
getStyles({matchUrl: sender.tab.url, enabled: true}, function(r) {
chrome.browserAction.setBadgeText({text: getBadgeText(r), tabId: sender.tab.id});
sendResponse(r);
});
break;
case "getStylesForUrl":
getStyles({url: request.url}, sendResponse);
break;
case "getStyleApplies":
sendResponse(styleAppliesToUrl(request.style, request.url));
break;
case "saveFromJSON":
saveFromJSON(request.json);
sendResponse({});
break;
case "styleChanged":
cachedGlobalStyleIds = null;
cachedStyles = [];
sendResponse({});
break;
case "getCachedStyles":
sendResponse(cachedStyles);
break;
case "cacheStyles":
request.styles.forEach(function(style) {
cachedStyles[style.id] = style;
});
break;
}
});
function styleAppliesToUrl(style, url) {
style.sections = style.sections.filter(function(section) {
return sectionAppliesToUrl(section, url);
});
if (style.sections.size == 0) {
return null;
}
return style;
}
function sectionAppliesToUrl(section, url) {
if (!section.urls && !section.domains && !section.urlPrefixes && !section.regexps) {
console.log(section + " is global");
return true;
}
if (section.urls && section.urls.indexOf(url) != -1) {
console.log(section + " applies to " + url + " due to URL rules");
return true;
}
if (section.urlPrefixes && section.urlPrefixes.some(function(prefix) {
return url.indexOf(prefix) == 0;
})) {
console.log(section + " applies to " + url + " due to URL prefix rules");
return true;
}
if (section.domains && getDomains(url).some(function(domain) {
return section.domains.indexOf(domain) != -1;
})) {
console.log(section + " applies due to " + url + " due to domain rules");
return true;
}
if (section.regexps && section.regexps.some(function(regexp) {
return (new RegExp(regexp)).test(url);
})) {
console.log(section + " applies to " + url + " due to regexp rules");
return true;
}
console.log(section + " does not apply due to " + url);
return false;
}
var cachedGlobalStyleIds = null;
var cachedStyles = [];
var background = true;

108
edit.html Normal file
View File

@ -0,0 +1,108 @@
<html>
<head>
<style type="text/css">
body {
margin: 0px;
font: 12px arial,sans-serif;
}
#header {
height: 100%;
width: 250px;
position: fixed;
top: 0px;
padding: 15px;
border-right: 1px dashed #AAA;
}
.notmac #header {
-webkit-box-shadow: 0px 0px 50px -18px black;
}
#sections {
padding-left: 290px;
}
h2 {
margin-top: 8px;
}
#basic-info {
margin-bottom: 12px;
}
#name {
width: 185px;
}
#sections > div {
margin: 10px;
padding: 0px 15px;
}
#sections > div:not(:first-child) {
border-top: 2px solid black;
}
span {
cursor: pointer;
color: #999;
}
label {
display: inline-block;
width: 10em;
vertical-align: top;
}
#header label {
width: 60px;
}
.code {
height: 10em;
width: 40em;
}
.applies-to ul {
display: inline-block;
margin: 0;
}
.applies-to li {
list-style-type: none;
}
.applies-to li .add-applies-to {
display: none;
}
.applies-to li:last-child .add-applies-to {
display: inline;
}
body > section > *:not(h2) {
padding-left: 15px;
}
#sections > div {
padding-top: 1em;
padding-bottom: 1em;
}
#sections > div:only-of-type .remove-section {
display: none;
}
#sections > div .add-section {
display: none;
}
#sections > div:last-of-type .add-section {
display: inline;
}
.applies-to img {
vertical-align: bottom;
}
</style>
<script src="localization.js"></script>
<script src="storage.js"></script>
<script src="messaging.js"></script>
<script src="edit.js"></script>
</head>
<body>
<div id="header">
<h1 id="heading"></h1>
<section id="basic-info">
<div><label for="name"><script>o("styleNameLabel")</script></label><input id="name" onchange="makeDirty()"><div>
<div><label for="enabled"><script>o("styleEnabledLabel")</script></label><input type="checkbox" id="enabled" onchange="makeDirty()"></div>
</section>
<button onclick="showMozillaFormat()"><script>o("styleToMozillaFormat")</script></button><img id="to-mozilla-help" src="help.png" onclick="showToMozillaHelp()"><br><br>
<button onclick="save()"><script>o("styleSaveLabel")</script></button>
<a href="manage.html"><button><script>o("styleCancelEditLabel")</script></button></a>
</div>
<section id="sections">
<h2><script>o("styleSectionsTitle")</script> <img id="sections-help" src="help.png" onclick="showSectionHelp()"></h2>
</section>
</body>
</html>

277
edit.js Normal file
View File

@ -0,0 +1,277 @@
var styleId = null;
var dirty = false;
var appliesToTemplate = document.createElement("li");
appliesToTemplate.innerHTML = '<select name="applies-type" onchange="makeDirty()"><option value="url">' + t("appliesUrlOption") + '</option><option value="url-prefix">' + t("appliesUrlPrefixOption") + '</option><option value="domain">' + t("appliesDomainOption") + '</option><option value="regexp">' + t("appliesRegexpOption") + '</option></select><input name="applies-value" onchange="makeDirty()"><button onclick="removeAppliesTo(event)" class="remove-applies-to">' + t("appliesRemove") + '</button><button class="add-applies-to" onclick="addAppliesTo(this.parentNode.parentNode)">' + t("appliesAdd") + '</button>';
var appliesToEverythingTemplate = document.createElement("li");
appliesToEverythingTemplate.className = "applies-to-everything";
appliesToEverythingTemplate.innerHTML = t("appliesToEverything") + ' <button class="add-applies-to" onclick="addAppliesTo(this.parentNode.parentNode)">' + t("appliesSpecify") + '</button>'
var sectionTemplate = document.createElement("div");
sectionTemplate.innerHTML = '<label>' + t('sectionCode') + '</label><textarea class="code" onchange="makeDirty()"></textarea><br><div class="applies-to"><label>' + t("appliesLabel") + ' <img src="help.png" onclick="showAppliesToHelp()" alt="' + t('helpAlt') + '"></label><ul class="applies-to-list"></ul></div><button class="remove-section" onclick="removeSection(event)">' + t('sectionRemove') + '</button><button class="add-section" onclick="addSection()">' + t('sectionAdd') + '</button>';
function makeDirty() {
dirty = true;
}
window.onbeforeunload = function() {
return dirty ? t('styleChangesNotSaved') : null;
}
function addAppliesTo(list, name, value) {
var showingEverything = list.querySelector(".applies-to-everything") != null;
// blow away "Everything" if it's there
if (showingEverything) {
list.removeChild(list.firstChild);
}
var e;
if (name && value) {
e = appliesToTemplate.cloneNode(true);
e.querySelector("[name=applies-type]").value = name;
e.querySelector("[name=applies-value]").value = value;
} else if (showingEverything || list.hasChildNodes()) {
e = appliesToTemplate.cloneNode(true);
if (list.hasChildNodes()) {
e.querySelector("[name=applies-type]").value = list.querySelector("li:last-child [name='applies-type']").value;
}
} else {
e = appliesToEverythingTemplate.cloneNode(true);
}
list.appendChild(e);
}
function addSection(section) {
var div = sectionTemplate.cloneNode(true);
var appliesTo = div.querySelector(".applies-to-list");
if (section) {
div.querySelector(".code").value = section.code;
if (section.urls) {
section.urls.forEach(function(url) {
addAppliesTo(appliesTo, "url", url);
});
}
if (section.urlPrefixes) {
section.urlPrefixes.forEach(function(url) {
addAppliesTo(appliesTo, "url-prefix", url);
});
}
if (section.domains) {
section.domains.forEach(function(d) {
addAppliesTo(appliesTo, "domain", d);
});
}
if (section.regexps) {
section.regexps.forEach(function(d) {
addAppliesTo(appliesTo, "regexp", d);
});
}
if (!section.urls && !section.urlPrefixes && !section.domains && !section.regexps) {
addAppliesTo(appliesTo);
}
} else {
addAppliesTo(appliesTo);
}
var sections = document.getElementById("sections");
sections.appendChild(div);
}
function removeAppliesTo(event) {
var appliesToList = event.target.parentNode.parentNode;
appliesToList.removeChild(event.target.parentNode);
if (!appliesToList.hasChildNodes()) {
appliesToList.appendChild(appliesToEverythingTemplate);
}
makeDirty();
}
function removeSection(event) {
event.target.parentNode.parentNode.removeChild(event.target.parentNode);
makeDirty();
}
window.addEventListener("load", init, false);
function init() {
tE("sections-help", "helpAlt", "alt");
var idMatch = /[&\?]id=([0-9]+)/.exec(location.href)
if (idMatch == null || idMatch.length != 2) { // match should be 2 - one for the whole thing, one for the parentheses
// This is an add
addSection();
document.title = t("addStyleTitle");
tE("heading", "addStyleTitle");
return;
}
// This is an edit
var id = idMatch[1];
getStyles({id: id}, function(styles) {
var style = styles[0];
styleId = style.id;
initWithStyle(style);
});
}
function initWithStyle(style) {
document.getElementById("name").value = style.name;
document.getElementById("enabled").checked = style.enabled == "true";
document.getElementById("heading").innerHTML = t("editStyleHeading");
initTitle(style);
// if this was done in response to an update, we need to clear existing sections
Array.prototype.forEach.call(document.querySelectorAll("#sections > div"), function(div) {
div.parentNode.removeChild(div);
});
style.sections.forEach(addSection);
}
function initTitle(style) {
document.title = t('editStyleTitle', [style.name]);
}
function validate() {
var name = document.getElementById("name").value;
if (name == "") {
return t("styleMissingName");
}
return null;
}
function save() {
var error = validate();
if (error) {
alert(error);
return;
}
var name = document.getElementById("name").value;
var enabled = document.getElementById("enabled").checked;
getDatabase(function(db) {
db.transaction(function (t) {
var sections = getSections();
if (styleId == null) {
t.executeSql('INSERT INTO styles (name, enabled) VALUES (?, ?);', [name, enabled]);
sections.forEach(function(s) {
t.executeSql("INSERT INTO sections (style_id, code) SELECT id, ? FROM styles ORDER BY id DESC LIMIT 1;", [s.code]);
s.meta.forEach(function(m) {
t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, ?, ? FROM sections ORDER BY id DESC LIMIT 1;", [m[0], m[1]]);
});
});
} else {
t.executeSql('UPDATE styles SET name = ?, enabled = ? WHERE id = ?;', [name, enabled, styleId]);
t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT id FROM sections WHERE style_id = ?);', [styleId]);
t.executeSql('DELETE FROM sections WHERE style_id = ?;', [styleId]);
sections.forEach(function(s) {
t.executeSql("INSERT INTO sections (style_id, code) VALUES (?, ?);", [styleId, s.code]);
s.meta.forEach(function(m) {
t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, ?, ? FROM sections ORDER BY id DESC LIMIT 1;", [m[0], m[1]]);
});
});
}
dirty = false;
}, reportError, saveComplete);
}, reportError);
}
function getSections() {
var sections = [];
Array.prototype.forEach.call(document.querySelectorAll("#sections > div"), function(div) {
var code = div.querySelector(".code").value;
if (/^\s*$/.test(code)) {
return;
}
sections.push({code: code, meta: getMeta(div)});
});
return sections;
}
function getMeta(e) {
var meta = [];
Array.prototype.forEach.call(e.querySelector(".applies-to-list").childNodes, function(li) {
if (li.className == appliesToEverythingTemplate.className) {
return;
}
var a = li.querySelector("[name=applies-type]").value;
var b = li.querySelector("[name=applies-value]").value;
if (a && b) {
meta.push([a, b]);
}
});
return meta;
}
function saveComplete() {
if (styleId == null) {
// Load the style id
getDatabase(function(db) {
db.readTransaction(function (t) {
t.executeSql('SELECT id FROM styles ORDER BY id DESC LIMIT 1', [], function(t, r) {
styleId = r.rows.item(0).id;
notifySave(true);
}, reportError)
}, reportError)
});
return;
}
notifySave(false);
}
function showMozillaFormat() {
var w = window.open("data:text/plain," + encodeURIComponent(toMozillaFormat()));
}
function toMozillaFormat() {
return getSections().map(function(section) {
if (section.meta.length == 0) {
return section.code;
}
var mf = "@-moz-document ";
mf += section.meta.map(function(meta) {
return meta[0] + "(\"" + meta[1].replace("\\", "\\\\") + "\")";
}).join(", ");
return mf + " {\n" + section.code + "\n}";
}).join("\n\n");
}
function notifySave(newStyle) {
chrome.extension.sendRequest({name: "styleChanged"});
getStyles({id: styleId}, function(styles) {
if (newStyle) {
notifyAllTabs({name:"styleAdded", style: styles[0]});
// give the code above a moment before we kill the page
setTimeout(function() {location.href = "edit.html?id=" + styleId;}, 200);
} else {
initTitle(styles[0]);
notifyAllTabs({name:"styleUpdated", style: styles[0]});
}
});
}
function showSectionHelp() {
showHelp(t("sectionHelp"));
}
function showAppliesToHelp() {
showHelp(t("appliesHelp"));
}
function showToMozillaHelp() {
showHelp(t("styleToMozillaFormatHelp"));
}
function showHelp(text) {
alert(text);
}
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
var installed = document.getElementById("installed");
switch(request.name) {
case "styleUpdated":
initWithStyle(request.style);
break;
case "styleDeleted":
if (styleId == request.id) {
window.close();
break;
}
}
});

BIN
help.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

50
install.js Normal file
View File

@ -0,0 +1,50 @@
chrome.extension.sendRequest({name:"getStylesForUrl", url: getMeta("stylish-id-url") || location.href}, function(response) {
if (response.length == 0) {
sendEvent("styleCanBeInstalledChrome");
} else {
sendEvent("styleAlreadyInstalledChrome");
}
});
function sendEvent(type) {
var stylishEvent = document.createEvent("Events");
stylishEvent.initEvent(type, false, false, document.defaultView, null);
document.dispatchEvent(stylishEvent);
}
document.addEventListener("stylishInstallChrome", function() {
getResource(getMeta("stylish-description"), function(name) {
if (confirm(chrome.i18n.getMessage('styleInstall', [name]))) {
getResource(getMeta("stylish-code-chrome"), function(code) {
// check for old style json
var json = JSON.parse(code);
chrome.extension.sendRequest({name:"saveFromJSON", json: json}, function(response) {
sendEvent("styleInstalledChrome");
});
});
getResource(getMeta("stylish-install-ping-url-chrome"));
}
});
}, false);
function getMeta(name) {
var e = document.querySelector("link[rel='" + name + "']");
return e ? e.getAttribute("href") : null;
}
function getResource(url, callback) {
if (url.indexOf("#") == 0) {
if (callback) {
callback(document.getElementById(url.substring(1)).innerText);
}
return;
}
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && callback) {
callback(xhr.responseText);
}
}
xhr.send();
}

19
localization.js Normal file
View File

@ -0,0 +1,19 @@
function t(key, params) {
var s = chrome.i18n.getMessage(key, params)
if (s == "") {
throw "Missing string '" + key + "'.";
}
return s;
}
function o(key) {
document.write(t(key));
}
function tE(id, key, attr, esc) {
if (attr) {
document.getElementById(id).setAttribute(attr, t(key));
} else if (typeof esc == "undefined" || esc) {
document.getElementById(id).appendChild(document.createTextNode(t(key)));
} else {
document.getElementById(id).innerHTML = t(key);
}
}

102
manage.html Normal file
View File

@ -0,0 +1,102 @@
<html>
<head>
<title></title>
<style>
body {
margin: 0px;
font: 12px arial,sans-serif;
}
a, a:visited {
color: #555;
-webkit-transition: color 0.5s;
}
a:hover {
color: #999;
}
#header {
height: 100%;
width: 250px;
position: fixed;
top: 0px;
padding: 15px;
border-right: 1px dashed #AAA;
}
#header {
-webkit-box-shadow: 0px 0px 50px -18px black;
}
#installed {
padding-left: 280px;
}
.enabled, .disabled {
margin: 10px;
padding: 0px 15px;
}
.enabled:not(:first-child), .disabled:not(:first-child) {
border-top: 2px solid gray;
}
.applies-to, .actions {
padding-left: 15px;
}
.actions > * {
margin-right: 5px;
}
.applies-to-extra {
font-weight: bold;
}
.disabled h2::after {
content: " (Disabled)";
}
.disabled {
opacity: 0.5;
}
.disabled .disable {
display: none;
}
.enabled .enable {
display: none;
}
/* Default, no update buttons */
.update, .check-update {
display: none;
}
/* Check update button for things that can*/
*[style-update-url] .check-update {
display: inline;
}
/* Update check in progress */
.checking-update .check-update {
display: none;
}
/* Updates available */
.can-update .update {
display: inline;
}
.can-update .check-update {
display: none;
}
/* Updates not available */
.no-update .check-update {
display: none;
}
/* Updates done */
.update-done .check-update {
display: none;
}
</style>
<script src="localization.js"></script>
<script src="storage.js"></script>
<script src="messaging.js"></script>
<script src="manage.js"></script>
</head>
<body>
<div id="header">
<img src="128.png">
<h1><script>o("manageHeading")</script></h1>
<p><script>o("manageText")</script></p>
<p><button onclick="checkUpdateAll()"><script>o("checkAllUpdates")</script></button></p>
<p><a href="edit.html"><button><script>o("addStyleLabel")</script></button></a></p>
</div>
<div id="installed"></div>
</body>
</html>

281
manage.js Normal file
View File

@ -0,0 +1,281 @@
var styleTemplate = document.createElement("div");
styleTemplate.innerHTML = "<h2 class='style-name'></h2><p class='applies-to'></p><p class='actions'><a class='style-edit-link' href='edit.html?id='><button>" + t('editStyleLabel') + "</button></a><button class='enable' onclick='enable(event, true)'>" + t('enableStyleLabel') + "</button><button class='disable' onclick='enable(event, false)'>" + t('disableStyleLabel') + "</button><button class='delete' onclick='doDelete(event)'>" + t('deleteStyleLabel') + "</button><button class='check-update' onclick='doCheckUpdate(event)'>" + t('checkForUpdate') + "</button><button class='update' onclick='doUpdate(event)'>" + t('installUpdate') + "</button><span class='update-note'></span></p>";
var appliesToExtraTemplate = document.createElement("span");
appliesToExtraTemplate.className = "applies-to-extra";
appliesToExtraTemplate.innerHTML = t('appliesDisplayTruncatedSuffix');
getStyles({}, showStyles);
function showStyles(styles) {
var installed = document.getElementById("installed");
styles.map(createStyleElement).forEach(function(e) {
installed.appendChild(e);
});
}
function createStyleElement(style) {
var e = styleTemplate.cloneNode(true);
e.setAttribute("class", style.enabled == "true" ? "enabled" : "disabled");
e.setAttribute("style-id", style.id);
if (style.updateUrl) {
e.setAttribute("style-update-url", style.updateUrl);
}
var styleName = e.querySelector(".style-name");
styleName.appendChild(document.createTextNode(style.name));
if (style.url) {
var homepage = document.createElement("a");
homepage.setAttribute("href", style.url);
homepage.setAttribute("target", "_blank");
var homepageImg = document.createElement("img");
homepageImg.src = "world_go.png";
homepageImg.alt = "*";
homepage.appendChild(homepageImg);
styleName.appendChild(document.createTextNode(" " ));
styleName.appendChild(homepage);
}
var domains = [];
var urls = [];
var urlPrefixes = [];
var regexps = [];
function add(array, property) {
style.sections.forEach(function(section) {
if (section[property]) {
section[property].filter(function(value) {
return array.indexOf(value) == -1;
}).forEach(function(value) {
array.push(value);
});;
}
});
}
add(domains, 'domains');
add(urls, 'urls');
add(urlPrefixes, 'urlPrefixes');
add(regexps, 'regexps');
var appliesToToShow = [];
if (domains)
appliesToToShow = appliesToToShow.concat(domains);
if (urls)
appliesToToShow = appliesToToShow.concat(urls);
if (urlPrefixes)
appliesToToShow = appliesToToShow.concat(urlPrefixes.map(function(u) { return u + "*"; }));
if (regexps)
appliesToToShow = appliesToToShow.concat(regexps.map(function(u) { return "/" + u + "/"; }));
var appliesToString = "";
var showAppliesToExtra = false;
if (appliesToToShow.length == "")
appliesToString = t('appliesToEverything');
else if (appliesToToShow.length <= 10)
appliesToString = appliesToToShow.join(", ");
else {
appliesToString = appliesToToShow.slice(0, 10).join(", ");
showAppliesToExtra = true;
}
e.querySelector(".applies-to").appendChild(document.createTextNode(t('appliesDisplay', [appliesToString])));
if (showAppliesToExtra) {
e.querySelector(".applies-to").appendChild(appliesToExtraTemplate.cloneNode(true));
}
var editLink = e.querySelector(".style-edit-link");
editLink.setAttribute("href", editLink.getAttribute("href") + style.id);
return e;
}
function enable(event, enabled) {
var id = getId(event);
enableStyle(id, enabled);
}
function doDelete() {
if (!confirm(t('deleteStyleConfirm'))) {
return;
}
var id = getId(event);
deleteStyle(id);
}
function getId(event) {
return getStyleElement(event).getAttribute("style-id");
}
function getStyleElement(event) {
var e = event.target;
while (e) {
if (e.hasAttribute("style-id")) {
return e;
}
e = e.parentNode;
}
return null;
}
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
switch(request.name) {
case "styleUpdated":
handleUpdate(request.style);
sendResponse({});
break;
case "styleAdded":
installed.appendChild(createStyleElement(request.style));
sendResponse({});
break;
case "styleDeleted":
handleDelete(request.id);
sendResponse({});
break;
}
});
function handleUpdate(style) {
var installed = document.getElementById("installed");
installed.replaceChild(createStyleElement(style), installed.querySelector("[style-id='" + style.id + "']"));
}
function handleDelete(id) {
var installed = document.getElementById("installed");
installed.removeChild(installed.querySelector("[style-id='" + id + "']"));
}
function doCheckUpdate(event) {
checkUpdate(getStyleElement(event));
}
function checkUpdateAll() {
Array.prototype.forEach.call(document.querySelectorAll("[style-update-url]"), checkUpdate);
}
function checkUpdate(element) {
element.querySelector(".update-note").innerHTML = t('checkingForUpdate');
element.className = element.className.replace("checking-update", "").replace("no-update", "").replace("can-update", "") + " checking-update";
var id = element.getAttribute("style-id");
var url = element.getAttribute("style-update-url");
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function (aEvt) {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
checkNeedsUpdate(id, JSON.parse(xhr.responseText));
} else if (xhr.status == 0) {
handleNeedsUpdate(t('updateCheckFailServerUnreachable'), id, null);
} else {
handleNeedsUpdate(t('updateCheckFailBadResponseCode', [xhr.status]), id, null);
}
}
};
xhr.send(null);
}
function checkNeedsUpdate(id, serverJson) {
getStyles({id: id}, function(styles) {
var style = styles[0];
if (codeIsEqual(style.sections, serverJson.sections)) {
handleNeedsUpdate("no", id, serverJson);
} else {
handleNeedsUpdate("yes", id, serverJson);
}
});
}
function handleNeedsUpdate(needsUpdate, id, serverJson) {
var e = document.querySelector("[style-id='" + id + "']");
e.className = e.className.replace("checking-update", "");
switch (needsUpdate) {
case "yes":
e.className += " can-update";
e.updatedCode = serverJson;
e.querySelector(".update-note").innerHTML = '';
break;
case "no":
e.className += " no-update";
e.querySelector(".update-note").innerHTML = t('updateCheckSucceededNoUpdate');
break;
default:
e.className += " no-update";
e.querySelector(".update-note").innerHTML = needsUpdate;
}
}
function doUpdate(event) {
var element = getStyleElement(event);
var o = {};
o.id = element.getAttribute('style-id');
o.sections = element.updatedCode.sections;
saveFromJSON(o, function() {
element.updatedCode = "";
element.className = element.className.replace("can-update", "update-done");
element.querySelector(".update-note").innerHTML = t('updateCompleted');
});
}
function codeIsEqual(a, b) {
if (a.length != b.length) {
return false;
}
var properties = ["code", "urlPrefixes", "urls", "domains", "regexps"];
for (var i = 0; i < a.length; i++) {
var found = false;
for (var j = 0; j < b.length; j++) {
var allEquals = properties.every(function(property) {
return jsonEquals(a[i], b[j], property);
});
if (allEquals) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}
function jsonEquals(a, b, property) {
var type = getType(a[property]);
var typeB = getType(b[property]);
if (type != typeB) {
// consider empty arrays equivalent to lack of property
if ((type == "undefined" || (type == "array" && a[property].length == 0)) && (typeB == "undefined" || (typeB == "array" && b[property].length == 0))) {
return true;
}
return false;
}
if (type == "undefined") {
return true;
}
if (type == "array") {
if (a[property].length != b[property].length) {
return false;
}
for (var i = 0; i < a.length; i++) {
var found = false;
for (var j = 0; j < b.length; j++) {
if (a[i] == b[j]) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}
if (type == "string") {
return a[property] == b[property];
}
}
function getType(o) {
if (typeof o == "undefined" || typeof o == "string") {
return typeof o;
}
if (o instanceof Array) {
return "array";
}
throw "Not supported - " + o;
}
document.title = t("manageTitle");

37
manifest.json Normal file
View File

@ -0,0 +1,37 @@
{
"name": "Stylish",
"version": "0.9",
"description": "__MSG_description__",
"icons": {
"16": "16.png",
"48": "48.png",
"128": "128.png"
},
"permissions": [
"tabs",
"http://userstyles.org/",
"http://userstyles.local/"
],
"background_page": "background.html",
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"run_at": "document_start",
"all_frames": true,
"js": ["apply.js"]
},
{
"matches": ["http://userstyles.org/*", "http://userstyles.local/*"],
"run_at": "document_end",
"all_frames": false,
"js": ["install.js"]
}
],
"options_page": "manage.html",
"browser_action": {
"default_icon": "19.png",
"default_title": "Stylish",
"default_popup": "popup.html"
},
"default_locale": "en"
}

21
messaging.js Normal file
View File

@ -0,0 +1,21 @@
function notifyAllTabs(request) {
chrome.windows.getAll({populate: true}, function(windows) {
windows.forEach(function(win) {
win.tabs.forEach(function(tab) {
chrome.tabs.sendRequest(tab.id, request);
updateBadgeText(tab);
});
});
});
}
function updateBadgeText(tab) {
getStyles({matchUrl: tab.url}, function(styles) {
chrome.browserAction.setBadgeText({text: getBadgeText(styles), tabId: tab.id});
});
}
function getBadgeText(styles) {
var e = styles.filter(function(style) { return style.enabled == "true"; });
return e.length == 0 ? "" : ("" + e.length);
}

117
popup.html Normal file
View File

@ -0,0 +1,117 @@
<style>
body {
width: 200px;
}
#no-styles {
font-style: italic;
}
.disabled {
color: #BBB;
}
.disabled .disable {
display: none;
}
.enabled .enable {
display: none;
}
.style-name {
font-weight: bold;
}
.actions {
font-size: x-small;
}
a, a:visited {
color: black;
}
.entry {
padding-bottom: 0.5em;
margin-bottom: 0.5em;
border-bottom: 1px solid black;
}
#find-styles, #manage-styles {
font-size: smaller;
}
#find-styles {
margin-bottom: 0.5em;
}
</style>
<script src="localization.js"></script>
<script src="storage.js"></script>
<script src="messaging.js"></script>
<script>
var styleTemplate = document.createElement("div");
styleTemplate.innerHTML = "<div class='style-name'></div><div class='actions'><a class='style-edit-link' href='edit.html?id=' onclick='return openLink(event);'>" + t('editStyleLabel') + "</a> <a href='#' class='enable' onclick='enable(event, true);return false;'>" + t('enableStyleLabel') + "</a> <a href='#' class='disable' onclick='enable(event, false);return false;'>" + t('disableStyleLabel') + "</a> <a href='#' class='delete' onclick='doDelete(event);return false;'>" + t('deleteStyleLabel') + "</a></div>";
chrome.tabs.getSelected(null, function(tab) {
//alert('here');
getStyles({matchUrl: tab.url}, showStyles);
document.querySelector("#find-styles a").href = "http://userstyles.org/styles/browse/all/" + encodeURIComponent(tab.url);
});
function showStyles(styles) {
var installed = document.getElementById("installed");
if (styles.length == 0) {
installed.innerHTML = "<div class='entry' id='no-styles'>" + t('noStylesForSite') + "</div>";
}
styles.map(createStyleElement).forEach(function(e) {
installed.appendChild(e);
});
}
function createStyleElement(style) {
var e = styleTemplate.cloneNode(true);
e.setAttribute("class", "entry " + (style.enabled == "true" ? "enabled" : "disabled"));
e.setAttribute("style-id", style.id);
var styleName = e.querySelector(".style-name");
styleName.appendChild(document.createTextNode(style.name));
var editLink = e.querySelector(".style-edit-link");
editLink.setAttribute("href", editLink.getAttribute("href") + style.id);
return e;
}
function enable(event, enabled) {
var id = getId(event);
enableStyle(id, enabled);
}
function doDelete() {
if (!confirm(t('deleteStyleConfirm'))) {
return;
}
var id = getId(event);
deleteStyle(id);
}
function getId(event) {
var e = event.target;
while (e) {
if (e.hasAttribute("style-id")) {
return e.getAttribute("style-id");
}
e = e.parentNode;
}
return null;
}
function openLink(event) {
chrome.tabs.create({url: event.target.href});
return false;
}
function handleUpdate(style) {
var installed = document.getElementById("installed");
installed.replaceChild(createStyleElement(style), installed.querySelector("[style-id='" + style.id + "']"));
}
function handleDelete(id) {
var installed = document.getElementById("installed");
installed.removeChild(installed.querySelector("[style-id='" + id + "']"));
}
</script>
<div id="installed"></div>
<div id="find-styles"><a href="#" onclick="return openLink(event);"><script>o('findStylesForSite')</script></a></div>
<div id="manage-styles"><a href="manage.html" target="_new"><script>o('openManage')</script></a></div>

366
storage.js Normal file
View File

@ -0,0 +1,366 @@
var namespacePattern = /^\s*@namespace\s+([a-zA-Z]+\s+)?url\(\"?http:\/\/www.w3.org\/1999\/xhtml\"?\);?\s*$/;
var stylishDb = null;
function getDatabase(ready, error) {
if (stylishDb != null) {
ready(stylishDb);
return;
}
stylishDb = openDatabase('stylish', '', 'Stylish Styles', 5*1024*1024);
if (stylishDb.version == "1.0" || stylishDb.version == "") {
dbV11(stylishDb, error, ready);
} else if (stylishDb.version == "1.1") {
dbV12(stylishDb, error, ready);
} else if (stylishDb.version == "1.2") {
dbV13(stylishDb, error, ready);
} else {
ready(stylishDb);
}
}
function dbV11(d, error, done) {
d.changeVersion(d.version, '1.1', function (t) {
t.executeSql('CREATE TABLE styles (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, url TEXT, updateUrl TEXT, md5Url TEXT, name TEXT NOT NULL, code TEXT NOT NULL, enabled INTEGER NOT NULL, originalCode TEXT NULL);');
t.executeSql('CREATE TABLE style_meta (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, style_id INTEGER NOT NULL, name TEXT NOT NULL, value TEXT NOT NULL);');
t.executeSql('CREATE INDEX style_meta_style_id ON style_meta (style_id);');
}, error, function() {dbV12(d, error, done)});
}
function dbV12(d, error, done) {
d.changeVersion(d.version, '1.2', function (t) {
// add section table
t.executeSql('CREATE TABLE sections (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, style_id INTEGER NOT NULL, code TEXT NOT NULL);');
t.executeSql('INSERT INTO sections (style_id, code) SELECT id, code FROM styles;');
// switch meta to sections
t.executeSql('DROP INDEX style_meta_style_id;');
t.executeSql('CREATE TABLE section_meta (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, section_id INTEGER NOT NULL, name TEXT NOT NULL, value TEXT NOT NULL);');
t.executeSql('INSERT INTO section_meta (section_id, name, value) SELECT s.id, sm.name, sm.value FROM sections s INNER JOIN style_meta sm ON sm.style_id = s.style_id;');
t.executeSql('CREATE INDEX section_meta_section_id ON section_meta (section_id);');
t.executeSql('DROP TABLE style_meta;');
// drop extra fields from styles table
t.executeSql('CREATE TABLE newstyles (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, url TEXT, updateUrl TEXT, md5Url TEXT, name TEXT NOT NULL, enabled INTEGER NOT NULL);');
t.executeSql('INSERT INTO newstyles (id, url, updateUrl, md5Url, name, enabled) SELECT id, url, updateUrl, md5Url, name, enabled FROM styles;');
t.executeSql('DROP TABLE styles;');
t.executeSql('ALTER TABLE newstyles RENAME TO styles;');
}, error, function() {dbV13(d, error, done)});
}
function dbV13(d, error, done) {
d.changeVersion(d.version, '1.3', function (t) {
// clear out orphans
t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT sections.id FROM sections LEFT JOIN styles ON styles.id = sections.style_id WHERE styles.id IS NULL);');
t.executeSql('DELETE FROM sections WHERE id IN (SELECT sections.id FROM sections LEFT JOIN styles ON styles.id = sections.style_id WHERE styles.id IS NULL);');
}, error, function() { done(d)});
}
function getStyles(options, callback) {
getDatabase(function(db) {
db.readTransaction(function (t) {
if ("matchUrl" in options) {
// get a list of style ids that apply to the url. we need to do this separately because we need all the metas for the styles, not just the matching ones
// find site-specific ones
var sql = "SELECT DISTINCT s.style_id FROM sections s INNER JOIN section_meta sm ON sm.section_id = s.id WHERE (sm.name = 'url' and sm.value = ?) OR (sm.name = 'url-prefix' AND ? LIKE (sm.value || '%')) OR (sm.name = 'regexp' AND ? REGEXP sm.value)";
var matchParams = [];
var domains = getDomains(options.matchUrl);
matchParams = matchParams.concat([options.matchUrl, options.matchUrl, options.matchUrl]).concat(domains);
var domainClause = "";
if (domains.length == 1) {
sql += " OR (sm.name = 'domain' AND sm.value = ?)";
} else if (domains.length > 1) {
sql += " OR (sm.name = 'domain' AND sm.value IN (";
sql += domains.map(function(d) { return "?";}).join(",");
sql += '))';
}
t.executeSql(sql, matchParams, function (t, r) {
var style_ids = [];
if (options.id) {
style_ids.push(options.id);
}
for (var i = 0; i < r.rows.length; i++) {
var values = r.rows.item(i);
style_ids.push(values.style_id);
}
// now add in global ones
getGlobalStyleIds(function(ids) {
style_ids = style_ids.concat(ids);
loadStyles(style_ids, options.enabled, options.url, callback);
});
});
} else {
loadStyles(options.id ? [options.id] : null, options.enabled, options.url, callback);
}
}, reportError);
}, reportError);
}
function getCache(callback) {
if (isBackground()) {
callback(cachedStyles);
return;
}
chrome.extension.sendRequest({name: "getCachedStyles"}, callback);
}
function fixBoolean(b) {
if (typeof b != "undefined") {
return b != "false";
}
return null;
}
function loadStyles(styleIds, enabled, url, callback) {
// clean up the parameters
enabled = fixBoolean(enabled);
if (typeof url == "undefined") {
url = null;
}
// grab what we can from the cache
if (styleIds) {
getCache(function(cache) {
var styles = [];
var styleIdsNeeded = [];
styleIds.forEach(function(id) {
if (cache[id]) {
if (checkStyle(cache[id], enabled, url)) {
styles.push(cache[id]);
}
} else {
styleIdsNeeded.push(id);
}
});
styleIds = styleIdsNeeded;
// do we have everything we need?
if (styleIds.length == 0) {
callback(styles);
return;
}
loadStylesFromDB(styles, styleIds, enabled, url, callback);
});
return;
}
loadStylesFromDB([], styleIds, enabled, url, callback);
}
function checkStyle(style, enabled, url) {
return (enabled == null || enabled == fixBoolean(style.enabled)) && (url == null || url == style.url);
}
function loadStylesFromDB(styles, styleIds, enabled, url, callback) {
// load from the db for the rest
getDatabase(function(db) {
db.readTransaction(function (t) {
var where = "";
var params = [];
if (styleIds) {
if (styleIds.size == 0) {
callback([]);
return;
}
where += " AND s.id IN ("
var firstStyleId = true;
styleIds.forEach(function(styleId) {
where += firstStyleId ? "?" : ",?";
firstStyleId = false;
params.push(styleId);
});
where += ")";
}
/*if (enabled != null) {
where += ' AND enabled = ?';
params.push(enabled);
}
if (url != null) {
where += ' AND s.url = ?';
params.push(url);
}*/
t.executeSql('SELECT DISTINCT s.*, se.id section_id, se.code, sm.name metaName, sm.value metaValue FROM styles s LEFT JOIN sections se ON se.style_id = s.id LEFT JOIN section_meta sm ON sm.section_id = se.id WHERE 1' + where + ' ORDER BY s.id, se.id, sm.id', params, function (t, r) {
var currentStyle = null;
var currentSection = null;
for (var i = 0; i < r.rows.length; i++) {
var values = r.rows.item(i);
var metaName = null;
switch (values.metaName) {
case null:
break;
case "url":
metaName = "urls";
break;
case "url-prefix":
metaName = "urlPrefixes";
break;
case "domain":
var metaName = "domains";
break;
case "regexps":
var metaName = "regexps";
break;
default:
var metaName = values.metaName + "s";
}
var metaValue = values.metaValue;
if (currentStyle == null || currentStyle.id != values.id) {
currentStyle = {id: values.id, url: values.url, updateUrl: values.updateUrl, md5Url: values.md5Url, name: values.name, enabled: values.enabled, sections: []};
styles.push(currentStyle);
}
if (currentSection == null || currentSection.id != values.section_id) {
currentSection = {id: values.section_id, code: values.code};
currentStyle.sections.push(currentSection);
}
if (metaName && metaValue) {
if (currentSection[metaName]) {
currentSection[metaName].push(metaValue);
} else {
currentSection[metaName] = [metaValue];
}
}
}
if (isBackground()) {
styles.forEach(function(style) {
cachedStyles[style.id] = style;
});
} else {
chrome.extension.sendRequest({name: "cacheStyles", styles: styles});
}
callback(styles.filter(function(style) {
return checkStyle(style, enabled, url);
}));
}, reportError);
}, reportError);
}, reportError);
}
function getGlobalStyleIds(callback) {
if (isBackground() && cachedGlobalStyleIds != null) {
callback(cachedGlobalStyleIds);
return;
}
getDatabase(function(db) {
db.readTransaction(function (t) {
t.executeSql("SELECT DISTINCT s.style_id, s.code FROM sections s LEFT JOIN section_meta sm ON sm.section_id = s.id INNER JOIN styles st ON st.id = s.style_id GROUP BY s.id HAVING COUNT(sm.id) = 0", [], function (t, r) {
var style_ids = [];
for (var i = 0; i < r.rows.length; i++) {
var values = r.rows.item(i);
// ignore namespace only sections
if (!namespacePattern.test(values.code) && style_ids.indexOf(values.style_id) == -1) {
style_ids.push(values.style_id);
}
}
if (isBackground()) {
cachedGlobalStyleIds = style_ids;
}
callback(style_ids);
}, reportError);
}, reportError);
}, reportError);
}
function saveFromJSON(o, callback) {
getDatabase(function(db) {
db.transaction(function (t) {
if (o.id) {
t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT id FROM sections WHERE style_id = ?);', [o.id]);
t.executeSql('DELETE FROM sections WHERE style_id = ?;', [o.id]);
} else {
t.executeSql('INSERT INTO styles (name, enabled, url, updateUrl) VALUES (?, ?, ?, ?);', [o.name, true, o.url, o.updateUrl]);
}
o.sections.forEach(function(section) {
if (o.id) {
t.executeSql('INSERT INTO sections (style_id, code) VALUES (?, ?);', [o.id, section.code]);
} else {
t.executeSql('INSERT INTO sections (style_id, code) SELECT id, ? FROM styles ORDER BY id DESC LIMIT 1;', [section.code]);
}
section.urls.forEach(function(u) {
t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'url', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]);
});
section.urlPrefixes.forEach(function(u) {
t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'url-prefix', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]);
});
section.domains.forEach(function(u) {
t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'domain', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]);
});
section.regexps.forEach(function(u) {
t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'regexp', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]);
});
});
}, reportError, function() {saveFromJSONComplete(o.id, callback)});
}, reportError);
}
function saveFromJSONComplete(id, callback) {
chrome.extension.sendRequest({name: "styleChanged"});
if (id) {
notifyAllTabs({name:"styleUpdated", style: id});
if (callback) {
callback(id);
}
return;
}
// Load the style id
getDatabase(function(db) {
db.readTransaction(function (t) {
t.executeSql('SELECT id FROM styles ORDER BY id DESC LIMIT 1', [], function(t, r) {
var styleId = r.rows.item(0).id;
getStyles({id: styleId}, function(styles) {
notifyAllTabs({name:"styleAdded", style: styles[0]});
});
if (callback) {
callback(styleId);
}
}, reportError)
}, reportError)
});
}
function reportError() {
for (i in arguments) {
if ("message" in arguments[i]) {
//alert(arguments[i].message);
console.log(arguments[i].message);
}
}
}
function isBackground() {
return typeof background != "undefined" && background;
}
function getDomains(url) {
var d = /.*?:\/*([^\/]+)/.exec(url)[1];
var domains = [d];
while (d.indexOf(".") != -1) {
d = d.substring(d.indexOf(".") + 1);
domains.push(d);
}
return domains;
}
function enableStyle(id, enabled) {
getDatabase(function(db) {
db.transaction(function (t) {
t.executeSql("UPDATE styles SET enabled = ? WHERE id = ?;", [enabled, id]);
}, reportError, function() {
chrome.extension.sendRequest({name: "styleChanged"});
getStyles({id: id}, function(styles) {
handleUpdate(styles[0]);
notifyAllTabs({name:"styleUpdated", style: styles[0]});
});
});
});
}
function deleteStyle(id) {
getDatabase(function(db) {
db.transaction(function (t) {
t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT id FROM sections WHERE style_id = ?);', [id]);
t.executeSql('DELETE FROM sections WHERE style_id = ?;', [id]);
t.executeSql("DELETE FROM styles WHERE id = ?;", [id]);
}, reportError, function() {
chrome.extension.sendRequest({name: "styleChanged"});
handleDelete(id);
notifyAllTabs({name:"styleDeleted", id: id});
});
});
}

BIN
world_go.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 944 B