@@ -6,6 +6,7 @@ class BaseCtrl {
|
|||||||
syncManager.sync();
|
syncManager.sync();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
angular.module('app.frontend').controller('BaseCtrl', BaseCtrl);
|
angular.module('app.frontend').controller('BaseCtrl', BaseCtrl);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
angular.module('app.frontend')
|
angular.module('app.frontend')
|
||||||
.controller('HomeCtrl', function ($scope, $location, $rootScope, $timeout, modelManager, syncManager, authManager) {
|
.controller('HomeCtrl', function ($scope, $location, $rootScope, $timeout, modelManager, syncManager, authManager, themeManager) {
|
||||||
|
|
||||||
function urlParam(key) {
|
function urlParam(key) {
|
||||||
return $location.search()[key];
|
return $location.search()[key];
|
||||||
@@ -34,6 +34,7 @@ angular.module('app.frontend')
|
|||||||
|
|
||||||
syncManager.loadLocalItems(function(items) {
|
syncManager.loadLocalItems(function(items) {
|
||||||
$scope.allTag.didLoad = true;
|
$scope.allTag.didLoad = true;
|
||||||
|
themeManager.activateInitialTheme();
|
||||||
$scope.$apply();
|
$scope.$apply();
|
||||||
|
|
||||||
syncManager.sync(null);
|
syncManager.sync(null);
|
||||||
|
|||||||
30
app/assets/javascripts/app/frontend/models/app/theme.js
Normal file
30
app/assets/javascripts/app/frontend/models/app/theme.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
class Theme extends Item {
|
||||||
|
|
||||||
|
constructor(json_obj) {
|
||||||
|
super(json_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
mapContentToLocalProperties(contentObject) {
|
||||||
|
super.mapContentToLocalProperties(contentObject)
|
||||||
|
this.url = contentObject.url;
|
||||||
|
this.name = contentObject.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
structureParams() {
|
||||||
|
var params = {
|
||||||
|
url: this.url,
|
||||||
|
name: this.name,
|
||||||
|
};
|
||||||
|
|
||||||
|
_.merge(params, super.structureParams());
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {uuid: this.uuid}
|
||||||
|
}
|
||||||
|
|
||||||
|
get content_type() {
|
||||||
|
return "SN|Theme";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,10 +7,12 @@ class GlobalExtensionsMenu {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
controller($scope, extensionManager, syncManager) {
|
controller($scope, extensionManager, syncManager, modelManager, themeManager) {
|
||||||
'ngInject';
|
'ngInject';
|
||||||
|
|
||||||
$scope.extensionManager = extensionManager;
|
$scope.extensionManager = extensionManager;
|
||||||
|
$scope.themeManager = themeManager;
|
||||||
|
$scope.state = {showDataExts: true, showThemes: true};
|
||||||
|
|
||||||
$scope.toggleExtensionForm = function() {
|
$scope.toggleExtensionForm = function() {
|
||||||
$scope.newExtensionData = {};
|
$scope.newExtensionData = {};
|
||||||
@@ -64,6 +66,19 @@ class GlobalExtensionsMenu {
|
|||||||
extensionManager.refreshExtensionsFromServer();
|
extensionManager.refreshExtensionsFromServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.submitTheme = function() {
|
||||||
|
themeManager.submitTheme($scope.state.themeUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.deleteTheme = function(theme) {
|
||||||
|
if(confirm("Are you sure you want to delete this theme?")) {
|
||||||
|
themeManager.deactivateTheme(theme);
|
||||||
|
modelManager.setItemToBeDeleted(theme);
|
||||||
|
syncManager.sync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class ModelManager {
|
|||||||
this.itemChangeObservers = [];
|
this.itemChangeObservers = [];
|
||||||
this.items = [];
|
this.items = [];
|
||||||
this._extensions = [];
|
this._extensions = [];
|
||||||
this.acceptableContentTypes = ["Note", "Tag", "Extension", "SN|Editor"];
|
this.acceptableContentTypes = ["Note", "Tag", "Extension", "SN|Editor", "SN|Theme"];
|
||||||
}
|
}
|
||||||
|
|
||||||
get allItems() {
|
get allItems() {
|
||||||
@@ -130,7 +130,9 @@ class ModelManager {
|
|||||||
item = new Extension(json_obj);
|
item = new Extension(json_obj);
|
||||||
} else if(json_obj.content_type == "SN|Editor") {
|
} else if(json_obj.content_type == "SN|Editor") {
|
||||||
item = new Editor(json_obj);
|
item = new Editor(json_obj);
|
||||||
} else {
|
} else if(json_obj.content_type == "SN|Theme") {
|
||||||
|
item = new Theme(json_obj);
|
||||||
|
} else {
|
||||||
item = new Item(json_obj);
|
item = new Item(json_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,7 +257,6 @@ class ModelManager {
|
|||||||
_.pull(this.tags, item);
|
_.pull(this.tags, item);
|
||||||
} else if(item.content_type == "Note") {
|
} else if(item.content_type == "Note") {
|
||||||
_.pull(this.notes, item);
|
_.pull(this.notes, item);
|
||||||
|
|
||||||
} else if(item.content_type == "Extension") {
|
} else if(item.content_type == "Extension") {
|
||||||
_.pull(this._extensions, item);
|
_.pull(this._extensions, item);
|
||||||
}
|
}
|
||||||
|
|||||||
82
app/assets/javascripts/app/services/themeManager.js
Normal file
82
app/assets/javascripts/app/services/themeManager.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
class ThemeManager {
|
||||||
|
|
||||||
|
constructor(modelManager, syncManager) {
|
||||||
|
this.syncManager = syncManager;
|
||||||
|
this.modelManager = modelManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
get themes() {
|
||||||
|
return this.modelManager.itemsForContentType("SN|Theme");
|
||||||
|
}
|
||||||
|
|
||||||
|
get activeTheme() {
|
||||||
|
var activeThemeId = localStorage.getItem("activeTheme");
|
||||||
|
if(!activeThemeId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var theme = _.find(this.themes, {uuid: activeThemeId});
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
activateInitialTheme() {
|
||||||
|
var theme = this.activeTheme;
|
||||||
|
if(theme) {
|
||||||
|
this.activateTheme(theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submitTheme(url) {
|
||||||
|
var name = this.displayNameForThemeFile(this.fileNameFromPath(url));
|
||||||
|
var theme = this.modelManager.createItem({content_type: "SN|Theme", url: url, name: name});
|
||||||
|
this.modelManager.addItem(theme);
|
||||||
|
theme.setDirty(true);
|
||||||
|
this.syncManager.sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
activateTheme(theme) {
|
||||||
|
var activeTheme = this.activeTheme;
|
||||||
|
if(activeTheme) {
|
||||||
|
this.deactivateTheme(activeTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
var link = document.createElement("link");
|
||||||
|
link.href = theme.url;
|
||||||
|
link.type = "text/css";
|
||||||
|
link.rel = "stylesheet";
|
||||||
|
link.media = "screen,print";
|
||||||
|
link.id = theme.uuid;
|
||||||
|
document.getElementsByTagName("head")[0].appendChild(link);
|
||||||
|
localStorage.setItem("activeTheme", theme.uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
deactivateTheme(theme) {
|
||||||
|
localStorage.removeItem("activeTheme");
|
||||||
|
var element = document.getElementById(theme.uuid);
|
||||||
|
if(element) {
|
||||||
|
element.disabled = true;
|
||||||
|
element.parentNode.removeChild(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isThemeActive(theme) {
|
||||||
|
return localStorage.getItem("activeTheme") === theme.uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileNameFromPath(filePath) {
|
||||||
|
return filePath.replace(/^.*[\\\/]/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
capitalizeString(string) {
|
||||||
|
return string.replace(/(?:^|\s)\S/g, function(a) { return a.toUpperCase(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
displayNameForThemeFile(fileName) {
|
||||||
|
let name = fileName.split(".")[0];
|
||||||
|
let cleaned = name.split("-").join(" ");
|
||||||
|
return this.capitalizeString(cleaned);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('app.frontend').service('themeManager', ThemeManager);
|
||||||
@@ -56,10 +56,13 @@ h2 {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 14px ;
|
font-size: 14px;
|
||||||
margin-top: 4px ;
|
margin-top: 4px;
|
||||||
margin-bottom: 3px;
|
margin-bottom: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,6 +167,40 @@ a.disabled {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Global Ext Menu */
|
||||||
|
|
||||||
|
.ext-section {
|
||||||
|
|
||||||
|
min-height: 50px;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
padding-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.opened {
|
||||||
|
h1 {
|
||||||
|
padding-top: 0px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
height: 10px;
|
height: 10px;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
|
|||||||
@@ -66,6 +66,10 @@
|
|||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ml-2 {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.pb-0 {
|
.pb-0 {
|
||||||
padding-bottom: 0px !important;
|
padding-bottom: 0px !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,79 @@
|
|||||||
.panel.panel-default.account-panel.panel-right
|
.panel.panel-default.account-panel.panel-right
|
||||||
.panel-body
|
.panel-body
|
||||||
%div{"style" => "font-size: 15px;", "ng-if" => "!extensionManager.extensions.length"} No extensions installed
|
.white-bg.medium-padding.ext-section{"ng-class" => "{'opened': state.showDataExts}"}
|
||||||
%div{"ng-if" => "extensionManager.extensions.length"}
|
%h1
|
||||||
%section.gray-bg.inline-h.mb-10.medium-padding{"ng-repeat" => "extension in extensionManager.extensions | orderBy: 'name'", "ng-init" => "extension.formData = {}"}
|
%a{"ng-click" => "state.showDataExts = !state.showDataExts"} Data Extensions
|
||||||
%h3.center-align {{extension.name}}
|
%div{"ng-if" => "state.showDataExts"}
|
||||||
.center-align.centered.mt-10
|
%div{"style" => "font-size: 15px;", "ng-if" => "!extensionManager.extensions.length"} No extensions installed
|
||||||
%label.block.normal Send data:
|
%div{"ng-if" => "extensionManager.extensions.length"}
|
||||||
%label.normal
|
%section.gray-bg.inline-h.mb-10.medium-padding{"ng-repeat" => "extension in extensionManager.extensions | orderBy: 'name'", "ng-init" => "extension.formData = {}"}
|
||||||
%input{"type" => "radio", "ng-model" => "extension.encrypted", "ng-value" => "true", "ng-change" => "changeExtensionEncryptionFormat(true, extension)"}
|
%h3.center-align {{extension.name}}
|
||||||
Encrypted
|
.center-align.centered.mt-10
|
||||||
%label.normal
|
%label.block.normal Send data:
|
||||||
%input{"type" => "radio", "ng-model" => "extension.encrypted", "ng-value" => "false", "ng-change" => "changeExtensionEncryptionFormat(false, extension)"}
|
%label.normal
|
||||||
Decrypted
|
%input{"type" => "radio", "ng-model" => "extension.encrypted", "ng-value" => "true", "ng-change" => "changeExtensionEncryptionFormat(true, extension)"}
|
||||||
|
Encrypted
|
||||||
|
%label.normal
|
||||||
|
%input{"type" => "radio", "ng-model" => "extension.encrypted", "ng-value" => "false", "ng-change" => "changeExtensionEncryptionFormat(false, extension)"}
|
||||||
|
Decrypted
|
||||||
|
|
||||||
.small-v-space
|
.small-v-space
|
||||||
|
|
||||||
%section.inline-h.white-bg.medium-padding.mb-10.pb-4{"ng-repeat" => "action in extension.actionsInGlobalContext()"}
|
%section.inline-h.white-bg.medium-padding.mb-10.pb-4{"ng-repeat" => "action in extension.actionsInGlobalContext()"}
|
||||||
%label.block {{action.label}}
|
%label.block {{action.label}}
|
||||||
%em{"style" => "font-style: italic;"} {{action.desc}}
|
%em{"style" => "font-style: italic;"} {{action.desc}}
|
||||||
%em{"ng-if" => "action.repeat_mode == 'watch'"}
|
%em{"ng-if" => "action.repeat_mode == 'watch'"}
|
||||||
Repeats when a change is made to your items.
|
Repeats when a change is made to your items.
|
||||||
%em{"ng-if" => "action.repeat_mode == 'loop'"}
|
%em{"ng-if" => "action.repeat_mode == 'loop'"}
|
||||||
Repeats at most once every {{action.repeat_timeout}} seconds
|
Repeats at most once every {{action.repeat_timeout}} seconds
|
||||||
%div
|
%div
|
||||||
%a{"ng-click" => "action.showPermissions = !action.showPermissions"} {{action.showPermissions ? "Hide permissions" : "Show permissions"}}
|
%a{"ng-click" => "action.showPermissions = !action.showPermissions"} {{action.showPermissions ? "Hide permissions" : "Show permissions"}}
|
||||||
%div{"ng-if" => "action.showPermissions"}
|
%div{"ng-if" => "action.showPermissions"}
|
||||||
{{action.permissionsString()}}
|
{{action.permissionsString()}}
|
||||||
%label.block.normal {{action.encryptionModeString()}}
|
%label.block.normal {{action.encryptionModeString()}}
|
||||||
|
|
||||||
%div
|
%div
|
||||||
.mt-5{"ng-if" => "action.repeat_mode"}
|
.mt-5{"ng-if" => "action.repeat_mode"}
|
||||||
%button.light{"ng-if" => "extensionManager.isRepeatActionEnabled(action)", "ng-click" => "extensionManager.disableRepeatAction(action, extension)"} Disable
|
%button.light{"ng-if" => "extensionManager.isRepeatActionEnabled(action)", "ng-click" => "extensionManager.disableRepeatAction(action, extension)"} Disable
|
||||||
%button.light{"ng-if" => "!extensionManager.isRepeatActionEnabled(action)", "ng-click" => "extensionManager.enableRepeatAction(action, extension)"} Enable
|
%button.light{"ng-if" => "!extensionManager.isRepeatActionEnabled(action)", "ng-click" => "extensionManager.enableRepeatAction(action, extension)"} Enable
|
||||||
%button.light.mt-10{"ng-if" => "!action.running && !action.repeat_mode", "ng-click" => "selectedAction(action, extension)"}
|
%button.light.mt-10{"ng-if" => "!action.running && !action.repeat_mode", "ng-click" => "selectedAction(action, extension)"}
|
||||||
Perform Action
|
Perform Action
|
||||||
.spinner.mb-5.centered.center-align.block{"ng-if" => "action.running"}
|
.spinner.mb-5.centered.center-align.block{"ng-if" => "action.running"}
|
||||||
%p.mb-5.mt-5.small{"ng-if" => "!action.error && action.lastExecuted && !action.running"}
|
%p.mb-5.mt-5.small{"ng-if" => "!action.error && action.lastExecuted && !action.running"}
|
||||||
Last run {{action.lastExecuted | appDateTime}}
|
Last run {{action.lastExecuted | appDateTime}}
|
||||||
%label.red{"ng-if" => "action.error"}
|
%label.red{"ng-if" => "action.error"}
|
||||||
Error performing action.
|
Error performing action.
|
||||||
|
|
||||||
%a.block.center-align.mt-10{"ng-click" => "extension.showURL = !extension.showURL"} Show URL
|
%a.block.center-align.mt-10{"ng-click" => "extension.showURL = !extension.showURL"} Show URL
|
||||||
%p.center-align.wrap{"ng-if" => "extension.showURL"} {{extension.url}}
|
%p.center-align.wrap{"ng-if" => "extension.showURL"} {{extension.url}}
|
||||||
%a.block.center-align.mt-5{"ng-click" => "deleteExtension(extension)"} Remove extension
|
%a.block.center-align.mt-5{"ng-click" => "deleteExtension(extension)"} Remove extension
|
||||||
|
|
||||||
.large-v-space
|
.large-v-space
|
||||||
|
|
||||||
%a.block{"ng-click" => "toggleExtensionForm()"} Add new extension
|
%a.block{"ng-click" => "toggleExtensionForm()"} Add new extension
|
||||||
|
|
||||||
%form.mt-10.mb-10{"ng-if" => "showNewExtensionForm"}
|
%form.mt-10.mb-10{"ng-if" => "showNewExtensionForm"}
|
||||||
%input.form-control{:autofocus => 'autofocus', :name => 'url', :placeholder => 'Extension URL', :required => true, :type => 'url', 'ng-model' => 'newExtensionData.url'}
|
%input.form-control{:autofocus => 'autofocus', :name => 'url', :placeholder => 'Extension URL', :required => true, :type => 'url', 'ng-model' => 'newExtensionData.url'}
|
||||||
%button.btn.dark-button.btn-block{"ng-click" => "submitNewExtensionForm()", :type => 'submit', "data-style" => "expand-right", "data-size" => "s", "state" => "buttonState"}
|
%button.btn.dark-button.btn-block{"ng-click" => "submitNewExtensionForm()", :type => 'submit', "data-style" => "expand-right", "data-size" => "s", "state" => "buttonState"}
|
||||||
Add Extension
|
Add Extension
|
||||||
|
|
||||||
%a.block.mt-5{"ng-click" => "reloadExtensionsPressed()", "ng-if" => "extensionManager.extensions.length > 0"} Reload all extensions
|
%a.block.mt-5{"ng-click" => "reloadExtensionsPressed()", "ng-if" => "extensionManager.extensions.length > 0"} Reload all extensions
|
||||||
%a.block.mt-5{"href" => "https://standardnotes.org/extensions", "target" => "_blank"} List of available extensions
|
%a.block.mt-5{"href" => "https://standardnotes.org/extensions", "target" => "_blank"} Available Extensions
|
||||||
|
|
||||||
|
|
||||||
|
.white-bg.medium-padding.mt-10.ext-section{"ng-class" => "{'opened': state.showThemes}"}
|
||||||
|
%h1
|
||||||
|
%a{"ng-click" => "state.showThemes = !state.showThemes"} Themes
|
||||||
|
%div{"ng-if" => "state.showThemes"}
|
||||||
|
%section{"ng-repeat" => "theme in themeManager.themes"}
|
||||||
|
%label {{theme.name}}
|
||||||
|
%p {{theme.url}}
|
||||||
|
%a{"ng-if" => "!themeManager.isThemeActive(theme)", "ng-click" => "themeManager.activateTheme(theme)"} Activate
|
||||||
|
%a{"ng-if" => "themeManager.isThemeActive(theme)", "ng-click" => "themeManager.deactivateTheme(theme)"} Deactivate
|
||||||
|
%a.red.ml-2{"ng-click" => "deleteTheme(theme)"} Delete
|
||||||
|
|
||||||
|
%section
|
||||||
|
%p.bold.mb-5 Install New Theme
|
||||||
|
%input.form-control{:autofocus => 'autofocus', :name => 'url', :placeholder => 'New Theme URL', :required => true,
|
||||||
|
:type => 'url', 'ng-model' => 'state.themeUrl', "ng-keyup" => "$event.keyCode == 13 && submitTheme();"}
|
||||||
|
%a.block.mt-5{"href" => "https://standardnotes.org/extensions/themes", "target" => "_blank"} Available Themes
|
||||||
|
|||||||
Reference in New Issue
Block a user