This commit is contained in:
Mo Bitar
2018-01-10 12:25:34 -06:00
parent 4bda20a8d9
commit ae29e502cb
24 changed files with 380 additions and 390 deletions

View File

@@ -58,22 +58,20 @@ angular.module('app.frontend')
} }
let associatedEditor = this.editorForNote(note); let associatedEditor = this.editorForNote(note);
if(associatedEditor) { if(associatedEditor && associatedEditor != this.selectedEditor) {
// setting note to not ready will remove the editor from view in a flash, // setting note to not ready will remove the editor from view in a flash,
// so we only want to do this if switching between external editors // so we only want to do this if switching between external editors
this.noteReady = false; this.noteReady = false;
} else { // switch after timeout, so that note data isnt posted to current editor
onReady();
}
// Activate new editor if it's different from the one currently activated
if(associatedEditor) {
// switch after timeout, so that note data isnt posted to current editor
$timeout(() => { $timeout(() => {
this.selectedEditor = associatedEditor; this.selectedEditor = associatedEditor;
onReady(); onReady();
}) })
} else if(associatedEditor) {
// Same editor as currently active
onReady();
} else { } else {
// No editor
this.selectedEditor = null; this.selectedEditor = null;
onReady(); onReady();
} }
@@ -106,7 +104,6 @@ angular.module('app.frontend')
} }
this.selectEditor = function(editor) { this.selectEditor = function(editor) {
console.log("selectEditor", editor);
this.showEditorMenu = false; this.showEditorMenu = false;
if(editor) { if(editor) {
@@ -419,18 +416,6 @@ angular.module('app.frontend')
this.selectedEditor = null; this.selectedEditor = null;
} }
} }
if(component.active) {
$timeout(function(){
var iframe = componentManager.iframeForComponent(component);
if(iframe) {
iframe.onload = function() {
componentManager.registerComponentWindow(component, iframe.contentWindow);
}.bind(this);
}
}.bind(this));
}
}.bind(this), contextRequestHandler: function(component){ }.bind(this), contextRequestHandler: function(component){
return this.note; return this.note;
}.bind(this), actionHandler: function(component, action, data){ }.bind(this), actionHandler: function(component, action, data){
@@ -441,21 +426,10 @@ angular.module('app.frontend')
element.setAttribute("style", `width:${widthString}; height:${heightString}; `); element.setAttribute("style", `width:${widthString}; height:${heightString}; `);
} }
if(data.type === "content") { if(data.type == "container") {
var iframe = componentManager.iframeForComponent(component);
var width = data.width;
var height = data.height;
iframe.width = width;
iframe.height = height;
setSize(iframe, data);
} else {
if(component.area == "note-tags") { if(component.area == "note-tags") {
var container = document.getElementById("note-tags-component-container"); var container = document.getElementById("note-tags-component-container");
setSize(container, data); setSize(container, data);
} else {
var container = document.getElementById("component-" + component.uuid);
setSize(container, data);
} }
} }
} }

View File

