Merged from Master

This commit is contained in:
Henry
2015-12-07 20:42:09 -08:00
53 changed files with 1798 additions and 481 deletions

View File

@@ -105,6 +105,12 @@
"implementation": "navigation/NavigationService.js"
}
],
"policies": [
{
"implementation": "creation/CreationPolicy.js",
"category": "creation"
}
],
"actions": [
{
"key": "navigate",

View File

@@ -68,7 +68,7 @@ define(
// Introduce one create action per type
return this.typeService.listTypes().filter(function (type) {
return type.hasFeature("creation");
return self.policyService.allow("creation", type);
}).map(function (type) {
return new CreateAction(
type,

View File

@@ -0,0 +1,45 @@
/*****************************************************************************
* 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";
/**
* A policy for determining whether objects of a given type can be
* created.
* @constructor
* @implements {Policy}
* @memberof platform/commonUI/browse
*/
function CreationPolicy() {
}
CreationPolicy.prototype.allow = function (type) {
return type.hasFeature("creation");
};
return CreationPolicy;
}
);

View File

@@ -33,6 +33,9 @@ define(
var mockTypeService,
mockDialogService,
mockCreationService,
mockPolicyService,
mockCreationPolicy,
mockPolicyMap = {},
mockTypes,
provider;
@@ -67,14 +70,32 @@ define(
"creationService",
[ "createObject" ]
);
mockPolicyService = jasmine.createSpyObj(
"policyService",
[ "allow" ]
);
mockTypes = [ "A", "B", "C" ].map(createMockType);
mockTypes.forEach(function(type){
mockPolicyMap[type.getName()] = true;
});
mockCreationPolicy = function(type){
return mockPolicyMap[type.getName()];
};
mockPolicyService.allow.andCallFake(function(category, type){
return category === "creation" && mockCreationPolicy(type) ? true : false;
});
mockTypeService.listTypes.andReturn(mockTypes);
provider = new CreateActionProvider(
mockTypeService,
mockDialogService,
mockCreationService
mockCreationService,
mockPolicyService
);
});
@@ -94,15 +115,15 @@ define(
it("does not expose non-creatable types", function () {
// One of the types won't have the creation feature...
mockTypes[1].hasFeature.andReturn(false);
mockPolicyMap[mockTypes[0].getName()] = false;
// ...so it should have been filtered out.
expect(provider.getActions({
key: "create",
domainObject: {}
}).length).toEqual(2);
// Make sure it was creation which was used to check
expect(mockTypes[1].hasFeature)
.toHaveBeenCalledWith("creation");
expect(mockPolicyService.allow)
.toHaveBeenCalledWith("creation", mockTypes[0]);
});
});
}

View File

@@ -0,0 +1,53 @@
/*****************************************************************************
* 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,describe,it,expect,beforeEach,jasmine*/
define(
["../../src/creation/CreationPolicy"],
function (CreationPolicy) {
"use strict";
describe("The creation policy", function () {
var mockType,
policy;
beforeEach(function () {
mockType = jasmine.createSpyObj(
'type',
['hasFeature']
);
policy = new CreationPolicy();
});
it("allows creation of types with the creation feature", function () {
mockType.hasFeature.andReturn(true);
expect(policy.allow(mockType)).toBeTruthy();
});
it("disallows creation of types without the creation feature", function () {
mockType.hasFeature.andReturn(false);
expect(policy.allow(mockType)).toBeFalsy();
});
});
}
);

View File

@@ -8,6 +8,7 @@
"creation/CreateMenuController",
"creation/CreateWizard",
"creation/CreationService",
"creation/CreationPolicy",
"creation/LocatorController",
"navigation/NavigateAction",
"navigation/NavigationService",

View File

@@ -50,7 +50,7 @@ define(
// Simply trigger refresh of in-view objects; do not
// write anything to database.
persistence.persist = function () {
cache.markDirty(editableObject);
return cache.markDirty(editableObject);
};
// Delegate refresh to the original object; this avoids refreshing

View File

@@ -115,6 +115,7 @@ define(
*/
EditableDomainObjectCache.prototype.markDirty = function (domainObject) {
this.dirtyObjects[domainObject.getId()] = domainObject;
return this.$q.when(true);
};
/**

View File

@@ -31,6 +31,7 @@ define(
mockEditableObject,
mockDomainObject,
mockCache,
mockPromise,
capability;
beforeEach(function () {
@@ -50,7 +51,9 @@ define(
"cache",
[ "markDirty" ]
);
mockPromise = jasmine.createSpyObj("promise", ["then"]);
mockCache.markDirty.andReturn(mockPromise);
mockDomainObject.getCapability.andReturn(mockPersistence);
capability = new EditablePersistenceCapability(
@@ -84,6 +87,10 @@ define(
expect(mockPersistence.refresh).toHaveBeenCalled();
});
it("returns a promise from persist", function () {
expect(capability.persist().then).toEqual(jasmine.any(Function));
});
});
}
);

View File

@@ -19,6 +19,10 @@
{
"implementation": "StyleSheetLoader.js",
"depends": [ "stylesheets[]", "$document", "THEME" ]
},
{
"implementation": "UnsupportedBrowserWarning.js",
"depends": [ "notificationService", "agentService" ]
}
],
"stylesheets": [

View File

@@ -36,29 +36,29 @@ $mobileTreeRightArrowW: 30px;
/************************** DEVICE WIDTHS */
// IMPORTANT! Usage assumes that ranges are mutually exclusive and have no gaps
$phoMaxW: 514px;
$tabMinW: 515px;
$tabMaxW: 1280px;
$desktopMinW: 1281px;
$phoMaxW: 767px;
$tabMinW: 768px;
$tabMaxW: 1024px;
$desktopMinW: 1025px;
/************************** MEDIA QUERIES: WINDOW CHECKS FOR SPECIFIC ORIENTATIONS FOR EACH DEVICE */
$screenPortrait: "screen and (orientation: portrait)";
$screenLandscape: "screen and (orientation: landscape)";
$screenPortrait: "(orientation: portrait)";
$screenLandscape: "(orientation: landscape)";
$mobileDevice: "(max-device-width: #{$tabMaxW})";
//$mobileDevice: "(max-device-width: #{$tabMaxW})";
$phoneCheck: "(max-device-width: #{$phoMaxW})";
$tabletCheck: $mobileDevice;
$desktopCheck: "(min-device-width: #{$desktopMinW})";
$tabletCheck: "(min-device-width: #{$tabMinW}) and (max-device-width: #{$tabMaxW})";
$desktopCheck: "(min-device-width: #{$desktopMinW}) and (-webkit-min-device-pixel-ratio: 1)";
/************************** MEDIA QUERIES: WINDOWS FOR SPECIFIC ORIENTATIONS FOR EACH DEVICE */
$phonePortrait: "#{$screenPortrait} and #{$phoneCheck} and #{$mobileDevice}";
$phoneLandscape: "#{$screenLandscape} and #{$phoneCheck} and #{$mobileDevice}";
$phonePortrait: "only screen and #{$screenPortrait} and #{$phoneCheck}";
$phoneLandscape: "only screen and #{$screenLandscape} and #{$phoneCheck}";
$tabletPortrait: "#{$screenPortrait} and #{$tabletCheck} and #{$mobileDevice}";
$tabletLandscape: "#{$screenLandscape} and #{$tabletCheck} and #{$mobileDevice}";
$tabletPortrait: "only screen and #{$screenPortrait} and #{$tabletCheck}";
$tabletLandscape: "only screen and #{$screenLandscape} and #{$tabletCheck}";
$desktop: "screen and #{$desktopCheck}";
$desktop: "only screen and #{$desktopCheck}";
/************************** DEVICE PARAMETERS FOR MENUS/REPRESENTATIONS */
$proporMenuOnly: 90%;

View File

@@ -1,7 +1,29 @@
<!--
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.
-->
<span class="s-btn"
ng-controller="DateTimeFieldController">
<input type="text"
ng-model="textValue"
ng-blur="restoreTextValue(); ngBlur()"
ng-class="{ error: textInvalid }">
</input>
<a class="ui-symbol icon icon-calendar"
@@ -11,8 +33,8 @@
<mct-popup ng-if="picker.active">
<div mct-click-elsewhere="picker.active = false">
<mct-control key="'datetime-picker'"
ng-model="ngModel"
field="field"
ng-model="pickerModel"
field="'value'"
options="{ hours: true }">
</mct-control>
</div>

View File

@@ -20,12 +20,14 @@
at runtime from the About dialog for additional information.
-->
<div ng-controller="TimeRangeController">
<div class="l-time-range-inputs-holder">
<form class="l-time-range-inputs-holder"
ng-submit="updateBoundsFromForm()">
<span class="l-time-range-inputs-elem ui-symbol type-icon">&#x43;</span>
<span class="l-time-range-input">
<mct-control key="'datetime-field'"
structure="{ format: parameters.format }"
ng-model="ngModel.outer"
ng-model="formModel"
ng-blur="updateBoundsFromForm()"
field="'start'"
class="time-range-start">
</mct-control>
@@ -36,12 +38,15 @@
<span class="l-time-range-input" ng-controller="ToggleController as t2">
<mct-control key="'datetime-field'"
structure="{ format: parameters.format }"
ng-model="ngModel.outer"
ng-model="formModel"
ng-blur="updateBoundsFromForm()"
field="'end'"
class="time-range-end">
</mct-control>&nbsp;
</span>
</div>
<input type="submit" class="hidden">
</form>
<div class="l-time-range-slider-holder">
<div class="l-time-range-slider">

View File

@@ -0,0 +1,64 @@
/*****************************************************************************
* 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*/
/**
* This bundle provides various general-purpose UI elements, including
* platform styling.
* @namespace platform/commonUI/general
*/
define(
[],
function () {
"use strict";
var WARNING_TITLE = "Unsupported browser",
WARNING_DESCRIPTION = [
"This software has been developed and tested",
"using the latest Google Chrome,",
"and may be unstable in other browsers."
].join(" "),
MOBILE_BROWSER = "Safari",
DESKTOP_BROWSER = "Chrome";
/**
* Shows a warning if a user's browser is unsupported.
* @memberof platform/commonUI/general
* @constructor
* @param {NotificationService} notificationService the notification
* service
*/
function UnsupportedBrowserWarning(notificationService, agentService) {
var testToBrowser = agentService.isMobile() ?
MOBILE_BROWSER : DESKTOP_BROWSER;
if (!agentService.isBrowser(testToBrowser)) {
notificationService.alert({
title: WARNING_TITLE,
actionText: WARNING_DESCRIPTION
});
}
}
return UnsupportedBrowserWarning;
}
);

View File

@@ -53,7 +53,9 @@ define(
formatter.parse($scope.textValue) !== value) {
$scope.textValue = formatter.format(value);
$scope.textInvalid = false;
$scope.lastValidValue = $scope.textValue;
}
$scope.pickerModel = { value: value };
}
function updateFromView(textValue) {
@@ -61,6 +63,17 @@ define(
if (!$scope.textInvalid) {
$scope.ngModel[$scope.field] =
formatter.parse(textValue);
$scope.lastValidValue = $scope.textValue;
}
}
function updateFromPicker(value) {
if (value !== $scope.ngModel[$scope.field]) {
$scope.ngModel[$scope.field] = value;
updateFromModel(value);
if ($scope.ngBlur) {
$scope.ngBlur();
}
}
}
@@ -69,10 +82,18 @@ define(
updateFromModel($scope.ngModel[$scope.field]);
}
function restoreTextValue() {
$scope.textValue = $scope.lastValidValue;
updateFromView($scope.textValue);
}
$scope.restoreTextValue = restoreTextValue;
$scope.picker = { active: false };
$scope.$watch('structure.format', setFormat);
$scope.$watch('ngModel[field]', updateFromModel);
$scope.$watch('pickerModel.value', updateFromPicker);
$scope.$watch('textValue', updateFromView);
}

View File

@@ -175,6 +175,13 @@ define(
updateViewFromModel($scope.ngModel);
}
function updateFormModel() {
$scope.formModel = {
start: (($scope.ngModel || {}).outer || {}).start,
end: (($scope.ngModel || {}).outer || {}).end
};
}
function updateOuterStart(t) {
var ngModel = $scope.ngModel;
@@ -192,6 +199,7 @@ define(
ngModel.inner.end
);
updateFormModel();
updateViewForInnerSpanFromModel(ngModel);
updateTicks();
}
@@ -213,6 +221,7 @@ define(
ngModel.inner.start
);
updateFormModel();
updateViewForInnerSpanFromModel(ngModel);
updateTicks();
}
@@ -223,6 +232,14 @@ define(
updateTicks();
}
function updateBoundsFromForm() {
$scope.ngModel = $scope.ngModel || {};
$scope.ngModel.outer = {
start: $scope.formModel.start,
end: $scope.formModel.end
};
}
$scope.startLeftDrag = startLeftDrag;
$scope.startRightDrag = startRightDrag;
$scope.startMiddleDrag = startMiddleDrag;
@@ -230,10 +247,13 @@ define(
$scope.rightDrag = rightDrag;
$scope.middleDrag = middleDrag;
$scope.updateBoundsFromForm = updateBoundsFromForm;
$scope.ticks = [];
// Initialize scope to defaults
updateViewFromModel($scope.ngModel);
updateFormModel();
$scope.$watchCollection("ngModel", updateViewFromModel);
$scope.$watch("spanWidth", updateSpanWidth);

View File

@@ -204,7 +204,7 @@ define(
// And poll for position changes enforced by styles
activeInterval = $interval(function () {
getSetPosition(getSetPosition());
}, POLLING_INTERVAL, false);
}, POLLING_INTERVAL, 0, false);
// ...and stop polling when we're destroyed.
$scope.$on('$destroy', function () {

View File

@@ -0,0 +1,98 @@
/*****************************************************************************
* 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/UnsupportedBrowserWarning"],
function (UnsupportedBrowserWarning) {
"use strict";
var MOBILE_BROWSER = "Safari",
DESKTOP_BROWSER = "Chrome",
UNSUPPORTED_BROWSERS = [
"Firefox",
"IE",
"Opera",
"Iceweasel"
];
describe("The unsupported browser warning", function () {
var mockNotificationService,
mockAgentService,
testAgent;
function instantiateWith(browser) {
testAgent = "Mozilla/5.0 " + browser + "/12.34.56";
return new UnsupportedBrowserWarning(
mockNotificationService,
mockAgentService
);
}
beforeEach(function () {
testAgent = "chrome";
mockNotificationService = jasmine.createSpyObj(
"notificationService",
[ "alert" ]
);
mockAgentService = jasmine.createSpyObj(
"agentService",
[ "isMobile", "isBrowser" ]
);
mockAgentService.isBrowser.andCallFake(function (substr) {
substr = substr.toLowerCase();
return testAgent.toLowerCase().indexOf(substr) !== -1;
});
});
[ false, true ].forEach(function (isMobile) {
var deviceType = isMobile ? "mobile" : "desktop",
goodBrowser = isMobile ? MOBILE_BROWSER : DESKTOP_BROWSER,
badBrowsers = UNSUPPORTED_BROWSERS.concat([
isMobile ? DESKTOP_BROWSER : MOBILE_BROWSER
]);
describe("on " + deviceType + " devices", function () {
beforeEach(function () {
mockAgentService.isMobile.andReturn(isMobile);
});
it("is not shown for " + goodBrowser, function () {
instantiateWith(goodBrowser);
expect(mockNotificationService.alert)
.not.toHaveBeenCalled();
});
badBrowsers.forEach(function (badBrowser) {
it("is shown for " + badBrowser, function () {
instantiateWith(badBrowser);
expect(mockNotificationService.alert)
.toHaveBeenCalled();
});
});
});
});
});
}
);

View File

@@ -67,21 +67,13 @@ define(
mockScope.ngModel = { testField: 12321 };
mockScope.field = "testField";
mockScope.structure = { format: "someFormat" };
mockScope.ngBlur = jasmine.createSpy('blur');
controller = new DateTimeFieldController(
mockScope,
mockFormatService
);
});
it("updates models from user-entered text", function () {
var newText = "1977-05-25 17:30:00";
mockScope.textValue = newText;
fireWatch("textValue", newText);
expect(mockScope.ngModel.testField)
.toEqual(mockFormat.parse(newText));
expect(mockScope.textInvalid).toBeFalsy();
fireWatch("ngModel[field]", mockScope.ngModel.testField);
});
it("updates text from model values", function () {
@@ -91,16 +83,55 @@ define(
expect(mockScope.textValue).toEqual("1977-05-25 17:30:00");
});
describe("when valid text is entered", function () {
var newText;
beforeEach(function () {
newText = "1977-05-25 17:30:00";
mockScope.textValue = newText;
fireWatch("textValue", newText);
});
it("updates models from user-entered text", function () {
expect(mockScope.ngModel.testField)
.toEqual(mockFormat.parse(newText));
expect(mockScope.textInvalid).toBeFalsy();
});
it("does not indicate a blur event", function () {
expect(mockScope.ngBlur).not.toHaveBeenCalled();
});
});
describe("when a date is chosen via the date picker", function () {
var newValue;
beforeEach(function () {
newValue = 12345654321;
mockScope.pickerModel.value = newValue;
fireWatch("pickerModel.value", newValue);
});
it("updates models", function () {
expect(mockScope.ngModel.testField).toEqual(newValue);
});
it("fires a blur event", function () {
expect(mockScope.ngBlur).toHaveBeenCalled();
});
});
it("exposes toggle state for date-time picker", function () {
expect(mockScope.picker.active).toBe(false);
});
describe("when user input is invalid", function () {
var newText, oldValue;
var newText, oldText, oldValue;
beforeEach(function () {
newText = "Not a date";
oldValue = mockScope.ngModel.testField;
oldText = mockScope.textValue;
mockScope.textValue = newText;
fireWatch("textValue", newText);
});
@@ -116,6 +147,11 @@ define(
it("does not modify user input", function () {
expect(mockScope.textValue).toEqual(newText);
});
it("restores valid text values on request", function () {
mockScope.restoreTextValue();
expect(mockScope.textValue).toEqual(oldText);
});
});
it("does not modify valid but irregular user input", function () {

View File

@@ -91,6 +91,39 @@ define(
.toHaveBeenCalledWith("ngModel", jasmine.any(Function));
});
describe("when changes are made via form entry", function () {
beforeEach(function () {
mockScope.ngModel = {
outer: { start: DAY * 2, end: DAY * 3 },
inner: { start: DAY * 2.25, end: DAY * 2.75 }
};
mockScope.formModel = {
start: DAY * 10000,
end: DAY * 11000
};
// These watches may not exist, but Angular would fire
// them if they did.
fireWatchCollection("formModel", mockScope.formModel);
fireWatch("formModel.start", mockScope.formModel.start);
fireWatch("formModel.end", mockScope.formModel.end);
});
it("does not immediately make changes to the model", function () {
expect(mockScope.ngModel.outer.start)
.not.toEqual(mockScope.formModel.start);
expect(mockScope.ngModel.outer.end)
.not.toEqual(mockScope.formModel.end);
});
it("updates model bounds on request", function () {
mockScope.updateBoundsFromForm();
expect(mockScope.ngModel.outer.start)
.toEqual(mockScope.formModel.start);
expect(mockScope.ngModel.outer.end)
.toEqual(mockScope.formModel.end);
});
});
describe("when dragged", function () {
beforeEach(function () {
mockScope.ngModel = {

View File

@@ -0,0 +1,95 @@
/*****************************************************************************
* 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/directives/MCTSplitPane"],
function (MCTSplitPane) {
'use strict';
var JQLITE_METHODS = [
'on',
'addClass',
'children',
'eq'
];
describe("The mct-split-pane directive", function () {
var mockParse,
mockLog,
mockInterval,
mctSplitPane;
beforeEach(function () {
mockParse = jasmine.createSpy('$parse');
mockLog =
jasmine.createSpyObj('$log', ['warn', 'info', 'debug']);
mockInterval = jasmine.createSpy('$interval');
mockInterval.cancel = jasmine.createSpy('mockCancel');
mctSplitPane = new MCTSplitPane(
mockParse,
mockLog,
mockInterval
);
});
it("is only applicable as an element", function () {
expect(mctSplitPane.restrict).toEqual("E");
});
describe("when its controller is applied", function () {
var mockScope,
mockElement,
testAttrs,
mockChildren,
controller;
beforeEach(function () {
mockScope =
jasmine.createSpyObj('$scope', ['$apply', '$watch', '$on']);
mockElement =
jasmine.createSpyObj('element', JQLITE_METHODS);
testAttrs = {};
mockChildren =
jasmine.createSpyObj('children', JQLITE_METHODS);
mockElement.children.andReturn(mockChildren);
mockChildren.eq.andReturn(mockChildren);
mockChildren[0] = {};
controller = mctSplitPane.controller[3](
mockScope,
mockElement,
testAttrs
);
});
it("sets an interval which does not trigger digests", function () {
expect(mockInterval.mostRecentCall.args[3]).toBe(false);
});
});
});
}
);

View File

@@ -19,8 +19,10 @@
"directives/MCTPopup",
"directives/MCTResize",
"directives/MCTScroll",
"directives/MCTSplitPane",
"services/Popup",
"services/PopupService",
"services/UrlService",
"StyleSheetLoader"
"StyleSheetLoader",
"UnsupportedBrowserWarning"
]

View File

@@ -43,6 +43,7 @@ define(
var userAgent = $window.navigator.userAgent,
matches = userAgent.match(/iPad|iPhone|Android/i) || [];
this.userAgent = userAgent;
this.mobileName = matches[0];
this.$window = $window;
}
@@ -91,6 +92,18 @@ define(
return !this.isPortrait();
};
/**
* Check if the user agent matches a certain named device,
* as indicated by checking for a case-insensitive substring
* match.
* @param {string} name the name to check for
* @returns {boolean} true if the user agent includes that name
*/
AgentService.prototype.isBrowser = function (name) {
name = name.toLowerCase();
return this.userAgent.toLowerCase().indexOf(name) !== -1;
};
return AgentService;
}
);

View File

@@ -81,6 +81,13 @@ define(
expect(agentService.isPortrait()).toBeTruthy();
expect(agentService.isLandscape()).toBeFalsy();
});
it("allows for checking browser type", function () {
testWindow.navigator.userAgent = "Chromezilla Safarifox";
agentService = new AgentService(testWindow);
expect(agentService.isBrowser("Chrome")).toBe(true);
expect(agentService.isBrowser("Firefox")).toBe(false);
});
});
}
);