mirror of
https://github.com/nasa/openmct.git
synced 2025-01-03 03:46:42 +00:00
Merge branch 'master' of https://github.com/nasa/openmctweb into open26
This commit is contained in:
commit
d120c8b139
@ -78,11 +78,6 @@
|
|||||||
"key": "navigationService",
|
"key": "navigationService",
|
||||||
"implementation": "navigation/NavigationService.js"
|
"implementation": "navigation/NavigationService.js"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"key": "urlService",
|
|
||||||
"implementation": "services/UrlService.js",
|
|
||||||
"depends": [ "$location" ]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"key": "creationService",
|
"key": "creationService",
|
||||||
"implementation": "creation/CreationService.js",
|
"implementation": "creation/CreationService.js",
|
||||||
|
@ -52,13 +52,18 @@ define(
|
|||||||
unlisten;
|
unlisten;
|
||||||
|
|
||||||
unlisten = $scope.$on('$locationChangeSuccess', function () {
|
unlisten = $scope.$on('$locationChangeSuccess', function () {
|
||||||
|
// Checks path to make sure /browse/ is at front
|
||||||
|
// if so, change $route.current
|
||||||
|
if ($location.path().indexOf("/browse/") === 0) {
|
||||||
$route.current = priorRoute;
|
$route.current = priorRoute;
|
||||||
|
}
|
||||||
unlisten();
|
unlisten();
|
||||||
});
|
});
|
||||||
// urlService.urlFor used to adjust current
|
// urlService.urlForLocation used to adjust current
|
||||||
// path to new, addressed, path based on
|
// path to new, addressed, path based on
|
||||||
// domainObject
|
// domainObject
|
||||||
$location.path(urlService.urlFor("browse", domainObject));
|
$location.path(urlService.urlForLocation("browse", domainObject));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback for updating the in-scope reference to the object
|
// Callback for updating the in-scope reference to the object
|
||||||
|
@ -54,7 +54,11 @@ define(
|
|||||||
if (viewKey) {
|
if (viewKey) {
|
||||||
$location.search('view', viewKey);
|
$location.search('view', viewKey);
|
||||||
unlisten = $scope.$on('$locationChangeSuccess', function () {
|
unlisten = $scope.$on('$locationChangeSuccess', function () {
|
||||||
|
// Checks path to make sure /browse/ is at front
|
||||||
|
// if so, change $route.current
|
||||||
|
if ($location.path().indexOf("/browse/") === 0) {
|
||||||
$route.current = priorRoute;
|
$route.current = priorRoute;
|
||||||
|
}
|
||||||
unlisten();
|
unlisten();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ define(
|
|||||||
// (browse) and the domainObject is passed in and
|
// (browse) and the domainObject is passed in and
|
||||||
// the path is returned and opened in a new tab
|
// the path is returned and opened in a new tab
|
||||||
perform: function () {
|
perform: function () {
|
||||||
$window.open(urlService.urlFor("browse", getSelectedObject()),
|
$window.open(urlService.urlForNewTab("browse", getSelectedObject()),
|
||||||
"_blank");
|
"_blank");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -61,7 +61,7 @@ define(
|
|||||||
);
|
);
|
||||||
mockUrlService = jasmine.createSpyObj(
|
mockUrlService = jasmine.createSpyObj(
|
||||||
"urlService",
|
"urlService",
|
||||||
["urlFor"]
|
["urlForLocation"]
|
||||||
);
|
);
|
||||||
mockObjectService = jasmine.createSpyObj(
|
mockObjectService = jasmine.createSpyObj(
|
||||||
"objectService",
|
"objectService",
|
||||||
@ -222,15 +222,20 @@ define(
|
|||||||
mockNavigationService.addListener.mostRecentCall.args[0](
|
mockNavigationService.addListener.mostRecentCall.args[0](
|
||||||
mockNextObject
|
mockNextObject
|
||||||
);
|
);
|
||||||
// location.path to be called with the urlService's
|
|
||||||
// urlFor function with the next domainObject and mode
|
// Allows the path index to be checked
|
||||||
expect(mockLocation.path).toHaveBeenCalledWith(
|
// prior to setting $route.current
|
||||||
mockUrlService.urlFor(mockMode, mockNextObject)
|
mockLocation.path.andReturn("/browse/");
|
||||||
);
|
|
||||||
|
|
||||||
// Exercise the Angular workaround
|
// Exercise the Angular workaround
|
||||||
mockScope.$on.mostRecentCall.args[1]();
|
mockScope.$on.mostRecentCall.args[1]();
|
||||||
expect(mockUnlisten).toHaveBeenCalled();
|
expect(mockUnlisten).toHaveBeenCalled();
|
||||||
|
|
||||||
|
// location.path to be called with the urlService's
|
||||||
|
// urlFor function with the next domainObject and mode
|
||||||
|
expect(mockLocation.path).toHaveBeenCalledWith(
|
||||||
|
mockUrlService.urlForLocation(mockMode, mockNextObject)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -68,6 +68,10 @@ define(
|
|||||||
fireWatch("representation.selected.key", "xyz");
|
fireWatch("representation.selected.key", "xyz");
|
||||||
expect(mockLocation.search).toHaveBeenCalledWith('view', "xyz");
|
expect(mockLocation.search).toHaveBeenCalledWith('view', "xyz");
|
||||||
|
|
||||||
|
// Allows the path index to be checked
|
||||||
|
// prior to setting $route.current
|
||||||
|
mockLocation.path.andReturn("/browse/");
|
||||||
|
|
||||||
// Exercise the Angular workaround
|
// Exercise the Angular workaround
|
||||||
mockScope.$on.mostRecentCall.args[1]();
|
mockScope.$on.mostRecentCall.args[1]();
|
||||||
expect(mockUnlisten).toHaveBeenCalled();
|
expect(mockUnlisten).toHaveBeenCalled();
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
"creation/LocatorController",
|
"creation/LocatorController",
|
||||||
"navigation/NavigateAction",
|
"navigation/NavigateAction",
|
||||||
"navigation/NavigationService",
|
"navigation/NavigationService",
|
||||||
"services/UrlService",
|
|
||||||
"windowing/FullscreenAction",
|
"windowing/FullscreenAction",
|
||||||
"windowing/NewTabAction",
|
"windowing/NewTabAction",
|
||||||
"windowing/WindowTitler"
|
"windowing/WindowTitler"
|
||||||
|
@ -53,7 +53,7 @@ define(
|
|||||||
|
|
||||||
// Mocks the urlService used to make the new tab's url from a
|
// Mocks the urlService used to make the new tab's url from a
|
||||||
// domainObject and mode
|
// domainObject and mode
|
||||||
mockUrlService = jasmine.createSpyObj("urlService", ["urlFor"]);
|
mockUrlService = jasmine.createSpyObj("urlService", ["urlForNewTab"]);
|
||||||
|
|
||||||
// Action done using the current context or mockContextCurrent
|
// Action done using the current context or mockContextCurrent
|
||||||
actionCurrent = new NewTabAction(mockUrlService, mockWindow,
|
actionCurrent = new NewTabAction(mockUrlService, mockWindow,
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"implementation": "actions/SaveAction.js",
|
"implementation": "actions/SaveAction.js",
|
||||||
"name": "Save",
|
"name": "Save",
|
||||||
"description": "Save changes made to these objects.",
|
"description": "Save changes made to these objects.",
|
||||||
"depends": [ "$location" ],
|
"depends": [ "$location", "urlService" ],
|
||||||
"priority": "mandatory"
|
"priority": "mandatory"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -76,7 +76,7 @@
|
|||||||
"implementation": "actions/CancelAction.js",
|
"implementation": "actions/CancelAction.js",
|
||||||
"name": "Cancel",
|
"name": "Cancel",
|
||||||
"description": "Discard changes made to these objects.",
|
"description": "Discard changes made to these objects.",
|
||||||
"depends": [ "$location" ]
|
"depends": [ "$location", "urlService" ]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"policies": [
|
"policies": [
|
||||||
|
@ -30,7 +30,7 @@ define(
|
|||||||
* Edit Mode. Exits the editing user interface and invokes object
|
* Edit Mode. Exits the editing user interface and invokes object
|
||||||
* capabilities to persist the changes that have been made.
|
* capabilities to persist the changes that have been made.
|
||||||
*/
|
*/
|
||||||
function CancelAction($location, context) {
|
function CancelAction($location, urlService, context) {
|
||||||
var domainObject = context.domainObject;
|
var domainObject = context.domainObject;
|
||||||
|
|
||||||
// Look up the object's "editor.completion" capability;
|
// Look up the object's "editor.completion" capability;
|
||||||
@ -50,7 +50,10 @@ define(
|
|||||||
// Discard the current root view (which will be the editing
|
// Discard the current root view (which will be the editing
|
||||||
// UI, which will have been pushed atop the Browise UI.)
|
// UI, which will have been pushed atop the Browise UI.)
|
||||||
function returnToBrowse() {
|
function returnToBrowse() {
|
||||||
$location.path("/browse");
|
$location.path($location.path(urlService.urlForLocation(
|
||||||
|
"browse",
|
||||||
|
domainObject
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -31,7 +31,7 @@ define(
|
|||||||
* Edit Mode. Exits the editing user interface and invokes object
|
* Edit Mode. Exits the editing user interface and invokes object
|
||||||
* capabilities to persist the changes that have been made.
|
* capabilities to persist the changes that have been made.
|
||||||
*/
|
*/
|
||||||
function SaveAction($location, context) {
|
function SaveAction($location, urlService, context) {
|
||||||
var domainObject = context.domainObject;
|
var domainObject = context.domainObject;
|
||||||
|
|
||||||
// Invoke any save behavior introduced by the editor capability;
|
// Invoke any save behavior introduced by the editor capability;
|
||||||
@ -45,7 +45,10 @@ define(
|
|||||||
// Discard the current root view (which will be the editing
|
// Discard the current root view (which will be the editing
|
||||||
// UI, which will have been pushed atop the Browise UI.)
|
// UI, which will have been pushed atop the Browise UI.)
|
||||||
function returnToBrowse() {
|
function returnToBrowse() {
|
||||||
return $location.path("/browse");
|
return $location.path(urlService.urlForLocation(
|
||||||
|
"browse",
|
||||||
|
domainObject
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -30,6 +30,7 @@ define(
|
|||||||
var mockLocation,
|
var mockLocation,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockEditorCapability,
|
mockEditorCapability,
|
||||||
|
mockUrlService,
|
||||||
actionContext,
|
actionContext,
|
||||||
action;
|
action;
|
||||||
|
|
||||||
@ -54,7 +55,10 @@ define(
|
|||||||
"editor",
|
"editor",
|
||||||
[ "save", "cancel" ]
|
[ "save", "cancel" ]
|
||||||
);
|
);
|
||||||
|
mockUrlService = jasmine.createSpyObj(
|
||||||
|
"urlService",
|
||||||
|
["urlForLocation"]
|
||||||
|
);
|
||||||
|
|
||||||
actionContext = {
|
actionContext = {
|
||||||
domainObject: mockDomainObject
|
domainObject: mockDomainObject
|
||||||
@ -64,7 +68,7 @@ define(
|
|||||||
mockDomainObject.getCapability.andReturn(mockEditorCapability);
|
mockDomainObject.getCapability.andReturn(mockEditorCapability);
|
||||||
mockEditorCapability.cancel.andReturn(mockPromise(true));
|
mockEditorCapability.cancel.andReturn(mockPromise(true));
|
||||||
|
|
||||||
action = new CancelAction(mockLocation, actionContext);
|
action = new CancelAction(mockLocation, mockUrlService, actionContext);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -91,7 +95,9 @@ define(
|
|||||||
|
|
||||||
it("returns to browse when performed", function () {
|
it("returns to browse when performed", function () {
|
||||||
action.perform();
|
action.perform();
|
||||||
expect(mockLocation.path).toHaveBeenCalledWith("/browse");
|
expect(mockLocation.path).toHaveBeenCalledWith(
|
||||||
|
mockUrlService.urlForLocation("browse", mockDomainObject)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ define(
|
|||||||
var mockLocation,
|
var mockLocation,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockEditorCapability,
|
mockEditorCapability,
|
||||||
|
mockUrlService,
|
||||||
actionContext,
|
actionContext,
|
||||||
action;
|
action;
|
||||||
|
|
||||||
@ -54,6 +55,10 @@ define(
|
|||||||
"editor",
|
"editor",
|
||||||
[ "save", "cancel" ]
|
[ "save", "cancel" ]
|
||||||
);
|
);
|
||||||
|
mockUrlService = jasmine.createSpyObj(
|
||||||
|
"urlService",
|
||||||
|
["urlForLocation"]
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
actionContext = {
|
actionContext = {
|
||||||
@ -64,7 +69,7 @@ define(
|
|||||||
mockDomainObject.getCapability.andReturn(mockEditorCapability);
|
mockDomainObject.getCapability.andReturn(mockEditorCapability);
|
||||||
mockEditorCapability.save.andReturn(mockPromise(true));
|
mockEditorCapability.save.andReturn(mockPromise(true));
|
||||||
|
|
||||||
action = new SaveAction(mockLocation, actionContext);
|
action = new SaveAction(mockLocation, mockUrlService, actionContext);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -91,7 +96,9 @@ define(
|
|||||||
|
|
||||||
it("returns to browse when performed", function () {
|
it("returns to browse when performed", function () {
|
||||||
action.perform();
|
action.perform();
|
||||||
expect(mockLocation.path).toHaveBeenCalledWith("/browse");
|
expect(mockLocation.path).toHaveBeenCalledWith(
|
||||||
|
mockUrlService.urlForLocation("browse", mockDomainObject)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,13 @@
|
|||||||
"description": "General UI elements, meant to be reused across modes",
|
"description": "General UI elements, meant to be reused across modes",
|
||||||
"resources": "res",
|
"resources": "res",
|
||||||
"extensions": {
|
"extensions": {
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"key": "urlService",
|
||||||
|
"implementation": "/services/UrlService.js",
|
||||||
|
"depends": [ "$location" ]
|
||||||
|
}
|
||||||
|
],
|
||||||
"runs": [
|
"runs": [
|
||||||
{
|
{
|
||||||
"implementation": "StyleSheetLoader.js",
|
"implementation": "StyleSheetLoader.js",
|
||||||
|
Binary file not shown.
@ -75,4 +75,5 @@
|
|||||||
<glyph unicode="õ" d="M638 898c0 35.4-28.6 64-64 64h-128c-35.4 0-64-28.6-64-64s28.6-64 64-64h128c35.4 0 64 28.6 64 64zM510 834c-247.4 0-448-200.6-448-448s200.6-448 448-448 448 200.6 448 448-200.6 448-448 448zM510 386h-336c0 185.2 150.8 336 336 336v-336z" />
|
<glyph unicode="õ" d="M638 898c0 35.4-28.6 64-64 64h-128c-35.4 0-64-28.6-64-64s28.6-64 64-64h128c35.4 0 64 28.6 64 64zM510 834c-247.4 0-448-200.6-448-448s200.6-448 448-448 448 200.6 448 448-200.6 448-448 448zM510 386h-336c0 185.2 150.8 336 336 336v-336z" />
|
||||||
<glyph unicode="ö" d="M448 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM448 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320z" />
|
<glyph unicode="ö" d="M448 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM448 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320z" />
|
||||||
<glyph unicode="" d="M998.208 111.136l-422.702 739.728c-34.928 61.124-92.084 61.124-127.012 0l-422.702-739.728c-34.928-61.126-5.906-111.136 64.494-111.136h843.428c70.4 0 99.422 50.010 64.494 111.136zM512 128c-35.2 0-64 28.8-64 64s28.8 64 64 64 64-28.8 64-64c0-35.2-28.8-64-64-64zM627.448 577.242l-38.898-194.486c-6.902-34.516-41.35-62.756-76.55-62.756s-69.648 28.24-76.552 62.758l-38.898 194.486c-6.902 34.516 16.25 62.756 51.45 62.756h128c35.2 0 58.352-28.24 51.448-62.758z" />
|
<glyph unicode="" d="M998.208 111.136l-422.702 739.728c-34.928 61.124-92.084 61.124-127.012 0l-422.702-739.728c-34.928-61.126-5.906-111.136 64.494-111.136h843.428c70.4 0 99.422 50.010 64.494 111.136zM512 128c-35.2 0-64 28.8-64 64s28.8 64 64 64 64-28.8 64-64c0-35.2-28.8-64-64-64zM627.448 577.242l-38.898-194.486c-6.902-34.516-41.35-62.756-76.55-62.756s-69.648 28.24-76.552 62.758l-38.898 194.486c-6.902 34.516 16.25 62.756 51.45 62.756h128c35.2 0 58.352-28.24 51.448-62.758z" />
|
||||||
|
<glyph unicode="" d="M1024 448l-448-512v1024zM448 960l-448-512 448-512z" />
|
||||||
</font></defs></svg>
|
</font></defs></svg>
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Binary file not shown.
Binary file not shown.
@ -57,13 +57,17 @@ define(
|
|||||||
// mouse may leave this element during the drag.
|
// mouse may leave this element during the drag.
|
||||||
var body = $document.find('body'),
|
var body = $document.find('body'),
|
||||||
initialPosition,
|
initialPosition,
|
||||||
|
$event,
|
||||||
delta;
|
delta;
|
||||||
|
|
||||||
// Utility function to cause evaluation of mctDrag,
|
// Utility function to cause evaluation of mctDrag,
|
||||||
// mctDragUp, etc
|
// mctDragUp, etc
|
||||||
function fireListener(name) {
|
function fireListener(name) {
|
||||||
// Evaluate the expression, with current delta
|
// Evaluate the expression, with current delta
|
||||||
scope.$eval(attrs[name], { delta: delta });
|
scope.$eval(attrs[name], {
|
||||||
|
delta: delta,
|
||||||
|
$event: $event
|
||||||
|
});
|
||||||
|
|
||||||
// Trigger prompt digestion
|
// Trigger prompt digestion
|
||||||
scope.$apply();
|
scope.$apply();
|
||||||
@ -82,6 +86,9 @@ define(
|
|||||||
delta = currentPosition.map(function (v, i) {
|
delta = currentPosition.map(function (v, i) {
|
||||||
return v - initialPosition[i];
|
return v - initialPosition[i];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Also track the plain event for firing listeners
|
||||||
|
$event = event;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called during a drag, on mousemove
|
// Called during a drag, on mousemove
|
||||||
@ -106,7 +113,7 @@ define(
|
|||||||
|
|
||||||
fireListener("mctDragUp");
|
fireListener("mctDragUp");
|
||||||
|
|
||||||
// Clear out start-of-drag position
|
// Clear out start-of-drag position, target
|
||||||
initialPosition = undefined;
|
initialPosition = undefined;
|
||||||
|
|
||||||
// Don't show selection highlights, etc
|
// Don't show selection highlights, etc
|
||||||
@ -131,6 +138,7 @@ define(
|
|||||||
|
|
||||||
// Don't show selection highlights, etc
|
// Don't show selection highlights, etc
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,34 +39,52 @@ define(
|
|||||||
// is returned. The view is defaulted to
|
// is returned. The view is defaulted to
|
||||||
// the current location's (current object's)
|
// the current location's (current object's)
|
||||||
// view set.
|
// view set.
|
||||||
function urlFor(mode, domainObject) {
|
function urlForLocation(mode, domainObject) {
|
||||||
var context = domainObject &&
|
var context = domainObject &&
|
||||||
domainObject.getCapability('context'),
|
domainObject.getCapability('context'),
|
||||||
objectPath = context ? context.getPath() : [],
|
objectPath = context ? context.getPath() : [],
|
||||||
ids = objectPath.map(function (domainObject) {
|
ids = objectPath.map(function (domainObject) {
|
||||||
return domainObject.getId();
|
return domainObject.getId();
|
||||||
}),
|
}),
|
||||||
viewPath = "?view=" + $location.search().view,
|
|
||||||
// Parses the path together. Starts with the
|
// Parses the path together. Starts with the
|
||||||
// default index.html file, then the mode passed
|
// default index.html file, then the mode passed
|
||||||
// into the service, followed by ids in the url
|
// into the service, followed by ids in the url
|
||||||
// joined by '/', and lastly the view path from
|
// joined by '/', and lastly the view path from
|
||||||
// the current location
|
// the current location
|
||||||
path = "index.html#/" + mode + "/" +
|
path = mode + "/" + ids.slice(1).join("/");
|
||||||
ids.slice(1).join("/") + viewPath;
|
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uses the Url for the current location
|
||||||
|
// from the urlForLocation function and
|
||||||
|
// includes the view and the index path
|
||||||
|
function urlForNewTab(mode, domainObject) {
|
||||||
|
var viewPath = "?view=" + $location.search().view,
|
||||||
|
newTabPath =
|
||||||
|
"index.html#" + urlForLocation(mode, domainObject) + viewPath;
|
||||||
|
return newTabPath;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
* Returns the Url path for a specific domain object
|
* Returns the Url path for a specific domain object
|
||||||
|
* without the index.html path and the view path
|
||||||
* @param {value} value of the browse or edit mode
|
* @param {value} value of the browse or edit mode
|
||||||
* for the path
|
* for the path
|
||||||
* @param {DomainObject} value of the domain object
|
* @param {DomainObject} value of the domain object
|
||||||
* to get the path of
|
* to get the path of
|
||||||
*/
|
*/
|
||||||
urlFor: urlFor
|
urlForNewTab: urlForNewTab,
|
||||||
|
/**
|
||||||
|
* Returns the Url path for a specific domain object
|
||||||
|
* including the index.html path and the view path
|
||||||
|
* allowing a new tab to hold the correct characteristics
|
||||||
|
* @param {value} value of the browse or edit mode
|
||||||
|
* for the path
|
||||||
|
* @param {DomainObject} value of the domain object
|
||||||
|
* to get the path of
|
||||||
|
*/
|
||||||
|
urlForLocation: urlForLocation
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -81,10 +81,11 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("invokes mctDragDown when dragging begins", function () {
|
it("invokes mctDragDown when dragging begins", function () {
|
||||||
mockElement.on.mostRecentCall.args[1](testEvent(42, 60));
|
var event = testEvent(42, 60);
|
||||||
|
mockElement.on.mostRecentCall.args[1](event);
|
||||||
expect(mockScope.$eval).toHaveBeenCalledWith(
|
expect(mockScope.$eval).toHaveBeenCalledWith(
|
||||||
testAttrs.mctDragDown,
|
testAttrs.mctDragDown,
|
||||||
{ delta: [0, 0] }
|
{ delta: [0, 0], $event: event }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -101,23 +102,27 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("invokes mctDrag expression during drag", function () {
|
it("invokes mctDrag expression during drag", function () {
|
||||||
|
var event;
|
||||||
|
|
||||||
mockElement.on.mostRecentCall.args[1](testEvent(42, 60));
|
mockElement.on.mostRecentCall.args[1](testEvent(42, 60));
|
||||||
|
|
||||||
// Find and invoke the mousemove listener
|
// Find and invoke the mousemove listener
|
||||||
mockBody.on.calls.forEach(function (call) {
|
mockBody.on.calls.forEach(function (call) {
|
||||||
if (call.args[0] === 'mousemove') {
|
if (call.args[0] === 'mousemove') {
|
||||||
call.args[1](testEvent(52, 200));
|
call.args[1](event = testEvent(52, 200));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Should have passed that delta to mct-drag expression
|
// Should have passed that delta to mct-drag expression
|
||||||
expect(mockScope.$eval).toHaveBeenCalledWith(
|
expect(mockScope.$eval).toHaveBeenCalledWith(
|
||||||
testAttrs.mctDrag,
|
testAttrs.mctDrag,
|
||||||
{ delta: [10, 140] }
|
{ delta: [10, 140], $event: event }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("invokes mctDragUp expression after drag", function () {
|
it("invokes mctDragUp expression after drag", function () {
|
||||||
|
var event;
|
||||||
|
|
||||||
mockElement.on.mostRecentCall.args[1](testEvent(42, 60));
|
mockElement.on.mostRecentCall.args[1](testEvent(42, 60));
|
||||||
|
|
||||||
// Find and invoke the mousemove listener
|
// Find and invoke the mousemove listener
|
||||||
@ -129,7 +134,7 @@ define(
|
|||||||
// Find and invoke the mousemove listener
|
// Find and invoke the mousemove listener
|
||||||
mockBody.on.calls.forEach(function (call) {
|
mockBody.on.calls.forEach(function (call) {
|
||||||
if (call.args[0] === 'mouseup') {
|
if (call.args[0] === 'mouseup') {
|
||||||
call.args[1](testEvent(40, 71));
|
call.args[1](event = testEvent(40, 71));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -138,7 +143,7 @@ define(
|
|||||||
// initial position
|
// initial position
|
||||||
expect(mockScope.$eval).toHaveBeenCalledWith(
|
expect(mockScope.$eval).toHaveBeenCalledWith(
|
||||||
testAttrs.mctDragUp,
|
testAttrs.mctDragUp,
|
||||||
{ delta: [-2, 11] }
|
{ delta: [-2, 11], $event: event }
|
||||||
);
|
);
|
||||||
|
|
||||||
// Should also have unregistered listeners
|
// Should also have unregistered listeners
|
||||||
|
@ -31,7 +31,11 @@ define(
|
|||||||
|
|
||||||
describe("The url service", function () {
|
describe("The url service", function () {
|
||||||
var urlService,
|
var urlService,
|
||||||
mockLocation;
|
mockLocation,
|
||||||
|
mockDomainObject,
|
||||||
|
mockContext,
|
||||||
|
mockMode,
|
||||||
|
testViews;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
// Creates a mockLocation, used to
|
// Creates a mockLocation, used to
|
||||||
@ -41,23 +45,19 @@ define(
|
|||||||
[ "path", "search" ]
|
[ "path", "search" ]
|
||||||
);
|
);
|
||||||
|
|
||||||
urlService = new UrlService(mockLocation);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("get url for a domainObject and mode", function () {
|
|
||||||
// The mockDomainObject is initialized as a
|
// The mockDomainObject is initialized as a
|
||||||
// spy object to ultimately be passed into the
|
// spy object to ultimately be passed into the
|
||||||
// urlService urlFor function
|
// urlService urlFor function
|
||||||
var mockDomainObject = jasmine.createSpyObj(
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
"domainObject",
|
"domainObject",
|
||||||
[ "getId", "getCapability", "getModel", "useCapability" ]
|
[ "getId", "getCapability", "getModel", "useCapability" ]
|
||||||
),
|
);
|
||||||
mockContext = jasmine.createSpyObj('context', ['getPath']),
|
mockContext = jasmine.createSpyObj('context', ['getPath']);
|
||||||
testViews = [
|
testViews = [
|
||||||
{ key: 'abc' },
|
{ key: 'abc' },
|
||||||
{ key: 'def', someKey: 'some value' },
|
{ key: 'def', someKey: 'some value' },
|
||||||
{ key: 'xyz' }
|
{ key: 'xyz' }
|
||||||
],
|
];
|
||||||
mockMode = "browse";
|
mockMode = "browse";
|
||||||
|
|
||||||
// The mockContext is set a path
|
// The mockContext is set a path
|
||||||
@ -81,7 +81,16 @@ define(
|
|||||||
// Uses the mockLocation to get the current
|
// Uses the mockLocation to get the current
|
||||||
// "mock" website's view
|
// "mock" website's view
|
||||||
mockLocation.search.andReturn({ view: 'def' });
|
mockLocation.search.andReturn({ view: 'def' });
|
||||||
urlService.urlFor(mockMode, mockDomainObject);
|
|
||||||
|
urlService = new UrlService(mockLocation);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("get url for a location using domainObject and mode", function () {
|
||||||
|
urlService.urlForLocation(mockMode, mockDomainObject);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("get url for a new tab using domainObject and mode", function () {
|
||||||
|
urlService.urlForNewTab(mockMode, mockDomainObject);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -13,5 +13,6 @@
|
|||||||
"directives/MCTDrag",
|
"directives/MCTDrag",
|
||||||
"directives/MCTResize",
|
"directives/MCTResize",
|
||||||
"directives/MCTScroll",
|
"directives/MCTScroll",
|
||||||
|
"services/UrlService",
|
||||||
"StyleSheetLoader"
|
"StyleSheetLoader"
|
||||||
]
|
]
|
@ -183,7 +183,7 @@
|
|||||||
{
|
{
|
||||||
"key": "mutation",
|
"key": "mutation",
|
||||||
"implementation": "capabilities/MutationCapability.js",
|
"implementation": "capabilities/MutationCapability.js",
|
||||||
"depends": [ "now" ]
|
"depends": [ "topic", "now" ]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "delegation",
|
"key": "delegation",
|
||||||
@ -200,6 +200,10 @@
|
|||||||
"key": "throttle",
|
"key": "throttle",
|
||||||
"implementation": "services/Throttle.js",
|
"implementation": "services/Throttle.js",
|
||||||
"depends": [ "$timeout" ]
|
"depends": [ "$timeout" ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "topic",
|
||||||
|
"implementation": "services/Topic.js"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"roots": [
|
"roots": [
|
||||||
|
@ -29,6 +29,8 @@ define(
|
|||||||
function () {
|
function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var TOPIC_PREFIX = "mutation:";
|
||||||
|
|
||||||
// Utility function to overwrite a destination object
|
// Utility function to overwrite a destination object
|
||||||
// with the contents of a source object.
|
// with the contents of a source object.
|
||||||
function copyValues(destination, source) {
|
function copyValues(destination, source) {
|
||||||
@ -71,7 +73,8 @@ define(
|
|||||||
* which will expose this capability
|
* which will expose this capability
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function MutationCapability(now, domainObject) {
|
function MutationCapability(topic, now, domainObject) {
|
||||||
|
var t = topic(TOPIC_PREFIX + domainObject.getId());
|
||||||
|
|
||||||
function mutate(mutator, timestamp) {
|
function mutate(mutator, timestamp) {
|
||||||
// Get the object's model and clone it, so the
|
// Get the object's model and clone it, so the
|
||||||
@ -96,6 +99,7 @@ define(
|
|||||||
copyValues(model, result);
|
copyValues(model, result);
|
||||||
}
|
}
|
||||||
model.modified = useTimestamp ? timestamp : now();
|
model.modified = useTimestamp ? timestamp : now();
|
||||||
|
t.notify(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report the result of the mutation
|
// Report the result of the mutation
|
||||||
@ -107,6 +111,10 @@ define(
|
|||||||
return fastPromise(mutator(clone)).then(handleMutation);
|
return fastPromise(mutator(clone)).then(handleMutation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function listen(listener) {
|
||||||
|
return t.listen(listener);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
* Alias of `mutate`, used to support useCapability.
|
* Alias of `mutate`, used to support useCapability.
|
||||||
@ -139,7 +147,16 @@ define(
|
|||||||
* @returns {Promise.<boolean>} a promise for the result
|
* @returns {Promise.<boolean>} a promise for the result
|
||||||
* of the mutation; true if changes were made.
|
* of the mutation; true if changes were made.
|
||||||
*/
|
*/
|
||||||
mutate: mutate
|
mutate: mutate,
|
||||||
|
/**
|
||||||
|
* Listen for mutations of this domain object's model.
|
||||||
|
* The provided listener will be invoked with the domain
|
||||||
|
* object's new model after any changes. To stop listening,
|
||||||
|
* invoke the function returned by this method.
|
||||||
|
* @param {Function} listener function to call on mutation
|
||||||
|
* @returns {Function} a function to stop listening
|
||||||
|
*/
|
||||||
|
listen: listen
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,24 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
/*global define*/
|
/*global define*/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
|
87
platform/core/src/services/Topic.js
Normal file
87
platform/core/src/services/Topic.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
/*global define*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `topic` service provides a way to create both named,
|
||||||
|
* shared listeners and anonymous, private listeners.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* var t = topic('foo'); // Use/create a named topic
|
||||||
|
* t.listen(function () { ... });
|
||||||
|
* t.notify({ some: "message" });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Named topics are shared; multiple calls to `topic`
|
||||||
|
* with the same argument will return a single object instance.
|
||||||
|
* Anonymous topics (where `topic` has been called with no
|
||||||
|
* arguments) are private; each call returns a new instance.
|
||||||
|
*
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
function Topic() {
|
||||||
|
var topics = {};
|
||||||
|
|
||||||
|
function createTopic() {
|
||||||
|
var listeners = [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
listen: function (listener) {
|
||||||
|
listeners.push(listener);
|
||||||
|
return function unlisten() {
|
||||||
|
listeners = listeners.filter(function (l) {
|
||||||
|
return l !== listener;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
notify: function (message) {
|
||||||
|
listeners.forEach(function (listener) {
|
||||||
|
listener(message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use and (if necessary) create a new topic.
|
||||||
|
* @param {string} [key] name of the topic to use
|
||||||
|
*/
|
||||||
|
return function (key) {
|
||||||
|
if (arguments.length < 1) {
|
||||||
|
return createTopic();
|
||||||
|
} else {
|
||||||
|
topics[key] = topics[key] || createTopic();
|
||||||
|
return topics[key];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Topic;
|
||||||
|
}
|
||||||
|
);
|
@ -25,21 +25,33 @@
|
|||||||
* MutationCapabilitySpec. Created by vwoeltje on 11/6/14.
|
* MutationCapabilitySpec. Created by vwoeltje on 11/6/14.
|
||||||
*/
|
*/
|
||||||
define(
|
define(
|
||||||
["../../src/capabilities/MutationCapability"],
|
[
|
||||||
function (MutationCapability) {
|
"../../src/capabilities/MutationCapability",
|
||||||
|
"../../src/services/Topic"
|
||||||
|
],
|
||||||
|
function (MutationCapability, Topic) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
describe("The mutation capability", function () {
|
describe("The mutation capability", function () {
|
||||||
var testModel,
|
var testModel,
|
||||||
|
topic,
|
||||||
mockNow,
|
mockNow,
|
||||||
domainObject = { getModel: function () { return testModel; } },
|
domainObject = {
|
||||||
|
getId: function () { return "test-id"; },
|
||||||
|
getModel: function () { return testModel; }
|
||||||
|
},
|
||||||
mutation;
|
mutation;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
testModel = { number: 6 };
|
testModel = { number: 6 };
|
||||||
|
topic = new Topic();
|
||||||
mockNow = jasmine.createSpy('now');
|
mockNow = jasmine.createSpy('now');
|
||||||
mockNow.andReturn(12321);
|
mockNow.andReturn(12321);
|
||||||
mutation = new MutationCapability(mockNow, domainObject);
|
mutation = new MutationCapability(
|
||||||
|
topic,
|
||||||
|
mockNow,
|
||||||
|
domainObject
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows mutation of a model", function () {
|
it("allows mutation of a model", function () {
|
||||||
@ -83,6 +95,42 @@ define(
|
|||||||
// Should have gotten a timestamp from 'now'
|
// Should have gotten a timestamp from 'now'
|
||||||
expect(testModel.modified).toEqual(42);
|
expect(testModel.modified).toEqual(42);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("notifies listeners of mutation", function () {
|
||||||
|
var mockCallback = jasmine.createSpy('callback');
|
||||||
|
mutation.listen(mockCallback);
|
||||||
|
mutation.invoke(function (m) {
|
||||||
|
m.number = 8;
|
||||||
|
});
|
||||||
|
expect(mockCallback).toHaveBeenCalled();
|
||||||
|
expect(mockCallback.mostRecentCall.args[0].number)
|
||||||
|
.toEqual(8);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows listeners to stop listening", function () {
|
||||||
|
var mockCallback = jasmine.createSpy('callback');
|
||||||
|
mutation.listen(mockCallback)(); // Unlisten immediately
|
||||||
|
mutation.invoke(function (m) {
|
||||||
|
m.number = 8;
|
||||||
|
});
|
||||||
|
expect(mockCallback).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shares listeners across instances", function () {
|
||||||
|
var mockCallback = jasmine.createSpy('callback'),
|
||||||
|
otherMutation = new MutationCapability(
|
||||||
|
topic,
|
||||||
|
mockNow,
|
||||||
|
domainObject
|
||||||
|
);
|
||||||
|
mutation.listen(mockCallback);
|
||||||
|
otherMutation.invoke(function (m) {
|
||||||
|
m.number = 8;
|
||||||
|
});
|
||||||
|
expect(mockCallback).toHaveBeenCalled();
|
||||||
|
expect(mockCallback.mostRecentCall.args[0].number)
|
||||||
|
.toEqual(8);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
@ -1,3 +1,24 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
|
70
platform/core/test/services/TopicSpec.js
Normal file
70
platform/core/test/services/TopicSpec.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
["../../src/services/Topic"],
|
||||||
|
function (Topic) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("The 'topic' service", function () {
|
||||||
|
var topic,
|
||||||
|
testMessage,
|
||||||
|
mockCallback;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
testMessage = { someKey: "some value"};
|
||||||
|
mockCallback = jasmine.createSpy('callback');
|
||||||
|
topic = new Topic();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("notifies listeners on a topic", function () {
|
||||||
|
topic("abc").listen(mockCallback);
|
||||||
|
topic("abc").notify(testMessage);
|
||||||
|
expect(mockCallback).toHaveBeenCalledWith(testMessage);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not notify listeners across topics", function () {
|
||||||
|
topic("abc").listen(mockCallback);
|
||||||
|
topic("xyz").notify(testMessage);
|
||||||
|
expect(mockCallback).not.toHaveBeenCalledWith(testMessage);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not notify listeners after unlistening", function () {
|
||||||
|
topic("abc").listen(mockCallback)(); // Unlisten immediately
|
||||||
|
topic("abc").notify(testMessage);
|
||||||
|
expect(mockCallback).not.toHaveBeenCalledWith(testMessage);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("provides anonymous private topics", function () {
|
||||||
|
var t1 = topic(), t2 = topic();
|
||||||
|
|
||||||
|
t1.listen(mockCallback);
|
||||||
|
t2.notify(testMessage);
|
||||||
|
expect(mockCallback).not.toHaveBeenCalledWith(testMessage);
|
||||||
|
t1.notify(testMessage);
|
||||||
|
expect(mockCallback).toHaveBeenCalledWith(testMessage);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
"services/Now",
|
"services/Now",
|
||||||
"services/Throttle",
|
"services/Throttle",
|
||||||
|
"services/Topic",
|
||||||
|
|
||||||
"types/MergeModels",
|
"types/MergeModels",
|
||||||
"types/TypeCapability",
|
"types/TypeCapability",
|
||||||
|
@ -23,7 +23,21 @@
|
|||||||
{
|
{
|
||||||
"key": "PlotController",
|
"key": "PlotController",
|
||||||
"implementation": "PlotController.js",
|
"implementation": "PlotController.js",
|
||||||
"depends": [ "$scope", "telemetryFormatter", "telemetryHandler", "throttle" ]
|
"depends": [
|
||||||
|
"$scope",
|
||||||
|
"telemetryFormatter",
|
||||||
|
"telemetryHandler",
|
||||||
|
"throttle",
|
||||||
|
"PLOT_FIXED_DURATION"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"constants": [
|
||||||
|
{
|
||||||
|
"key": "PLOT_FIXED_DURATION",
|
||||||
|
"value": 900000,
|
||||||
|
"priority": "fallback",
|
||||||
|
"comment": "Fifteen minutes."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"policies": [
|
"policies": [
|
||||||
|
@ -82,8 +82,9 @@
|
|||||||
|
|
||||||
<mct-chart draw="subplot.getDrawingObject()"
|
<mct-chart draw="subplot.getDrawingObject()"
|
||||||
ng-mousemove="subplot.hover($event)"
|
ng-mousemove="subplot.hover($event)"
|
||||||
ng-mousedown="subplot.startMarquee($event)"
|
mct-drag="subplot.continueDrag($event)"
|
||||||
ng-mouseup="subplot.endMarquee($event); plot.update()">
|
mct-drag-down="subplot.startDrag($event)"
|
||||||
|
mct-drag-up="subplot.endDrag($event); plot.update()">
|
||||||
</mct-chart>
|
</mct-chart>
|
||||||
|
|
||||||
<!-- TODO: Move into correct position; make part of group; infer from set of actions -->
|
<!-- TODO: Move into correct position; make part of group; infer from set of actions -->
|
||||||
|
@ -51,7 +51,13 @@ define(
|
|||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function PlotController($scope, telemetryFormatter, telemetryHandler, throttle) {
|
function PlotController(
|
||||||
|
$scope,
|
||||||
|
telemetryFormatter,
|
||||||
|
telemetryHandler,
|
||||||
|
throttle,
|
||||||
|
PLOT_FIXED_DURATION
|
||||||
|
) {
|
||||||
var subPlotFactory = new SubPlotFactory(telemetryFormatter),
|
var subPlotFactory = new SubPlotFactory(telemetryFormatter),
|
||||||
modeOptions = new PlotModeOptions([], subPlotFactory),
|
modeOptions = new PlotModeOptions([], subPlotFactory),
|
||||||
subplots = [],
|
subplots = [],
|
||||||
@ -99,7 +105,8 @@ define(
|
|||||||
updater = new PlotUpdater(
|
updater = new PlotUpdater(
|
||||||
handle,
|
handle,
|
||||||
($scope.axes[0].active || {}).key,
|
($scope.axes[0].active || {}).key,
|
||||||
($scope.axes[1].active || {}).key
|
($scope.axes[1].active || {}).key,
|
||||||
|
PLOT_FIXED_DURATION
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +56,9 @@ define(
|
|||||||
domainOffset,
|
domainOffset,
|
||||||
mousePosition,
|
mousePosition,
|
||||||
marqueeStart,
|
marqueeStart,
|
||||||
|
panStart,
|
||||||
|
panStartBounds,
|
||||||
|
subPlotBounds,
|
||||||
hoverCoordinates,
|
hoverCoordinates,
|
||||||
isHovering = false;
|
isHovering = false;
|
||||||
|
|
||||||
@ -88,8 +91,7 @@ define(
|
|||||||
// pixel coordinates in the canvas area) from a mouse
|
// pixel coordinates in the canvas area) from a mouse
|
||||||
// event object.
|
// event object.
|
||||||
function toMousePosition($event) {
|
function toMousePosition($event) {
|
||||||
var target = $event.target,
|
var bounds = subPlotBounds;
|
||||||
bounds = target.getBoundingClientRect();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: $event.clientX - bounds.left,
|
x: $event.clientX - bounds.left,
|
||||||
@ -155,6 +157,25 @@ define(
|
|||||||
tickGenerator.generateRangeTicks(RANGE_TICKS);
|
tickGenerator.generateRangeTicks(RANGE_TICKS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updatePan() {
|
||||||
|
var start, current, delta, nextOrigin;
|
||||||
|
|
||||||
|
// Clear the previous panning pan-zoom state
|
||||||
|
panZoomStack.popPanZoom();
|
||||||
|
|
||||||
|
// Calculate what the new resulting pan-zoom should be
|
||||||
|
start = mousePositionToDomainRange(panStart);
|
||||||
|
current = mousePositionToDomainRange(mousePosition);
|
||||||
|
delta = [ current[0] - start[0], current[1] - start[1] ];
|
||||||
|
nextOrigin = [
|
||||||
|
panStartBounds.origin[0] - delta[0],
|
||||||
|
panStartBounds.origin[1] - delta[1]
|
||||||
|
];
|
||||||
|
|
||||||
|
// ...and push a new one at the current mouse position
|
||||||
|
panZoomStack.pushPanZoom(nextOrigin, panStartBounds.dimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Perform a marquee zoom.
|
// Perform a marquee zoom.
|
||||||
function marqueeZoom(start, end) {
|
function marqueeZoom(start, end) {
|
||||||
@ -241,31 +262,77 @@ define(
|
|||||||
*/
|
*/
|
||||||
hover: function ($event) {
|
hover: function ($event) {
|
||||||
isHovering = true;
|
isHovering = true;
|
||||||
|
subPlotBounds = $event.target.getBoundingClientRect();
|
||||||
mousePosition = toMousePosition($event);
|
mousePosition = toMousePosition($event);
|
||||||
updateHoverCoordinates();
|
updateHoverCoordinates();
|
||||||
if (marqueeStart) {
|
if (marqueeStart) {
|
||||||
updateMarqueeBox();
|
updateMarqueeBox();
|
||||||
}
|
}
|
||||||
|
if (panStart) {
|
||||||
|
updatePan();
|
||||||
|
updateDrawingBounds();
|
||||||
|
updateTicks();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Continue a previously-start pan or zoom gesture.
|
||||||
|
* @param $event the mouse event
|
||||||
|
*/
|
||||||
|
continueDrag: function ($event) {
|
||||||
|
mousePosition = toMousePosition($event);
|
||||||
|
if (marqueeStart) {
|
||||||
|
updateMarqueeBox();
|
||||||
|
}
|
||||||
|
if (panStart) {
|
||||||
|
updatePan();
|
||||||
|
updateDrawingBounds();
|
||||||
|
updateTicks();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Initiate a marquee zoom action.
|
* Initiate a marquee zoom action.
|
||||||
* @param $event the mouse event
|
* @param $event the mouse event
|
||||||
*/
|
*/
|
||||||
startMarquee: function ($event) {
|
startDrag: function ($event) {
|
||||||
mousePosition = marqueeStart = toMousePosition($event);
|
subPlotBounds = $event.target.getBoundingClientRect();
|
||||||
|
mousePosition = toMousePosition($event);
|
||||||
|
// Treat any modifier key as a pan
|
||||||
|
if ($event.altKey || $event.shiftKey || $event.ctrlKey) {
|
||||||
|
// Start panning
|
||||||
|
panStart = mousePosition;
|
||||||
|
panStartBounds = panZoomStack.getPanZoom();
|
||||||
|
// We're starting a pan, so add this back as a
|
||||||
|
// state on the stack; it will get replaced
|
||||||
|
// during the pan.
|
||||||
|
panZoomStack.pushPanZoom(
|
||||||
|
panStartBounds.origin,
|
||||||
|
panStartBounds.dimensions
|
||||||
|
);
|
||||||
|
$event.preventDefault();
|
||||||
|
} else {
|
||||||
|
// Start marquee zooming
|
||||||
|
marqueeStart = mousePosition;
|
||||||
updateMarqueeBox();
|
updateMarqueeBox();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Complete a marquee zoom action.
|
* Complete a marquee zoom action.
|
||||||
* @param $event the mouse event
|
* @param $event the mouse event
|
||||||
*/
|
*/
|
||||||
endMarquee: function ($event) {
|
endDrag: function ($event) {
|
||||||
mousePosition = toMousePosition($event);
|
mousePosition = toMousePosition($event);
|
||||||
|
subPlotBounds = undefined;
|
||||||
if (marqueeStart) {
|
if (marqueeStart) {
|
||||||
marqueeZoom(marqueeStart, mousePosition);
|
marqueeZoom(marqueeStart, mousePosition);
|
||||||
marqueeStart = undefined;
|
marqueeStart = undefined;
|
||||||
updateMarqueeBox();
|
updateMarqueeBox();
|
||||||
updateDrawingBounds();
|
updateDrawingBounds();
|
||||||
|
updateTicks();
|
||||||
|
}
|
||||||
|
if (panStart) {
|
||||||
|
// End panning
|
||||||
|
panStart = undefined;
|
||||||
|
panStartBounds = undefined;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
@ -43,9 +43,9 @@ define(
|
|||||||
var mid = Math.floor((min + max) / 2),
|
var mid = Math.floor((min + max) / 2),
|
||||||
found = buffer[mid * 2];
|
found = buffer[mid * 2];
|
||||||
|
|
||||||
// Collisions are not wanted
|
// On collisions, insert at same index
|
||||||
if (found === value) {
|
if (found === value) {
|
||||||
return -1;
|
return mid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, if we're down to a single index,
|
// Otherwise, if we're down to a single index,
|
||||||
|
@ -42,14 +42,17 @@ define(
|
|||||||
* @param {TelemetryHandle} handle the handle to telemetry access
|
* @param {TelemetryHandle} handle the handle to telemetry access
|
||||||
* @param {string} domain the key to use when looking up domain values
|
* @param {string} domain the key to use when looking up domain values
|
||||||
* @param {string} range the key to use when looking up range values
|
* @param {string} range the key to use when looking up range values
|
||||||
|
* @param {number} maxDuration maximum plot duration to display
|
||||||
|
* @param {number} maxPoints maximum number of points to display
|
||||||
*/
|
*/
|
||||||
function PlotUpdater(handle, domain, range, maxPoints) {
|
function PlotUpdater(handle, domain, range, fixedDuration, maxPoints) {
|
||||||
var ids = [],
|
var ids = [],
|
||||||
lines = {},
|
lines = {},
|
||||||
dimensions = [0, 0],
|
dimensions = [0, 0],
|
||||||
origin = [0, 0],
|
origin = [0, 0],
|
||||||
domainExtrema,
|
domainExtrema,
|
||||||
rangeExtrema,
|
rangeExtrema,
|
||||||
|
buffers = {},
|
||||||
bufferArray = [],
|
bufferArray = [],
|
||||||
domainOffset;
|
domainOffset;
|
||||||
|
|
||||||
@ -61,11 +64,10 @@ define(
|
|||||||
// Check if this set of ids matches the current set of ids
|
// Check if this set of ids matches the current set of ids
|
||||||
// (used to detect if line preparation can be skipped)
|
// (used to detect if line preparation can be skipped)
|
||||||
function idsMatch(nextIds) {
|
function idsMatch(nextIds) {
|
||||||
return nextIds.map(function (id, index) {
|
return ids.length === nextIds.length &&
|
||||||
|
nextIds.every(function (id, index) {
|
||||||
return ids[index] === id;
|
return ids[index] === id;
|
||||||
}).reduce(function (a, b) {
|
});
|
||||||
return a && b;
|
|
||||||
}, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare plot lines for this group of telemetry objects
|
// Prepare plot lines for this group of telemetry objects
|
||||||
@ -74,7 +76,7 @@ define(
|
|||||||
next = {};
|
next = {};
|
||||||
|
|
||||||
// Detect if we already have everything we need prepared
|
// Detect if we already have everything we need prepared
|
||||||
if (ids.length === nextIds.length && idsMatch(nextIds)) {
|
if (idsMatch(nextIds)) {
|
||||||
// Nothing to prepare, move on
|
// Nothing to prepare, move on
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -88,13 +90,13 @@ define(
|
|||||||
|
|
||||||
// Create buffers for these objects
|
// Create buffers for these objects
|
||||||
bufferArray = ids.map(function (id) {
|
bufferArray = ids.map(function (id) {
|
||||||
var buffer = new PlotLineBuffer(
|
buffers[id] = buffers[id] || new PlotLineBuffer(
|
||||||
domainOffset,
|
domainOffset,
|
||||||
INITIAL_SIZE,
|
INITIAL_SIZE,
|
||||||
maxPoints
|
maxPoints
|
||||||
);
|
);
|
||||||
next[id] = lines[id] || new PlotLine(buffer);
|
next[id] = lines[id] || new PlotLine(buffers[id]);
|
||||||
return buffer;
|
return buffers[id];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +109,7 @@ define(
|
|||||||
lines = next;
|
lines = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Initialize the domain offset, based on these observed values
|
// Initialize the domain offset, based on these observed values
|
||||||
function initializeDomainOffset(values) {
|
function initializeDomainOffset(values) {
|
||||||
domainOffset =
|
domainOffset =
|
||||||
@ -133,7 +136,7 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update dimensions and origin based on extrema of plots
|
// Update dimensions and origin based on extrema of plots
|
||||||
function updateExtrema() {
|
function updateBounds() {
|
||||||
if (bufferArray.length > 0) {
|
if (bufferArray.length > 0) {
|
||||||
domainExtrema = bufferArray.map(function (lineBuffer) {
|
domainExtrema = bufferArray.map(function (lineBuffer) {
|
||||||
return lineBuffer.getDomainExtrema();
|
return lineBuffer.getDomainExtrema();
|
||||||
@ -143,10 +146,40 @@ define(
|
|||||||
return lineBuffer.getRangeExtrema();
|
return lineBuffer.getRangeExtrema();
|
||||||
}).reduce(reduceExtrema);
|
}).reduce(reduceExtrema);
|
||||||
|
|
||||||
|
// Calculate best-fit dimensions
|
||||||
dimensions = (rangeExtrema[0] === rangeExtrema[1]) ?
|
dimensions = (rangeExtrema[0] === rangeExtrema[1]) ?
|
||||||
[dimensionsOf(domainExtrema), 2.0 ] :
|
[dimensionsOf(domainExtrema), 2.0 ] :
|
||||||
[dimensionsOf(domainExtrema), dimensionsOf(rangeExtrema)];
|
[dimensionsOf(domainExtrema), dimensionsOf(rangeExtrema)];
|
||||||
origin = [originOf(domainExtrema), originOf(rangeExtrema)];
|
origin = [originOf(domainExtrema), originOf(rangeExtrema)];
|
||||||
|
|
||||||
|
// ...then enforce a fixed duration if needed
|
||||||
|
if (fixedDuration !== undefined) {
|
||||||
|
origin[0] = origin[0] + dimensions[0] - fixedDuration;
|
||||||
|
dimensions[0] = fixedDuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce maximum duration on all plot lines; not that
|
||||||
|
// domain extrema must be up-to-date for this to behave correctly.
|
||||||
|
function enforceDuration() {
|
||||||
|
var cutoff;
|
||||||
|
|
||||||
|
function enforceDurationForBuffer(plotLineBuffer) {
|
||||||
|
var index = plotLineBuffer.findInsertionIndex(cutoff);
|
||||||
|
if (index > 0) {
|
||||||
|
// Leave one point untrimmed, such that line will
|
||||||
|
// continue off left edge of visible plot area.
|
||||||
|
plotLineBuffer.trim(index - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fixedDuration !== undefined &&
|
||||||
|
domainExtrema !== undefined &&
|
||||||
|
(domainExtrema[1] - domainExtrema[0] > fixedDuration)) {
|
||||||
|
cutoff = domainExtrema[1] - fixedDuration;
|
||||||
|
bufferArray.forEach(enforceDurationForBuffer);
|
||||||
|
updateBounds(); // Extrema may have changed now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,8 +213,8 @@ define(
|
|||||||
// Add new data
|
// Add new data
|
||||||
objects.forEach(addPointFor);
|
objects.forEach(addPointFor);
|
||||||
|
|
||||||
// Finally, update extrema
|
// Then, update extrema
|
||||||
updateExtrema();
|
updateBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add historical data for this domain object
|
// Add historical data for this domain object
|
||||||
@ -213,12 +246,12 @@ define(
|
|||||||
line.addSeries(series, domain, range);
|
line.addSeries(series, domain, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, update extrema
|
// Update extrema
|
||||||
updateExtrema();
|
updateBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use a default MAX_POINTS if none is provided
|
// Use a default MAX_POINTS if none is provided
|
||||||
maxPoints = maxPoints || MAX_POINTS;
|
maxPoints = maxPoints !== undefined ? maxPoints : MAX_POINTS;
|
||||||
|
|
||||||
// Initially prepare state for these objects.
|
// Initially prepare state for these objects.
|
||||||
// Note that this may be an empty array at this time,
|
// Note that this may be an empty array at this time,
|
||||||
|
@ -127,7 +127,7 @@ define(
|
|||||||
|
|
||||||
// Simulate a marquee zoom. Note that the mockElement
|
// Simulate a marquee zoom. Note that the mockElement
|
||||||
// is 100 by 100 and starts at 10,20
|
// is 100 by 100 and starts at 10,20
|
||||||
subplot.startMarquee({
|
subplot.startDrag({
|
||||||
target: mockElement,
|
target: mockElement,
|
||||||
clientX: 60,
|
clientX: 60,
|
||||||
clientY: 45
|
clientY: 45
|
||||||
@ -137,7 +137,7 @@ define(
|
|||||||
clientX: 75,
|
clientX: 75,
|
||||||
clientY: 85
|
clientY: 85
|
||||||
});
|
});
|
||||||
subplot.endMarquee({
|
subplot.endDrag({
|
||||||
target: mockElement,
|
target: mockElement,
|
||||||
clientX: 80,
|
clientX: 80,
|
||||||
clientY: 95
|
clientY: 95
|
||||||
@ -162,7 +162,7 @@ define(
|
|||||||
|
|
||||||
// Simulate a marquee zoom. Note that the mockElement
|
// Simulate a marquee zoom. Note that the mockElement
|
||||||
// is 100 by 100 and starts at 10,20
|
// is 100 by 100 and starts at 10,20
|
||||||
subplot.startMarquee({
|
subplot.startDrag({
|
||||||
target: mockElement,
|
target: mockElement,
|
||||||
clientX: 60,
|
clientX: 60,
|
||||||
clientY: 45
|
clientY: 45
|
||||||
@ -172,7 +172,7 @@ define(
|
|||||||
clientX: 75,
|
clientX: 75,
|
||||||
clientY: 85
|
clientY: 85
|
||||||
});
|
});
|
||||||
subplot.endMarquee({
|
subplot.endDrag({
|
||||||
target: mockElement,
|
target: mockElement,
|
||||||
clientX: 60,
|
clientX: 60,
|
||||||
clientY: 45
|
clientY: 45
|
||||||
|
@ -83,9 +83,6 @@ define(
|
|||||||
expect(buffer.findInsertionIndex(10)).toEqual(4);
|
expect(buffer.findInsertionIndex(10)).toEqual(4);
|
||||||
expect(buffer.findInsertionIndex(14.5)).toEqual(5);
|
expect(buffer.findInsertionIndex(14.5)).toEqual(5);
|
||||||
expect(buffer.findInsertionIndex(20)).toEqual(6);
|
expect(buffer.findInsertionIndex(20)).toEqual(6);
|
||||||
|
|
||||||
// 9 is already in there, disallow insertion
|
|
||||||
expect(buffer.findInsertionIndex(9)).toEqual(-1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows insertion in the middle", function () {
|
it("allows insertion in the middle", function () {
|
||||||
|
@ -4,12 +4,12 @@
|
|||||||
{
|
{
|
||||||
"key": "mctInclude",
|
"key": "mctInclude",
|
||||||
"implementation": "MCTInclude.js",
|
"implementation": "MCTInclude.js",
|
||||||
"depends": [ "templates[]" ]
|
"depends": [ "templates[]", "$sce" ]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "mctRepresentation",
|
"key": "mctRepresentation",
|
||||||
"implementation": "MCTRepresentation.js",
|
"implementation": "MCTRepresentation.js",
|
||||||
"depends": [ "representations[]", "views[]", "representers[]", "$q", "$log" ]
|
"depends": [ "representations[]", "views[]", "representers[]", "$q", "$sce", "$log" ]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"gestures": [
|
"gestures": [
|
||||||
|
@ -53,17 +53,17 @@ define(
|
|||||||
* @param {TemplateDefinition[]} templates an array of
|
* @param {TemplateDefinition[]} templates an array of
|
||||||
* template extensions
|
* template extensions
|
||||||
*/
|
*/
|
||||||
function MCTInclude(templates) {
|
function MCTInclude(templates, $sce) {
|
||||||
var templateMap = {};
|
var templateMap = {};
|
||||||
|
|
||||||
// Prepopulate templateMap for easy look up by key
|
// Prepopulate templateMap for easy look up by key
|
||||||
templates.forEach(function (template) {
|
templates.forEach(function (template) {
|
||||||
var key = template.key,
|
var key = template.key,
|
||||||
path = [
|
path = $sce.trustAsResourceUrl([
|
||||||
template.bundle.path,
|
template.bundle.path,
|
||||||
template.bundle.resources,
|
template.bundle.resources,
|
||||||
template.templateUrl
|
template.templateUrl
|
||||||
].join("/");
|
].join("/"));
|
||||||
// First found should win (priority ordering)
|
// First found should win (priority ordering)
|
||||||
templateMap[key] = templateMap[key] || path;
|
templateMap[key] = templateMap[key] || path;
|
||||||
});
|
});
|
||||||
|
@ -52,7 +52,7 @@ define(
|
|||||||
* representation extensions
|
* representation extensions
|
||||||
* @param {ViewDefinition[]} views an array of view extensions
|
* @param {ViewDefinition[]} views an array of view extensions
|
||||||
*/
|
*/
|
||||||
function MCTRepresentation(representations, views, representers, $q, $log) {
|
function MCTRepresentation(representations, views, representers, $q, $sce, $log) {
|
||||||
var representationMap = {},
|
var representationMap = {},
|
||||||
gestureMap = {};
|
gestureMap = {};
|
||||||
|
|
||||||
@ -69,11 +69,11 @@ define(
|
|||||||
|
|
||||||
// Get a path to a representation
|
// Get a path to a representation
|
||||||
function getPath(representation) {
|
function getPath(representation) {
|
||||||
return [
|
return $sce.trustAsResourceUrl([
|
||||||
representation.bundle.path,
|
representation.bundle.path,
|
||||||
representation.bundle.resources,
|
representation.bundle.resources,
|
||||||
representation.templateUrl
|
representation.templateUrl
|
||||||
].join("/");
|
].join("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look up a matching representation for this domain object
|
// Look up a matching representation for this domain object
|
||||||
|
@ -31,6 +31,7 @@ define(
|
|||||||
|
|
||||||
describe("The mct-include directive", function () {
|
describe("The mct-include directive", function () {
|
||||||
var testTemplates,
|
var testTemplates,
|
||||||
|
mockSce,
|
||||||
mctInclude;
|
mctInclude;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
@ -46,7 +47,14 @@ define(
|
|||||||
templateUrl: "z/template.html"
|
templateUrl: "z/template.html"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
mctInclude = new MCTInclude(testTemplates);
|
mockSce = jasmine.createSpyObj(
|
||||||
|
'$sce',
|
||||||
|
['trustAsResourceUrl']
|
||||||
|
);
|
||||||
|
mockSce.trustAsResourceUrl.andCallFake(function (url) {
|
||||||
|
return url;
|
||||||
|
});
|
||||||
|
mctInclude = new MCTInclude(testTemplates, mockSce);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("has a built-in template, with ng-include src=inclusion", function () {
|
it("has a built-in template, with ng-include src=inclusion", function () {
|
||||||
@ -69,6 +77,12 @@ define(
|
|||||||
expect(scope.inclusion).toEqual("x/y/z/template.html");
|
expect(scope.inclusion).toEqual("x/y/z/template.html");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("trusts template URLs", function () {
|
||||||
|
mctInclude.controller({ key: "xyz" });
|
||||||
|
expect(mockSce.trustAsResourceUrl)
|
||||||
|
.toHaveBeenCalledWith("x/y/z/template.html");
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
@ -38,6 +38,7 @@ define(
|
|||||||
testViews,
|
testViews,
|
||||||
mockRepresenters,
|
mockRepresenters,
|
||||||
mockQ,
|
mockQ,
|
||||||
|
mockSce,
|
||||||
mockLog,
|
mockLog,
|
||||||
mockScope,
|
mockScope,
|
||||||
mockElement,
|
mockElement,
|
||||||
@ -95,8 +96,16 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
mockQ = { when: mockPromise };
|
mockQ = { when: mockPromise };
|
||||||
|
mockSce = jasmine.createSpyObj(
|
||||||
|
'$sce',
|
||||||
|
['trustAsResourceUrl']
|
||||||
|
);
|
||||||
mockLog = jasmine.createSpyObj("$log", LOG_FUNCTIONS);
|
mockLog = jasmine.createSpyObj("$log", LOG_FUNCTIONS);
|
||||||
|
|
||||||
|
|
||||||
|
mockSce.trustAsResourceUrl.andCallFake(function (url) {
|
||||||
|
return url;
|
||||||
|
});
|
||||||
mockScope = jasmine.createSpyObj("scope", [ "$watch" ]);
|
mockScope = jasmine.createSpyObj("scope", [ "$watch" ]);
|
||||||
mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
|
mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
|
||||||
mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
|
mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
|
||||||
@ -108,6 +117,7 @@ define(
|
|||||||
testViews,
|
testViews,
|
||||||
mockRepresenters,
|
mockRepresenters,
|
||||||
mockQ,
|
mockQ,
|
||||||
|
mockSce,
|
||||||
mockLog
|
mockLog
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -125,9 +135,18 @@ define(
|
|||||||
|
|
||||||
it("watches scope when linked", function () {
|
it("watches scope when linked", function () {
|
||||||
mctRepresentation.link(mockScope, mockElement);
|
mctRepresentation.link(mockScope, mockElement);
|
||||||
expect(mockScope.$watch).toHaveBeenCalledWith("key", jasmine.any(Function));
|
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||||
expect(mockScope.$watch).toHaveBeenCalledWith("domainObject", jasmine.any(Function));
|
"key",
|
||||||
expect(mockScope.$watch).toHaveBeenCalledWith("domainObject.getModel().modified", jasmine.any(Function));
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||||
|
"domainObject",
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||||
|
"domainObject.getModel().modified",
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("recognizes keys for representations", function () {
|
it("recognizes keys for representations", function () {
|
||||||
@ -152,6 +171,18 @@ define(
|
|||||||
expect(mockScope.inclusion).toEqual("x/y/z/template.html");
|
expect(mockScope.inclusion).toEqual("x/y/z/template.html");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("trusts template URLs", function () {
|
||||||
|
mctRepresentation.link(mockScope, mockElement);
|
||||||
|
|
||||||
|
mockScope.key = "xyz";
|
||||||
|
|
||||||
|
// Trigger the watch
|
||||||
|
mockScope.$watch.calls[0].args[1]();
|
||||||
|
|
||||||
|
expect(mockSce.trustAsResourceUrl)
|
||||||
|
.toHaveBeenCalledWith("x/y/z/template.html");
|
||||||
|
});
|
||||||
|
|
||||||
it("loads declared capabilities", function () {
|
it("loads declared capabilities", function () {
|
||||||
mctRepresentation.link(mockScope, mockElement);
|
mctRepresentation.link(mockScope, mockElement);
|
||||||
|
|
||||||
|
@ -59,6 +59,7 @@ define(
|
|||||||
telemetryObjects = [],
|
telemetryObjects = [],
|
||||||
pool = lossless ? new TelemetryQueue() : new TelemetryTable(),
|
pool = lossless ? new TelemetryQueue() : new TelemetryTable(),
|
||||||
metadatas,
|
metadatas,
|
||||||
|
unlistenToMutation,
|
||||||
updatePending;
|
updatePending;
|
||||||
|
|
||||||
// Look up domain objects which have telemetry capabilities.
|
// Look up domain objects which have telemetry capabilities.
|
||||||
@ -146,13 +147,23 @@ define(
|
|||||||
telemetryObjects = objects;
|
telemetryObjects = objects;
|
||||||
metadatas = objects.map(lookupMetadata);
|
metadatas = objects.map(lookupMetadata);
|
||||||
// Fire callback, as this will be the first time that
|
// Fire callback, as this will be the first time that
|
||||||
// telemetry objects are available
|
// telemetry objects are available, or these objects
|
||||||
|
// will have changed.
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
return objects;
|
return objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function unsubscribeAll() {
|
||||||
|
return unsubscribePromise.then(function (unsubscribes) {
|
||||||
|
return $q.all(unsubscribes.map(function (unsubscribe) {
|
||||||
|
return unsubscribe();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize() {
|
||||||
// Get a reference to relevant objects (those with telemetry
|
// Get a reference to relevant objects (those with telemetry
|
||||||
// capabilities) and subscribe to their telemetry updates.
|
// capabilities) and subscribe to their telemetry updates.
|
||||||
// Keep a reference to their promised return values, as these
|
// Keep a reference to their promised return values, as these
|
||||||
@ -163,6 +174,32 @@ define(
|
|||||||
unsubscribePromise = telemetryObjectPromise
|
unsubscribePromise = telemetryObjectPromise
|
||||||
.then(cacheObjectReferences)
|
.then(cacheObjectReferences)
|
||||||
.then(subscribeAll);
|
.then(subscribeAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
function idsMatch(ids) {
|
||||||
|
return ids.length === telemetryObjects.length &&
|
||||||
|
ids.every(function (id, index) {
|
||||||
|
return telemetryObjects[index].getId() === id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function modelChange(model) {
|
||||||
|
if (!idsMatch((model || {}).composition || [])) {
|
||||||
|
// Reinitialize if composition has changed
|
||||||
|
unsubscribeAll().then(initialize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addMutationListener() {
|
||||||
|
var mutation = domainObject &&
|
||||||
|
domainObject.getCapability('mutation');
|
||||||
|
if (mutation) {
|
||||||
|
return mutation.listen(modelChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
unlistenToMutation = addMutationListener();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
@ -172,11 +209,10 @@ define(
|
|||||||
* @memberof TelemetrySubscription
|
* @memberof TelemetrySubscription
|
||||||
*/
|
*/
|
||||||
unsubscribe: function () {
|
unsubscribe: function () {
|
||||||
return unsubscribePromise.then(function (unsubscribes) {
|
if (unlistenToMutation) {
|
||||||
return $q.all(unsubscribes.map(function (unsubscribe) {
|
unlistenToMutation();
|
||||||
return unsubscribe();
|
}
|
||||||
}));
|
return unsubscribeAll();
|
||||||
});
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Get the most recent domain value that has been observed
|
* Get the most recent domain value that has been observed
|
||||||
|
@ -32,7 +32,9 @@ define(
|
|||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockCallback,
|
mockCallback,
|
||||||
mockTelemetry,
|
mockTelemetry,
|
||||||
|
mockMutation,
|
||||||
mockUnsubscribe,
|
mockUnsubscribe,
|
||||||
|
mockUnlisten,
|
||||||
mockSeries,
|
mockSeries,
|
||||||
testMetadata,
|
testMetadata,
|
||||||
subscription;
|
subscription;
|
||||||
@ -59,7 +61,12 @@ define(
|
|||||||
"telemetry",
|
"telemetry",
|
||||||
["subscribe", "getMetadata"]
|
["subscribe", "getMetadata"]
|
||||||
);
|
);
|
||||||
|
mockMutation = jasmine.createSpyObj(
|
||||||
|
"mutation",
|
||||||
|
["mutate", "listen"]
|
||||||
|
);
|
||||||
mockUnsubscribe = jasmine.createSpy("unsubscribe");
|
mockUnsubscribe = jasmine.createSpy("unsubscribe");
|
||||||
|
mockUnlisten = jasmine.createSpy("unlisten");
|
||||||
mockSeries = jasmine.createSpyObj(
|
mockSeries = jasmine.createSpyObj(
|
||||||
"series",
|
"series",
|
||||||
[ "getPointCount", "getDomainValue", "getRangeValue" ]
|
[ "getPointCount", "getDomainValue", "getRangeValue" ]
|
||||||
@ -68,12 +75,19 @@ define(
|
|||||||
mockQ.when.andCallFake(mockPromise);
|
mockQ.when.andCallFake(mockPromise);
|
||||||
|
|
||||||
mockDomainObject.hasCapability.andReturn(true);
|
mockDomainObject.hasCapability.andReturn(true);
|
||||||
mockDomainObject.getCapability.andReturn(mockTelemetry);
|
mockDomainObject.getCapability.andCallFake(function (c) {
|
||||||
|
return {
|
||||||
|
telemetry: mockTelemetry,
|
||||||
|
mutation: mockMutation
|
||||||
|
}[c];
|
||||||
|
});
|
||||||
mockDomainObject.getId.andReturn('test-id');
|
mockDomainObject.getId.andReturn('test-id');
|
||||||
|
|
||||||
mockTelemetry.subscribe.andReturn(mockUnsubscribe);
|
mockTelemetry.subscribe.andReturn(mockUnsubscribe);
|
||||||
mockTelemetry.getMetadata.andReturn(testMetadata);
|
mockTelemetry.getMetadata.andReturn(testMetadata);
|
||||||
|
|
||||||
|
mockMutation.listen.andReturn(mockUnlisten);
|
||||||
|
|
||||||
mockSeries.getPointCount.andReturn(42);
|
mockSeries.getPointCount.andReturn(42);
|
||||||
mockSeries.getDomainValue.andReturn(123456);
|
mockSeries.getDomainValue.andReturn(123456);
|
||||||
mockSeries.getRangeValue.andReturn(789);
|
mockSeries.getRangeValue.andReturn(789);
|
||||||
@ -213,6 +227,22 @@ define(
|
|||||||
expect(mockCallback2)
|
expect(mockCallback2)
|
||||||
.toHaveBeenCalledWith([ mockDomainObject ]);
|
.toHaveBeenCalledWith([ mockDomainObject ]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("reinitializes on mutation", function () {
|
||||||
|
expect(mockTelemetry.subscribe.calls.length).toEqual(1);
|
||||||
|
// Notify of a mutation which appears to change composition
|
||||||
|
mockMutation.listen.mostRecentCall.args[0]({
|
||||||
|
composition: ['Z']
|
||||||
|
});
|
||||||
|
// Use subscribe call as an indication of reinitialization
|
||||||
|
expect(mockTelemetry.subscribe.calls.length).toEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("stops listening for mutation on unsubscribe", function () {
|
||||||
|
expect(mockUnlisten).not.toHaveBeenCalled();
|
||||||
|
subscription.unsubscribe();
|
||||||
|
expect(mockUnlisten).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
Loading…
Reference in New Issue
Block a user