@@ -121,46 +121,49 @@ angular.module('app.frontend')
componentManager.registerHandler({identifier: "roomBar", areas: ["rooms"], activationHandler: (component) => { componentManager.registerHandler({identifier: "roomBar", areas: ["rooms"], activationHandler: (component) => {
if(component.active) { if(component.active) {
// Show room, if it was not activated manually (in the event of event from componentManager)
if(!component.showRoom) {
this.selectRoom(component);
}
$timeout(() => { $timeout(() => {
var iframe = componentManager.iframeForComponent(component); var lastSize = component.getRoomLastSize();
if(iframe) { if(lastSize) {
var lastSize = component.getRoomLastSize(); componentManager.handleSetSizeEvent(component, lastSize);
if(lastSize) {
componentManager.handleSetSizeEvent(component, lastSize);
}
iframe.onload = function() {
componentManager.registerComponentWindow(component, iframe.contentWindow);
}.bind(this);
} }
}); });
} }
}, actionHandler: (component, action, data) => { }, actionHandler: (component, action, data) => {
if(action == "set-size") { if(action == "set-size") {
componentManager.handleSetSizeEvent(component, data);
component.setRoomLastSize(data); component.setRoomLastSize(data);
} }
}}); }});
this.selectRoom = function(room) { this.selectRoom = function(room) {
room.show = !room.show;
// Allows us to send messages to component modal directive // Allows us to send messages to component modal directive
if(!room.directiveController) { if(!room.directiveController) {
room.directiveController = {}; room.directiveController = {onDismiss: () => {
room.showRoom = false;
}};
} }
if(!room.show) { // Make sure to call dismiss() before setting new showRoom value
room.directiveController.dismiss(); // This way the directive stays alive long enough to deactivate the associated component
} // (The directive's life is at the mercy of "ng-if" => "room.showRoom")
if(room.showRoom) {
room.directiveController.dismiss(() => {
console.log("Show", room.show); });
} else {
room.showRoom = true;
}
} }
// Handle singleton ProLink instance // Handle singleton ProLink instance
singletonManager.registerSingleton({content_type: "SN|Component", package_info: {identifier: "org.standardnotes.prolink"}}, (resolvedSingleton) => { singletonManager.registerSingleton({content_type: "SN|Component", package_info: {identifier: "org.standardnotes.prolink"}}, (resolvedSingleton) => {
console.log("Roombar received resolved ProLink", resolvedSingleton);
}, (valueCallback) => { }, (valueCallback) => {
console.log("Creating prolink");
// Safe to create. Create and return object. // Safe to create. Create and return object.
let url = window._prolink_package_url; let url = window._prolink_package_url;
packageManager.installPackage(url, (component) => { packageManager.installPackage(url, (component) => {

View File

@@ -63,19 +63,9 @@ angular.module('app.frontend')
componentManager.registerHandler({identifier: "tags", areas: ["tags-list"], activationHandler: function(component){ componentManager.registerHandler({identifier: "tags", areas: ["tags-list"], activationHandler: function(component){
this.component = component; this.component = component;
if(component.active) {
$timeout(function(){
var iframe = document.getElementById("tags-list-iframe");
iframe.onload = function() {
componentManager.registerComponentWindow(this.component, iframe.contentWindow);
}.bind(this);
}.bind(this));
}
}.bind(this), contextRequestHandler: function(component){ }.bind(this), contextRequestHandler: function(component){
return null; return null;
}.bind(this), actionHandler: function(component, action, data){ }.bind(this), actionHandler: function(component, action, data){
if(action === "select-item") { if(action === "select-item") {
var tag = modelManager.findItem(data.item.uuid); var tag = modelManager.findItem(data.item.uuid);
if(tag) { if(tag) {
@@ -86,7 +76,6 @@ angular.module('app.frontend')
else if(action === "clear-selection") { else if(action === "clear-selection") {
this.selectTag(this.allTag); this.selectTag(this.allTag);
} }
}.bind(this)}); }.bind(this)});
this.setAllTag = function(allTag) { this.setAllTag = function(allTag) {

View File

@@ -73,6 +73,14 @@ class Component extends Item {
return "SN|Component"; return "SN|Component";
} }
computedUrl() {
if(this.offlineOnly || (isDesktopApplication() && this.local_url)) {
return this.local_url.replace("sn://", this.desktopManager.getApplicationDataPath() + "/");
} else {
return this.url || this.hosted_url;
}
}
isEditor() { isEditor() {
return this.area == "editor-editor"; return this.area == "editor-editor";
} }

View File

@@ -1,27 +1,9 @@
class Theme extends Item { class Theme extends Component {
constructor(json_obj) { constructor(json_obj) {
super(json_obj); super(json_obj);
}
mapContentToLocalProperties(content) { this.area = "themes";
super.mapContentToLocalProperties(content)
this.url = content.url;
this.name = content.name;
}
structureParams() {
var params = {
url: this.url,
name: this.name
};
_.merge(params, super.structureParams());
return params;
}
toJSON() {
return {uuid: this.uuid}
} }
get content_type() { get content_type() {

View File

@@ -296,7 +296,6 @@ angular.module('app.frontend')
let prefsContentType = "SN|UserPreferences"; let prefsContentType = "SN|UserPreferences";
singletonManager.registerSingleton({content_type: prefsContentType}, (resolvedSingleton) => { singletonManager.registerSingleton({content_type: prefsContentType}, (resolvedSingleton) => {
console.log("AuthManager received resolved UserPreferences", resolvedSingleton);
this.userPreferences = resolvedSingleton; this.userPreferences = resolvedSingleton;
this.userPreferencesDidChange(); this.userPreferencesDidChange();
}, (valueCallback) => { }, (valueCallback) => {
@@ -304,7 +303,6 @@ angular.module('app.frontend')
var prefs = new Item({content_type: prefsContentType}); var prefs = new Item({content_type: prefsContentType});
modelManager.addItem(prefs); modelManager.addItem(prefs);
prefs.setDirty(true); prefs.setDirty(true);
console.log("Created new prefs", prefs);
$rootScope.sync(); $rootScope.sync();
valueCallback(prefs); valueCallback(prefs);
}); });

View File

@@ -3,12 +3,11 @@ let ClientDataDomain = "org.standardnotes.sn.components";
class ComponentManager { class ComponentManager {
constructor($rootScope, modelManager, syncManager, desktopManager, themeManager, $timeout, $compile) { constructor($rootScope, modelManager, syncManager, desktopManager, $timeout, $compile) {
this.$compile = $compile; this.$compile = $compile;
this.$rootScope = $rootScope; this.$rootScope = $rootScope;
this.modelManager = modelManager; this.modelManager = modelManager;
this.syncManager = syncManager; this.syncManager = syncManager;
this.themeManager = themeManager;
this.desktopManager = desktopManager; this.desktopManager = desktopManager;
this.timeout = $timeout; this.timeout = $timeout;
this.streamObservers = []; this.streamObservers = [];
@@ -21,9 +20,9 @@ class ComponentManager {
this.handlers = []; this.handlers = [];
$rootScope.$on("theme-changed", function(){ // $rootScope.$on("theme-changed", function(){
this.postThemeToComponents(); // this.postThemeToAllComponents();
}.bind(this)) // }.bind(this))
window.addEventListener("message", function(event){ window.addEventListener("message", function(event){
if(this.loggingEnabled) { if(this.loggingEnabled) {
@@ -32,7 +31,7 @@ class ComponentManager {
this.handleMessage(this.componentForSessionKey(event.data.sessionKey), event.data); this.handleMessage(this.componentForSessionKey(event.data.sessionKey), event.data);
}.bind(this), false); }.bind(this), false);
this.modelManager.addItemSyncObserver("component-manager", "*", function(allItems, validItems, deletedItems, source) { this.modelManager.addItemSyncObserver("component-manager", "*", (allItems, validItems, deletedItems, source) => {
/* If the source of these new or updated items is from a Component itself saving items, we don't need to notify /* If the source of these new or updated items is from a Component itself saving items, we don't need to notify
components again of the same item. Regarding notifying other components than the issuing component, other mapping sources components again of the same item. Regarding notifying other components than the issuing component, other mapping sources
@@ -42,7 +41,9 @@ class ComponentManager {
return; return;
} }
var syncedComponents = allItems.filter(function(item){return item.content_type === "SN|Component" }); var syncedComponents = allItems.filter(function(item) {
return item.content_type === "SN|Component" || item.content_type == "SN|Theme"
});
/* We only want to sync if the item source is Retrieved, not MappingSourceRemoteSaved to avoid /* We only want to sync if the item source is Retrieved, not MappingSourceRemoteSaved to avoid
recursion caused by the component being modified and saved after it is updated. recursion caused by the component being modified and saved after it is updated.
@@ -77,9 +78,10 @@ class ComponentManager {
} }
]; ];
this.runWithPermissions(observer.component, requiredPermissions, observer.originalMessage.permissions, function(){ this.runWithPermissions(observer.component, requiredPermissions, () => {
console.log("Stream observer, sending items", relevantItems);
this.sendItemsInReply(observer.component, relevantItems, observer.originalMessage); this.sendItemsInReply(observer.component, relevantItems, observer.originalMessage);
}.bind(this)) })
} }
var requiredContextPermissions = [ var requiredContextPermissions = [
@@ -89,36 +91,43 @@ class ComponentManager {
]; ];
for(let observer of this.contextStreamObservers) { for(let observer of this.contextStreamObservers) {
this.runWithPermissions(observer.component, requiredContextPermissions, observer.originalMessage.permissions, function(){ for(let handler of this.handlers) {
for(let handler of this.handlers) { if(!handler.areas.includes(observer.component.area) && !handler.areas.includes("*")) {
if(!handler.areas.includes(observer.component.area)) { continue;
continue; }
} if(handler.contextRequestHandler) {
var itemInContext = handler.contextRequestHandler(observer.component); var itemInContext = handler.contextRequestHandler(observer.component);
if(itemInContext) { if(itemInContext) {
var matchingItem = _.find(allItems, {uuid: itemInContext.uuid}); var matchingItem = _.find(allItems, {uuid: itemInContext.uuid});
if(matchingItem) { if(matchingItem) {
this.sendContextItemInReply(observer.component, matchingItem, observer.originalMessage, source); this.runWithPermissions(observer.component, requiredContextPermissions, () => {
this.sendContextItemInReply(observer.component, matchingItem, observer.originalMessage, source);
})
} }
} }
} }
}.bind(this)) }
} }
}.bind(this)) });
} }
postThemeToComponents() { postThemeToAllComponents() {
for(var component of this.components) { for(var component of this.components) {
if(!component.active || !component.window) { if(component.area == "themes" || !component.active || !component.window) {
continue; continue;
} }
this.postThemeToComponent(component); this.postThemeToComponent(component);
} }
} }
getActiveTheme() {
return this.componentsForArea("themes").find((theme) => {return theme.active});
}
postThemeToComponent(component) { postThemeToComponent(component) {
var activeTheme = this.getActiveTheme();
var data = { var data = {
themes: [this.themeManager.currentTheme ? this.themeManager.currentTheme.url : null] themes: [activeTheme ? activeTheme.computedUrl() : null]
} }
this.sendMessageToComponent(component, {action: "themes", data: data}) this.sendMessageToComponent(component, {action: "themes", data: data})
@@ -126,7 +135,7 @@ class ComponentManager {
contextItemDidChangeInArea(area) { contextItemDidChangeInArea(area) {
for(let handler of this.handlers) { for(let handler of this.handlers) {
if(handler.areas.includes(area) === false) { if(handler.areas.includes(area) === false && !handler.areas.includes("*")) {
continue; continue;
} }
var observers = this.contextStreamObservers.filter(function(observer){ var observers = this.contextStreamObservers.filter(function(observer){
@@ -134,8 +143,10 @@ class ComponentManager {
}) })
for(let observer of observers) { for(let observer of observers) {
var itemInContext = handler.contextRequestHandler(observer.component); if(handler.contextRequestHandler) {
this.sendContextItemInReply(observer.component, itemInContext, observer.originalMessage); var itemInContext = handler.contextRequestHandler(observer.component);
this.sendContextItemInReply(observer.component, itemInContext, observer.originalMessage);
}
} }
} }
} }
@@ -176,7 +187,7 @@ class ComponentManager {
} }
get components() { get components() {
return this.modelManager.itemsForContentType("SN|Component"); return this.modelManager.allItemsMatchingTypes(["SN|Component", "SN|Theme"]);
} }
componentsForArea(area) { componentsForArea(area) {
@@ -220,7 +231,7 @@ class ComponentManager {
save-context-client-data save-context-client-data
get-context-client-data get-context-client-data
install-local-component install-local-component
open-component toggle-activate-component
*/ */
if(message.action === "stream-items") { if(message.action === "stream-items") {
@@ -237,14 +248,14 @@ class ComponentManager {
this.handleSaveItemsMessage(component, message); this.handleSaveItemsMessage(component, message);
} else if(message.action === "install-local-component") { } else if(message.action === "install-local-component") {
this.handleInstallLocalComponentMessage(component, message); this.handleInstallLocalComponentMessage(component, message);
} else if(message.action === "open-component") { } else if(message.action === "toggle-activate-component") {
let openComponent = this.modelManager.findItem(message.data.uuid); let componentToToggle = this.modelManager.findItem(message.data.uuid);
this.openModalComponent(openComponent); this.handleToggleComponentMessage(component, componentToToggle, message);
} }
// Notify observers // Notify observers
for(let handler of this.handlers) { for(let handler of this.handlers) {
if(handler.areas.includes(component.area)) { if(handler.areas.includes(component.area) || handler.areas.includes("*")) {
this.timeout(function(){ this.timeout(function(){
handler.actionHandler(component, message.action, message.data); handler.actionHandler(component, message.action, message.data);
}) })
@@ -254,7 +265,7 @@ class ComponentManager {
removePrivatePropertiesFromResponseItems(responseItems, includeUrls) { removePrivatePropertiesFromResponseItems(responseItems, includeUrls) {
// Don't allow component to overwrite these properties. // Don't allow component to overwrite these properties.
var privateProperties = ["appData", "autoupdate", "permissions", "active"]; var privateProperties = ["appData", "autoupdate", "permissions", "active", "encrypted"];
if(includeUrls) { if(includeUrls) {
privateProperties = privateProperties.concat(["url", "hosted_url", "local_url"]); privateProperties = privateProperties.concat(["url", "hosted_url", "local_url"]);
} }
@@ -278,7 +289,7 @@ class ComponentManager {
} }
]; ];
this.runWithPermissions(component, requiredPermissions, message.permissions, function(){ this.runWithPermissions(component, requiredPermissions, () => {
if(!_.find(this.streamObservers, {identifier: component.uuid})) { if(!_.find(this.streamObservers, {identifier: component.uuid})) {
// for pushing laster as changes come in // for pushing laster as changes come in
this.streamObservers.push({ this.streamObservers.push({
@@ -289,14 +300,13 @@ class ComponentManager {
}) })
} }
// push immediately now // push immediately now
var items = []; var items = [];
for(var contentType of message.data.content_types) { for(var contentType of message.data.content_types) {
items = items.concat(this.modelManager.itemsForContentType(contentType)); items = items.concat(this.modelManager.itemsForContentType(contentType));
} }
this.sendItemsInReply(component, items, message); this.sendItemsInReply(component, items, message);
}.bind(this)); });
} }
handleStreamContextItemMessage(component, message) { handleStreamContextItemMessage(component, message) {
@@ -307,7 +317,7 @@ class ComponentManager {
} }
]; ];
this.runWithPermissions(component, requiredPermissions, message.permissions, function(){ this.runWithPermissions(component, requiredPermissions, function(){
if(!_.find(this.contextStreamObservers, {identifier: component.uuid})) { if(!_.find(this.contextStreamObservers, {identifier: component.uuid})) {
// for pushing laster as changes come in // for pushing laster as changes come in
this.contextStreamObservers.push({ this.contextStreamObservers.push({
@@ -318,27 +328,49 @@ class ComponentManager {
} }
// push immediately now // push immediately now
for(let handler of this.handlers) { for(let handler of this.handlersForArea(component.area)) {
if(handler.areas.includes(component.area) === false) {
continue;
}
var itemInContext = handler.contextRequestHandler(component); var itemInContext = handler.contextRequestHandler(component);
this.sendContextItemInReply(component, itemInContext, message); this.sendContextItemInReply(component, itemInContext, message);
} }
}.bind(this)) }.bind(this))
} }
handleSaveItemsMessage(component, message) { isItemWithinComponentContextJurisdiction(item, component) {
var requiredContentTypes = _.uniq(message.data.items.map((i) => {return i.content_type})).sort(); for(let handler of this.handlersForArea(component.area)) {
var requiredPermissions = [ var itemInContext = handler.contextRequestHandler(component);
{ if(itemInContext.uuid == item.uuid) {
name: "stream-items", return true;
content_types: requiredContentTypes
} }
]; }
return false;
}
this.runWithPermissions(component, requiredPermissions, message.permissions, () => { handlersForArea(area) {
var responseItems = message.data.items; return this.handlers.filter((candidate) => {return candidate.areas.includes(area)});
}
handleSaveItemsMessage(component, message) {
var responseItems = message.data.items;
var requiredPermissions;
// Check if you're just trying to save the context item, which requires only stream-context-item permissions
if(responseItems.length == 1 && this.isItemWithinComponentContextJurisdiction(responseItems[0], component)) {
requiredPermissions = [
{
name: "stream-context-item"
}
];
} else {
var requiredContentTypes = _.uniq(responseItems.map((i) => {return i.content_type})).sort();
requiredPermissions = [
{
name: "stream-items",
content_types: requiredContentTypes
}
];
}
this.runWithPermissions(component, requiredPermissions, () => {
this.removePrivatePropertiesFromResponseItems(responseItems, {includeUrls: true}); this.removePrivatePropertiesFromResponseItems(responseItems, {includeUrls: true});
@@ -373,7 +405,7 @@ class ComponentManager {
} }
]; ];
this.runWithPermissions(component, requiredPermissions, message.permissions, () => { this.runWithPermissions(component, requiredPermissions, () => {
var responseItem = message.data.item; var responseItem = message.data.item;
this.removePrivatePropertiesFromResponseItems([responseItem]); this.removePrivatePropertiesFromResponseItems([responseItem]);
var item = this.modelManager.createItem(responseItem); var item = this.modelManager.createItem(responseItem);
@@ -397,7 +429,7 @@ class ComponentManager {
} }
]; ];
this.runWithPermissions(component, requiredPermissions, message.permissions, () => { this.runWithPermissions(component, requiredPermissions, () => {
var items = message.data.items; var items = message.data.items;
var noun = items.length == 1 ? "item" : "items"; var noun = items.length == 1 ? "item" : "items";
if(confirm(`Are you sure you want to delete ${items.length} ${noun}?`)) { if(confirm(`Are you sure you want to delete ${items.length} ${noun}?`)) {
@@ -419,10 +451,8 @@ class ComponentManager {
} }
]; ];
this.runWithPermissions(component, requiredPermissions, message.permissions, () => { this.runWithPermissions(component, requiredPermissions, () => {
console.log("Received install-local-component event");
this.desktopManager.installOfflineComponentFromData(message.data, (response) => { this.desktopManager.installOfflineComponentFromData(message.data, (response) => {
console.log("componentManager: installed component:", response);
var component = this.modelManager.mapResponseItemsToLocalModels([response], ModelManager.MappingSourceComponentRetrieved)[0]; var component = this.modelManager.mapResponseItemsToLocalModels([response], ModelManager.MappingSourceComponentRetrieved)[0];
// Save updated URL // Save updated URL
component.setDirty(true); component.setDirty(true);
@@ -432,68 +462,73 @@ class ComponentManager {
} }
handleSetComponentDataMessage(component, message) { handleSetComponentDataMessage(component, message) {
var requiredPermissions = [ // A component setting its own data does not require special permissions
{ this.runWithPermissions(component, [], () => {
name: "stream-items",
content_types: [component.content_type]
}
];
this.runWithPermissions(component, requiredPermissions, message.permissions, () => {
component.componentData = message.data.componentData; component.componentData = message.data.componentData;
component.setDirty(true); component.setDirty(true);
this.syncManager.sync(); this.syncManager.sync();
}); });
} }
runWithPermissions(component, requiredPermissions, requestedPermissions, runFunction) {
var acquiredPermissions = component.permissions; handleToggleComponentMessage(sourceComponent, targetComponent, message) {
if(targetComponent.area == "modal") {
var requestedMatchesRequired = true; this.openModalComponent(targetComponent);
} else {
for(var required of requiredPermissions) { if(targetComponent.active) {
var matching = _.find(requestedPermissions, required); this.deactivateComponent(targetComponent);
var matching = requestedPermissions.filter((p) => { } else {
var matchesContentTypes = true; if(targetComponent.content_type == "SN|Theme") {
if(p.content_types) { // Deactive currently active theme
matchesContentTypes = JSON.stringify(p.content_types.sort()) == JSON.stringify(required.content_types.sort()); var activeTheme = this.getActiveTheme();
} if(activeTheme) {
return p.name == required.name && matchesContentTypes; this.deactivateComponent(activeTheme);
})[0]; }
if(!matching) {
/* Required permissions can be 1 content type, and requestedPermisisons may send an array of content types.
In the case of an array, we can just check to make sure that requiredPermissions content type is found in the array
*/
matching = requestedPermissions.filter((requested) => {
return Array.isArray(requested.content_types) && requested.content_types.containsSubset(required.content_types);
});
console.log("Matching 2nd chance", matching);
if(!matching) {
requestedMatchesRequired = false;
break;
} }
this.activateComponent(targetComponent);
} }
} }
}
if(!requestedMatchesRequired) { runWithPermissions(component, requiredPermissions, runFunction) {
// Error with Component permissions request
console.error("You are requesting permissions", requestedPermissions, "when you need to be requesting", requiredPermissions, ". Component:", component);
return false;
}
if(!component.permissions) { if(!component.permissions) {
component.permissions = []; component.permissions = [];
} }
var acquiredMatchesRequested = angular.toJson(component.permissions.sort()) === angular.toJson(requestedPermissions.sort()); var acquiredPermissions = component.permissions;
var acquiredMatchesRequired = true;
if(!acquiredMatchesRequested) { for(var required of requiredPermissions) {
this.promptForPermissions(component, requestedPermissions, function(approved){ var matching = acquiredPermissions.find((candidate) => {
var matchesContentTypes = true;
if(candidate.content_types && required.content_types) {
matchesContentTypes = JSON.stringify(candidate.content_types.sort()) == JSON.stringify(required.content_types.sort());
}
return candidate.name == required.name && matchesContentTypes;
});
if(!matching) {
/* Required permissions can be 1 content type, and requestedPermisisons may send an array of content types.
In the case of an array, we can just check to make sure that requiredPermissions content type is found in the array
*/
matching = acquiredPermissions.find((candidate) => {
return Array.isArray(candidate.content_types)
&& Array.isArray(required.content_types)
&& candidate.content_types.containsSubset(required.content_types);
});
if(!matching) {
acquiredMatchesRequired = false;
break;
}
}
}
// var acquiredMatchesRequested = angular.toJson(component.permissions.sort()) === angular.toJson(requestedPermissions.sort());
if(!acquiredMatchesRequired) {
this.promptForPermissions(component, requiredPermissions, function(approved){
if(approved) { if(approved) {
runFunction(); runFunction();
} }
@@ -503,18 +538,22 @@ class ComponentManager {
} }
} }
promptForPermissions(component, requestedPermissions, callback) { promptForPermissions(component, permissions, callback) {
// since these calls are asyncronous, multiple dialogs may be requested at the same time. We only want to present one and trigger all callbacks based on one modal result // since these calls are asyncronous, multiple dialogs may be requested at the same time. We only want to present one and trigger all callbacks based on one modal result
var existingDialog = _.find(this.permissionDialogs, {component: component}); var existingDialog = _.find(this.permissionDialogs, {component: component});
var scope = this.$rootScope.$new(true); var scope = this.$rootScope.$new(true);
scope.component = component; scope.component = component;
scope.permissions = requestedPermissions; scope.permissions = permissions;
scope.actionBlock = callback; scope.actionBlock = callback;
scope.callback = function(approved) { scope.callback = function(approved) {
if(approved) { if(approved) {
component.permissions = requestedPermissions; for(var permission of permissions) {
if(!component.permissions.includes(permission)) {
component.permissions.push(permission);
}
}
component.setDirty(true); component.setDirty(true);
this.syncManager.sync(); this.syncManager.sync();
} }
@@ -544,6 +583,9 @@ class ComponentManager {
openModalComponent(component) { openModalComponent(component) {
var scope = this.$rootScope.$new(true); var scope = this.$rootScope.$new(true);
scope.component = component; scope.component = component;
scope.onDismiss = () => {
}
var el = this.$compile( "<component-modal component='component' class='modal'></component-modal>" )(scope); var el = this.$compile( "<component-modal component='component' class='modal'></component-modal>" )(scope);
angular.element(document.body).append(el); angular.element(document.body).append(el);
} }
@@ -591,7 +633,7 @@ class ComponentManager {
component.active = true; component.active = true;
for(var handler of this.handlers) { for(var handler of this.handlers) {
if(handler.areas.includes(component.area)) { if(handler.areas.includes(component.area) || handler.areas.includes("*")) {
handler.activationHandler(component); handler.activationHandler(component);
} }
} }
@@ -604,6 +646,10 @@ class ComponentManager {
if(!this.activeComponents.includes(component)) { if(!this.activeComponents.includes(component)) {
this.activeComponents.push(component); this.activeComponents.push(component);
} }
if(component.area == "themes") {
this.postThemeToAllComponents();
}
} }
registerHandler(handler) { registerHandler(handler) {
@@ -640,12 +686,13 @@ class ComponentManager {
} }
deactivateComponent(component) { deactivateComponent(component) {
console.log("Deactivating component", component);
var didChange = component.active != false; var didChange = component.active != false;
component.active = false; component.active = false;
component.sessionKey = null; component.sessionKey = null;
for(var handler of this.handlers) { for(var handler of this.handlers) {
if(handler.areas.includes(component.area)) { if(handler.areas.includes(component.area) || handler.areas.includes("*")) {
handler.activationHandler(component); handler.activationHandler(component);
} }
} }
@@ -664,6 +711,10 @@ class ComponentManager {
this.contextStreamObservers = this.contextStreamObservers.filter(function(o){ this.contextStreamObservers = this.contextStreamObservers.filter(function(o){
return o.component !== component; return o.component !== component;
}) })
if(component.area == "themes") {
this.postThemeToAllComponents();
}
} }
deleteComponent(component) { deleteComponent(component) {
@@ -713,14 +764,6 @@ class ComponentManager {
component.ignoreEvents = !on; component.ignoreEvents = !on;
} }
urlForComponent(component) {
if(isDesktopApplication() && component.local_url) {
return component.local_url.replace("sn://", this.desktopManager.getApplicationDataPath() + "/");
} else {
return component.url || component.hosted_url;
}
}
iframeForComponent(component) { iframeForComponent(component) {
for(var frame of document.getElementsByTagName("iframe")) { for(var frame of document.getElementsByTagName("iframe")) {
var componentId = frame.dataset.componentId; var componentId = frame.dataset.componentId;

View File

@@ -6,8 +6,8 @@ class ComponentModal {
this.scope = { this.scope = {
show: "=", show: "=",
component: "=", component: "=",
controller: "=", callback: "=",
callback: "=" onDismiss: "&"
}; };
} }
@@ -18,42 +18,23 @@ class ComponentModal {
controller($scope, $timeout, componentManager) { controller($scope, $timeout, componentManager) {
'ngInject'; 'ngInject';
let identifier = "modal-" + $scope.component.uuid; if($scope.component.directiveController) {
$scope.component.directiveController.dismiss = function(callback) {
$scope.component.directiveController.dismiss = function() { $scope.dismiss(callback);
$scope.component.show = false;
componentManager.deactivateComponent($scope.component);
componentManager.deregisterHandler(identifier);
$scope.el.remove();
}
$scope.dismiss = function() {
$scope.component.directiveController.dismiss();
}
$scope.url = function() {
return componentManager.urlForComponent($scope.component);
}
componentManager.registerHandler({identifier: identifier, areas: ["modal"], activationHandler: (component) => {
if(component.active) {
$timeout(function(){
var iframe = componentManager.iframeForComponent(component);
if(iframe) {
iframe.onload = function() {
componentManager.registerComponentWindow(component, iframe.contentWindow);
}.bind(this);
}
}.bind(this));
} }
}, }
actionHandler: function(component, action, data) {
if(action == "set-size") { $scope.dismiss = function(callback) {
componentManager.handleSetSizeEvent(component, data); var onDismiss = $scope.component.directiveController && $scope.component.directiveController.onDismiss();
} // Setting will null out compinent-view's component, which will handle deactivation
}.bind(this)}); $scope.component = null;
$timeout(() => {
$scope.el.remove();
onDismiss && onDismiss();
callback && callback();
})
}
componentManager.activateComponent($scope.component);
} }
} }

View File

@@ -1,18 +1,41 @@
class ComponentView { class ComponentView {
constructor() { constructor(componentManager, $timeout) {
this.restrict = "E"; this.restrict = "E";
this.templateUrl = "frontend/directives/component-view.html"; this.templateUrl = "frontend/directives/component-view.html";
this.scope = { this.scope = {
component: "=" component: "="
}; };
this.componentManager = componentManager;
this.timeout = $timeout;
} }
link($scope, el, attrs, ctrl) { link($scope, el, attrs, ctrl) {
$scope.el = el; $scope.el = el;
let identifier = "component-view-" + Math.random();
this.componentManager.registerHandler({identifier: identifier, areas: ["*"], activationHandler: (component) => {
if(component.active) {
this.timeout(function(){
var iframe = this.componentManager.iframeForComponent(component);
if(iframe) {
iframe.onload = function() {
this.componentManager.registerComponentWindow(component, iframe.contentWindow);
}.bind(this);
}
}.bind(this));
}
},
actionHandler: function(component, action, data) {
if(action == "set-size") {
this.componentManager.handleSetSizeEvent(component, data);
}
}.bind(this)});
$scope.$watch('component', function(component, prevComponent){ $scope.$watch('component', function(component, prevComponent){
console.log("Component View Setting Component", component); // console.log("Component View Setting Component", component);
ctrl.componentValueChanging(component, prevComponent); ctrl.componentValueChanging(component, prevComponent);
}); });
} }
@@ -20,6 +43,8 @@ class ComponentView {
controller($scope, $timeout, componentManager, desktopManager) { controller($scope, $timeout, componentManager, desktopManager) {
'ngInject'; 'ngInject';
console.log("Creating New Component View");
this.componentValueChanging = (component, prevComponent) => { this.componentValueChanging = (component, prevComponent) => {
if(prevComponent && component !== prevComponent) { if(prevComponent && component !== prevComponent) {
// Deactive old component // Deactive old component
@@ -33,39 +58,15 @@ class ComponentView {
} }
} }
let identifier = "component-view-" + Math.random(); $scope.$on("$destroy", function() {
console.log("DESTROY COMPONENT VIEW");
$scope.url = function() { componentManager.deregisterHandler($scope.identifier);
if($scope.component.offlineOnly) { if($scope.component) {
return $scope.component.local_url; componentManager.deactivateComponent($scope.component);
} }
});
if(desktopManager.isDesktop && $scope.component.local_url) {
return $scope.component.local_url;
}
return $scope.component.hosted_url || $scope.component.url;
}
componentManager.registerHandler({identifier: identifier, areas: ["*"], activationHandler: (component) => {
if(component.active) {
$timeout(function(){
var iframe = componentManager.iframeForComponent(component);
if(iframe) {
iframe.onload = function() {
componentManager.registerComponentWindow(component, iframe.contentWindow);
}.bind(this);
}
}.bind(this));
}
},
actionHandler: function(component, action, data) {
if(action == "set-size") {
componentManager.handleSetSizeEvent(component, data);
}
}.bind(this)});
} }
} }
angular.module('app.frontend').directive('componentView', () => new ComponentView); angular.module('app.frontend').directive('componentView', (componentManager, $timeout) => new ComponentView(componentManager, $timeout));

View File

@@ -30,7 +30,6 @@ class EditorMenu {
} }
$scope.toggleDefaultForEditor = function(editor) { $scope.toggleDefaultForEditor = function(editor) {
console.log("Toggling editor", editor);
if($scope.defaultEditor == editor) { if($scope.defaultEditor == editor) {
$scope.removeEditorDefault(editor); $scope.removeEditorDefault(editor);
} else { } else {

View File

@@ -30,8 +30,6 @@ class PermissionsModal {
controller($scope, modelManager) { controller($scope, modelManager) {
console.log("permissions", $scope.permissions);
$scope.formattedPermissions = $scope.permissions.map(function(permission){ $scope.formattedPermissions = $scope.permissions.map(function(permission){
if(permission.name === "stream-items") { if(permission.name === "stream-items") {
var types = permission.content_types.map(function(type){ var types = permission.content_types.map(function(type){

View File

@@ -1,54 +1,65 @@
class ThemeManager { class ThemeManager {
constructor(modelManager, syncManager, $rootScope, storageManager) { constructor(modelManager, syncManager, $rootScope, storageManager, componentManager) {
this.syncManager = syncManager; this.syncManager = syncManager;
this.modelManager = modelManager; this.modelManager = modelManager;
this.$rootScope = $rootScope; this.$rootScope = $rootScope;
this.storageManager = storageManager; this.storageManager = storageManager;
componentManager.registerHandler({identifier: "themeManager", areas: ["themes"], activationHandler: (component) => {
if(component.active) {
this.activateTheme(component);
} else {
this.deactivateTheme(component);
}
}});
} }
get themes() { get themes() {
return this.modelManager.itemsForContentType("SN|Theme"); return this.modelManager.itemsForContentType("SN|Theme");
} }
/* /*
activeTheme: computed property that returns saved theme activeTheme: computed property that returns saved theme
currentTheme: stored variable that allows other classes to watch changes currentTheme: stored variable that allows other classes to watch changes
*/ */
get activeTheme() { // get activeTheme() {
var activeThemeId = this.storageManager.getItem("activeTheme"); // var activeThemeId = this.storageManager.getItem("activeTheme");
if(!activeThemeId) { // if(!activeThemeId) {
return null; // return null;
} // }
//
var theme = _.find(this.themes, {uuid: activeThemeId}); // var theme = _.find(this.themes, {uuid: activeThemeId});
return theme; // return theme;
} // }
activateInitialTheme() { activateInitialTheme() {
var theme = this.activeTheme; // var theme = this.activeTheme;
if(theme) { // if(theme) {
this.activateTheme(theme); // this.activateTheme(theme);
} // }
} }
submitTheme(url) { // submitTheme(url) {
var name = this.displayNameForThemeFile(this.fileNameFromPath(url)); // var name = this.displayNameForThemeFile(this.fileNameFromPath(url));
var theme = this.modelManager.createItem({content_type: "SN|Theme", url: url, name: name}); // var theme = this.modelManager.createItem({content_type: "SN|Theme", url: url, name: name});
this.modelManager.addItem(theme); // this.modelManager.addItem(theme);
theme.setDirty(true); // theme.setDirty(true);
this.syncManager.sync(); // this.syncManager.sync();
} // }
activateTheme(theme) { activateTheme(theme) {
var activeTheme = this.activeTheme; if(this.activeTheme && this.activeTheme !== theme) {
if(activeTheme) { this.deactivateTheme(this.activeTheme);
this.deactivateTheme(activeTheme);
} }
var url = theme.computedUrl();
var link = document.createElement("link"); var link = document.createElement("link");
link.href = theme.url; link.href = url;
link.type = "text/css"; link.type = "text/css";
link.rel = "stylesheet"; link.rel = "stylesheet";
link.media = "screen,print"; link.media = "screen,print";
@@ -72,10 +83,6 @@ class ThemeManager {
this.$rootScope.$broadcast("theme-changed"); this.$rootScope.$broadcast("theme-changed");
} }
isThemeActive(theme) {
return this.storageManager.getItem("activeTheme") === theme.uuid;
}
fileNameFromPath(filePath) { fileNameFromPath(filePath) {
return filePath.replace(/^.*[\\\/]/, ''); return filePath.replace(/^.*[\\\/]/, '');
} }

View File

@@ -68,7 +68,7 @@ $heading-height: 75px;
#note-tags-component-container { #note-tags-component-container {
height: 50px; height: 50px;
#note-tags-iframe { iframe {
height: 50px; height: 50px;
width: 100%; width: 100%;
position: absolute; position: absolute;
@@ -116,8 +116,8 @@ $heading-height: 75px;
#editor-pane-component-stack { #editor-pane-component-stack {
width: 100%; width: 100%;
.component { .component-stack-item {
height: 50px; // height: 50px;
width: 100%; width: 100%;
position: relative; position: relative;
&:not(:last-child) { &:not(:last-child) {
@@ -128,28 +128,6 @@ $heading-height: 75px;
border-top: 1px solid $bg-color; border-top: 1px solid $bg-color;
} }
.exit-button {
width: 15px;
height: 100%;
position: absolute;
right: 0;
background-color: transparent;
cursor: pointer;
display: flex;
align-items: center;
color: rgba(black, 0.7);
text-align: center;
padding-left: 2px;
.content {
}
&:hover {
background-color: rgba(gray, 0.3);
}
}
iframe { iframe {
width: 100%; width: 100%;
} }

View File

@@ -12,6 +12,10 @@
} }
} }
.panel {
background-color: white;
}
.modal { .modal {
position: fixed; position: fixed;
margin-left: auto; margin-left: auto;
@@ -44,14 +48,12 @@
} }
} }
.modal-iframe-container { // Optionally use if .component-view container is not flex-based
.component-view-container {
flex-grow: 1; flex-grow: 1;
display: flex; display: flex;
height: 100%;
iframe { width: 100%;
flex-grow: 1;
width: 100%;
}
} }
.component-view { .component-view {

View File

@@ -0,0 +1,7 @@
.panel {
color: black;
a {
color: $blue-color;
}
}

View File

@@ -11,5 +11,6 @@ $dark-gray: #2e2e2e;
@import "app/menus"; @import "app/menus";
@import "app/modals"; @import "app/modals";
@import "app/lock-screen"; @import "app/lock-screen";
@import "app/stylekit-sub";
@import "ionicons"; @import "ionicons";

View File

@@ -1,7 +1,9 @@
.sn-component .background{"ng-click" => "dismiss()"}
.panel{"ng-attr-id" => "component-{{component.uuid}}"}
.header .content
%h1.title {{component.name}} .sn-component
%a.close-button.info{"ng-click" => "dismiss()"} Close .panel{"ng-attr-id" => "component-{{component.uuid}}"}
.modal-iframe-container{"ng-attr-id" => "component-{{component.uuid}}"} .header
%iframe{"ng-src" => "{{url() | trusted}}", "frameBorder" => "0", "sandbox" => "allow-scripts allow-top-navigation-by-user-activation allow-popups allow-popups-to-escape-sandbox allow-modals", "data-component-id" => "{{component.uuid}}"} %h1.title {{component.name}}
%a.close-button.info{"ng-click" => "dismiss()"} Close
%component-view.component-view{"component" => "component"}

View File

@@ -1,6 +1,6 @@
%iframe{"ng-if" => "component", %iframe{"ng-if" => "component",
"ng-attr-id" => "component-{{component.uuid}}", "ng-attr-id" => "component-{{component.uuid}}",
"ng-src" => "{{url() | trusted}}", "frameBorder" => "0", "ng-src" => "{{component.computedUrl() | trusted}}", "frameBorder" => "0",
"sandbox" => "allow-scripts allow-top-navigation-by-user-activation allow-popups allow-popups-to-escape-sandbox allow-modals", "sandbox" => "allow-scripts allow-top-navigation-by-user-activation allow-popups allow-popups-to-escape-sandbox allow-modals",
"data-component-id" => "{{component.uuid}}"} "data-component-id" => "{{component.uuid}}"}
Loading Loading

View File

@@ -24,8 +24,8 @@
%h3 %h3
%input.bold{"ng-if" => "theme.rename", "ng-model" => "theme.tempName", "ng-keyup" => "$event.keyCode == 13 && submitExtensionRename(theme);", "mb-autofocus" => "true", "should-focus" => "true"} %input.bold{"ng-if" => "theme.rename", "ng-model" => "theme.tempName", "ng-keyup" => "$event.keyCode == 13 && submitExtensionRename(theme);", "mb-autofocus" => "true", "should-focus" => "true"}
%span{"ng-if" => "!theme.rename"} {{theme.name}} %span{"ng-if" => "!theme.rename"} {{theme.name}}
%a{"ng-if" => "!themeManager.isThemeActive(theme)", "ng-click" => "themeManager.activateTheme(theme); $event.stopPropagation();"} Activate -# %a{"ng-if" => "!themeManager.isThemeActive(theme)", "ng-click" => "themeManager.activateTheme(theme); $event.stopPropagation();"} Activate
%a{"ng-if" => "themeManager.isThemeActive(theme)", "ng-click" => "themeManager.deactivateTheme(theme); $event.stopPropagation();"} Deactivate -# %a{"ng-if" => "themeManager.isThemeActive(theme)", "ng-click" => "themeManager.deactivateTheme(theme); $event.stopPropagation();"} Deactivate
.mt-3{"ng-if" => "theme.showDetails"} .mt-3{"ng-if" => "theme.showDetails"}
.link-group .link-group
%a{"ng-click" => "renameExtension(theme); $event.stopPropagation();"} Rename %a{"ng-click" => "renameExtension(theme); $event.stopPropagation();"} Rename

View File

@@ -5,7 +5,7 @@
.panel .panel
.header .header
%h1.title Activate Extension %h1.title Activate Extension
%a.close-button.info Cancel %a.close-button.info{"ng-click" => "deny()"} Cancel
.content .content
.panel-section .panel-section
.panel-row .panel-row
@@ -14,7 +14,6 @@
would like to interact with your would like to interact with your
%span{"ng-repeat" => "permission in formattedPermissions"} %span{"ng-repeat" => "permission in formattedPermissions"}
{{permission}}. {{permission}}.
-# %p.wrap URL: {{component.runningUrl}}
.panel-row .panel-row
%p %p

View File

@@ -8,8 +8,8 @@
#save-status{"ng-class" => "{'red bold': ctrl.saveError, 'orange bold': ctrl.syncTakingTooLong}", "ng-bind-html" => "ctrl.noteStatus"} #save-status{"ng-class" => "{'red bold': ctrl.saveError, 'orange bold': ctrl.syncTakingTooLong}", "ng-bind-html" => "ctrl.noteStatus"}
.editor-tags .editor-tags
#note-tags-component-container{"ng-if" => "ctrl.tagsComponent && ctrl.tagsComponent.active"} #note-tags-component-container{"ng-if" => "ctrl.tagsComponent"}
%iframe#note-tags-iframe{"ng-src" => "{{ctrl.componentManager.urlForComponent(ctrl.tagsComponent) | trusted}}", "frameBorder" => "0", "sandbox" => "allow-scripts", "data-component-id" => "{{ctrl.tagsComponent.uuid}}"} %component-view.component-view{ "component" => "ctrl.tagsComponent"}
%input.tags-input{"ng-if" => "!(ctrl.tagsComponent && ctrl.tagsComponent.active)", "type" => "text", "ng-keyup" => "$event.keyCode == 13 && $event.target.blur();", %input.tags-input{"ng-if" => "!(ctrl.tagsComponent && ctrl.tagsComponent.active)", "type" => "text", "ng-keyup" => "$event.keyCode == 13 && $event.target.blur();",
"ng-model" => "ctrl.tagsString", "placeholder" => "#tags", "ng-blur" => "ctrl.updateTagsFromTagsString($event, ctrl.tagsString)"} "ng-model" => "ctrl.tagsString", "placeholder" => "#tags", "ng-blur" => "ctrl.updateTagsFromTagsString($event, ctrl.tagsString)"}
@@ -42,8 +42,7 @@
.editor-content#editor-content{"ng-if" => "ctrl.noteReady && !ctrl.note.errorDecrypting"} .editor-content#editor-content{"ng-if" => "ctrl.noteReady && !ctrl.note.errorDecrypting"}
%panel-resizer.left{"panel-id" => "'editor-content'", "on-resize-finish" => "ctrl.onPanelResizeFinish","control" => "ctrl.resizeControl", "min-width" => 300, "property" => "'left'", "hoverable" => "true"} %panel-resizer.left{"panel-id" => "'editor-content'", "on-resize-finish" => "ctrl.onPanelResizeFinish","control" => "ctrl.resizeControl", "min-width" => 300, "property" => "'left'", "hoverable" => "true"}
-# ng-show is required here (as opposed to ng-if) in order for the component-view to receive events such as nulling ctrl.selectorEditor %component-view.component-view{"ng-if" => "ctrl.selectedEditor", "component" => "ctrl.selectedEditor"}
%component-view{"ng-show" => "ctrl.selectedEditor", "component" => "ctrl.selectedEditor", "class" => "component-view"}
%textarea.editable#note-text-editor{"ng-if" => "!ctrl.selectedEditor", "ng-model" => "ctrl.note.text", %textarea.editable#note-text-editor{"ng-if" => "!ctrl.selectedEditor", "ng-model" => "ctrl.note.text",
"ng-change" => "ctrl.contentChanged()", "ng-click" => "ctrl.clickedTextArea()", "ng-focus" => "ctrl.onContentFocus()", "dir" => "auto"} "ng-change" => "ctrl.contentChanged()", "ng-click" => "ctrl.clickedTextArea()", "ng-focus" => "ctrl.onContentFocus()", "dir" => "auto"}
{{ctrl.onSystemEditorLoad()}} {{ctrl.onSystemEditorLoad()}}
@@ -53,6 +52,4 @@
%p.medium-padding{"style" => "padding-top: 0 !important;"} There was an error decrypting this item. Ensure you are running the latest version of this app, then sign out and sign back in to try again. %p.medium-padding{"style" => "padding-top: 0 !important;"} There was an error decrypting this item. Ensure you are running the latest version of this app, then sign out and sign back in to try again.
#editor-pane-component-stack #editor-pane-component-stack
.component.component-stack-border{"ng-repeat" => "component in ctrl.componentStack", "ng-if" => "component.active", "ng-show" => "!component.ignoreEvents", "id" => "{{'component-' + component.uuid}}", "ng-mouseover" => "component.showExit = true", "ng-mouseleave" => "component.showExit = false"} %component-view.component-view.component-stack-item{"ng-repeat" => "component in ctrl.componentStack", "ng-if" => "component.active", "component" => "component"}
.exit-button.body-text-color{"ng-if" => "component.showExit", "ng-click" => "ctrl.disableComponentForCurrentItem(component, true)"} ×
%iframe{"ng-src" => "{{ctrl.componentManager.urlForComponent(component) | trusted}}", "frameBorder" => "0", "sandbox" => "allow-scripts allow-top-navigation-by-user-activation allow-popups allow-popups-to-escape-sandbox allow-modals", "data-component-id" => "{{component.uuid}}"}

View File

@@ -22,7 +22,7 @@
.item{"ng-repeat" => "room in ctrl.rooms"} .item{"ng-repeat" => "room in ctrl.rooms"}
.column{"ng-click" => "ctrl.selectRoom(room)"} .column{"ng-click" => "ctrl.selectRoom(room)"}
.label {{room.name}} .label {{room.name}}
%component-modal{"ng-if" => "room.show", "component" => "room"} %component-modal{"ng-if" => "room.showRoom", "component" => "room", "controller" => "room.directiveController"}
.right .right

View File

@@ -1,5 +1,6 @@
.section.tags#tags-column .section.tags#tags-column
%iframe#tags-list-iframe{"ng-if" => "ctrl.component && ctrl.component.active", "ng-src" => "{{ctrl.componentManager.urlForComponent(ctrl.component) | trusted}}", "frameBorder" => "0", "style" => "width: 100%; height: 100%;", "sandbox" => "allow-scripts"} .component-view-container{"ng-if" => "ctrl.component"}
%component-view.component-view{"component" => "ctrl.component"}
#tags-content.content{"ng-if" => "!(ctrl.component && ctrl.component.active)"} #tags-content.content{"ng-if" => "!(ctrl.component && ctrl.component.active)"}
#tags-title-bar.section-title-bar #tags-title-bar.section-title-bar
.section-title-bar-header .section-title-bar-header

View File

@@ -6,9 +6,12 @@
.sn-component .panel { .sn-component .panel {
box-shadow: 0px 2px 13px #C8C8C8; box-shadow: 0px 2px 13px #C8C8C8;
border-radius: 0.7rem; border-radius: 0.7rem;
overflow: scroll;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden;
}
.sn-component .panel a:hover {
text-decoration: underline;
} }
.sn-component .panel.static { .sn-component .panel.static {
box-shadow: none; box-shadow: none;
@@ -28,15 +31,16 @@
.sn-component .panel .header .close-button { .sn-component .panel .header .close-button {
font-weight: bold; font-weight: bold;
} }
.sn-component .panel .footer { .sn-component .panel .footer, .sn-component .panel .panel-footer {
padding: 1rem 2rem; padding: 1rem 2rem;
border-top: 1px solid #F1F1F1; border-top: 1px solid #E1E1E1;
box-sizing: border-box;
} }
.sn-component .panel .footer .left { .sn-component .panel .footer .left, .sn-component .panel .panel-footer .left {
text-align: right; text-align: right;
display: block; display: block;
} }
.sn-component .panel .footer .right { .sn-component .panel .footer .right, .sn-component .panel .panel-footer .right {
text-align: right; text-align: right;
display: block; display: block;
} }
@@ -44,75 +48,88 @@
padding: 1.6rem 2rem; padding: 1.6rem 2rem;
padding-bottom: 0; padding-bottom: 0;
flex-grow: 1; flex-grow: 1;
overflow: scroll;
} }
.sn-component .panel .content p { .sn-component .panel .content p {
color: #454545; color: #454545;
line-height: 1.3; line-height: 1.3;
} }
.sn-component .panel .content .label, .sn-component .panel .content .panel-section .subtitle { .sn-component .panel .content .label, .sn-component .panel .content .panel-section .subtitle, .sn-component .panel-section .panel .content .subtitle {
font-weight: bold; font-weight: bold;
} }
.sn-component .panel .content .panel-section { .sn-component .panel-section {
padding-bottom: 1.6rem; padding-bottom: 1.6rem;
}
.sn-component .panel .content .panel-section .panel-row {
display: flex; display: flex;
justify-content: space-between; flex-direction: column;
align-items: center;
padding-top: 0.4rem;
} }
.sn-component .panel .content .panel-section .panel-row.centered { .sn-component .panel-section.no-bottom-pad {
justify-content: center; padding-bottom: 0;
} }
.sn-component .panel .content .panel-section .panel-row .panel-column { .sn-component .panel-section.hero {
width: 100%;
}
.sn-component .panel .content .panel-section .panel-row:not(:last-child) {
padding-bottom: 0.4rem;
}
.sn-component .panel .content .panel-section .panel-row:not(:last-child).condensed {
padding-top: 0.2rem;
padding-bottom: 0.2rem;
}
.sn-component .panel .content .panel-section .panel-row p {
margin: 0;
padding: 0;
}
.sn-component .panel .content .panel-section.hero {
text-align: center; text-align: center;
} }
.sn-component .panel .content .panel-section p:last-child { .sn-component .panel-section p:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
.sn-component .panel .content .panel-section:not(:last-child) { .sn-component .panel-section:not(:last-child) {
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
border-bottom: 1px solid #DDDDDD; border-bottom: 1px solid #DDDDDD;
} }
.sn-component .panel .content .panel-section:last-child { .sn-component .panel-section:not(:last-child).no-border {
border-bottom: none;
}
.sn-component .panel-section:last-child {
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
.sn-component .panel .content .panel-section .outer-title { .sn-component .panel-section .outer-title {
border-bottom: 1px solid #DDDDDD; border-bottom: 1px solid #DDDDDD;
padding-bottom: 0.9rem; padding-bottom: 0.9rem;
margin-top: 2.1rem; margin-top: 2.1rem;
margin-bottom: 15px; margin-bottom: 15px;
} }
.sn-component .panel .content .panel-section .subtitle { .sn-component .panel-section .subtitle {
margin-top: -0.5rem; margin-top: -0.5rem;
} }
.sn-component .panel .content .panel-section .subtitle.subtle { .sn-component .panel-section .subtitle.subtle {
font-weight: normal; font-weight: normal;
opacity: 0.6; opacity: 0.6;
} }
.sn-component .panel .content .panel-form { .sn-component .panel-row {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 0.4rem;
}
.sn-component .panel-row.centered {
justify-content: center;
}
.sn-component .panel-row .panel-column {
width: 100%; width: 100%;
} }
.sn-component .panel .content .panel-form.half { .sn-component .panel-row.default-padding, .sn-component .panel-row:not(:last-child) {
padding-bottom: 0.4rem;
}
.sn-component .panel-row.default-padding.condensed, .sn-component .panel-row:not(:last-child).condensed {
padding-top: 0.2rem;
padding-bottom: 0.2rem;
}
.sn-component .panel-row p {
margin: 0;
padding: 0;
}
.sn-component .panel-form {
width: 100%;
}
.sn-component .panel-form.half {
width: 50%; width: 50%;
} }
.sn-component .panel .content .panel-form .form-submit { .sn-component .panel-form .form-submit {
margin-top: 0.15rem; margin-top: 0.15rem;
} }
.sn-component .right-aligned {
justify-content: flex-end;
text-align: right;
}
.sn-component .menu-panel { .sn-component .menu-panel {
box-shadow: 0px 4px 4px #C8C8C8; box-shadow: 0px 4px 4px #C8C8C8;
border-radius: 0.6rem; border-radius: 0.6rem;
@@ -156,14 +173,14 @@
.sn-component .menu-panel .row .column .left { .sn-component .menu-panel .row .column .left {
display: flex; display: flex;
} }
.sn-component .menu-panel .row .button .label, .sn-component .menu-panel .row .box .label, .sn-component .menu-panel .row .circle .label, .sn-component .menu-panel .row .button .panel .content .panel-section .subtitle, .sn-component .panel .content .panel-section .menu-panel .row .button .subtitle, .sn-component .menu-panel .row .box .panel .content .panel-section .subtitle, .sn-component .panel .content .panel-section .menu-panel .row .box .subtitle, .sn-component .menu-panel .row .circle .panel .content .panel-section .subtitle, .sn-component .panel .content .panel-section .menu-panel .row .circle .subtitle { .sn-component .menu-panel .row .button .label, .sn-component .menu-panel .row .box .label, .sn-component .menu-panel .row .circle .label, .sn-component .menu-panel .row .button .panel-section .subtitle, .sn-component .panel-section .menu-panel .row .button .subtitle, .sn-component .menu-panel .row .box .panel-section .subtitle, .sn-component .panel-section .menu-panel .row .box .subtitle, .sn-component .menu-panel .row .circle .panel-section .subtitle, .sn-component .panel-section .menu-panel .row .circle .subtitle {
font-size: 0.8rem; font-size: 0.8rem;
font-weight: normal; font-weight: normal;
} }
.sn-component .menu-panel .row:hover { .sn-component .menu-panel .row:hover {
background-color: #efefef; background-color: #efefef;
} }
.sn-component .menu-panel .row .label, .sn-component .menu-panel .row .panel .content .panel-section .subtitle, .sn-component .panel .content .panel-section .menu-panel .row .subtitle { .sn-component .menu-panel .row .label, .sn-component .menu-panel .row .panel-section .subtitle, .sn-component .panel-section .menu-panel .row .subtitle {
font-size: 1rem; font-size: 1rem;
font-weight: bold; font-weight: bold;
} }
@@ -270,6 +287,9 @@
.sn-component .horizontal-group > *:not(:first-child), .sn-component .input-group > *:not(:first-child) { .sn-component .horizontal-group > *:not(:first-child), .sn-component .input-group > *:not(:first-child) {
margin-left: 0.9rem; margin-left: 0.9rem;
} }
.sn-component .border-bottom {
border-bottom: 1px solid #DDDDDD;
}
.sn-component .checkbox-group { .sn-component .checkbox-group {
padding-top: 0.5rem; padding-top: 0.5rem;
padding-bottom: 0.3rem; padding-bottom: 0.3rem;
@@ -322,7 +342,7 @@
text-align: center; text-align: center;
border: 1px solid; border: 1px solid;
} }
.sn-component .button .label, .sn-component .box .label, .sn-component .circle .label, .sn-component .button .panel .content .panel-section .subtitle, .sn-component .panel .content .panel-section .button .subtitle, .sn-component .box .panel .content .panel-section .subtitle, .sn-component .panel .content .panel-section .box .subtitle, .sn-component .circle .panel .content .panel-section .subtitle, .sn-component .panel .content .panel-section .circle .subtitle { .sn-component .button .label, .sn-component .box .label, .sn-component .circle .label, .sn-component .button .panel-section .subtitle, .sn-component .panel-section .button .subtitle, .sn-component .box .panel-section .subtitle, .sn-component .panel-section .box .subtitle, .sn-component .circle .panel-section .subtitle, .sn-component .panel-section .circle .subtitle {
font-weight: bold; font-weight: bold;
display: block; display: block;
text-align: center; text-align: center;
@@ -602,10 +622,10 @@
.sn-component .app-bar .item.no-pointer { .sn-component .app-bar .item.no-pointer {
cursor: default; cursor: default;
} }
.sn-component .app-bar .item:hover > .label:not(.subtle), .sn-component .app-bar .panel .content .panel-section .item:hover > .subtitle:not(.subtle), .sn-component .panel .content .panel-section .app-bar .item:hover > .subtitle:not(.subtle), .sn-component .app-bar .item:hover > .sublabel:not(.subtle), .sn-component .app-bar .item:hover > .column > .label:not(.subtle), .sn-component .app-bar .panel .content .panel-section .item:hover > .column > .subtitle:not(.subtle), .sn-component .panel .content .panel-section .app-bar .item:hover > .column > .subtitle:not(.subtle), .sn-component .app-bar .item:hover > .column > .sublabel:not(.subtle) { .sn-component .app-bar .item:hover > .label:not(.subtle), .sn-component .app-bar .panel-section .item:hover > .subtitle:not(.subtle), .sn-component .panel-section .app-bar .item:hover > .subtitle:not(.subtle), .sn-component .app-bar .item:hover > .sublabel:not(.subtle), .sn-component .app-bar .item:hover > .column > .label:not(.subtle), .sn-component .app-bar .panel-section .item:hover > .column > .subtitle:not(.subtle), .sn-component .panel-section .app-bar .item:hover > .column > .subtitle:not(.subtle), .sn-component .app-bar .item:hover > .column > .sublabel:not(.subtle) {
color: #086DD6; color: #086DD6;
} }
.sn-component .app-bar .item > .label, .sn-component .app-bar .panel .content .panel-section .item > .subtitle, .sn-component .panel .content .panel-section .app-bar .item > .subtitle, .sn-component .app-bar .item > .column > .label, .sn-component .app-bar .panel .content .panel-section .item > .column > .subtitle, .sn-component .panel .content .panel-section .app-bar .item > .column > .subtitle { .sn-component .app-bar .item > .label, .sn-component .app-bar .panel-section .item > .subtitle, .sn-component .panel-section .app-bar .item > .subtitle, .sn-component .app-bar .item > .column > .label, .sn-component .app-bar .panel-section .item > .column > .subtitle, .sn-component .panel-section .app-bar .item > .column > .subtitle {
font-weight: bold; font-weight: bold;
font-size: 0.9rem; font-size: 0.9rem;
white-space: nowrap; white-space: nowrap;