Skip to content

Commit

Permalink
Merge branch 'master' into release/4.4
Browse files Browse the repository at this point in the history
  • Loading branch information
AmazingJaze committed Oct 2, 2015
2 parents d0ccc54 + 0a7e512 commit fad5c5d
Show file tree
Hide file tree
Showing 5 changed files with 9,284 additions and 2 deletions.
48 changes: 48 additions & 0 deletions src/js/WinJS/Controls/ContentDialog.js
Expand Up @@ -24,6 +24,54 @@ define([
"use strict";

_Accents.createAccentRule(".win-contentdialog-dialog", [{ name: "outline-color", value: _Accents.ColorTypes.accent }]);

//
// Implementation Overview
//
// ContentDialog's responsibilities are divided into the following:
//
// Show/hide state management
// This involves firing the beforeshow, aftershow, beforehide, and afterhide events.
// It also involves making sure the control behaves properly when things happen in a
// variety of states such as:
// - show is called while the control is already shown
// - hide is called while the control is in the middle of showing
// - dispose is called within a beforeshow event handler
// The ContentDialog solves these problems by being implemented around a state machine.
// The states are defined in a variable called *States* and the ContentDialog's *_setState*
// method is used to change states. See the comments above the *States* variable for details.
//
// Modal
// The ContentDialog is a modal. It must coordinate with other dismissables (e.g. Flyout,
// AppBar). Specifically, it must coordinate moving focus as well as ensuring that it is
// drawn at the proper z-index relative to the other dismissables. The ContentDialog
// relies on the _LightDismissService for all of this coordination. The only pieces the
// ContentDialog is responsible for are:
// - Describing what happens when a light dismiss is triggered on the ContentDialog.
// - Describing how the ContentDialog should take/restore focus when it becomes the
// topmost dismissable.
// Other functionality that the ContentDialog gets from the _LightDismissService is around
// the eating of keyboard events. While a ContentDialog is shown, keyboard events should
// not escape the ContentDialog. This prevents global hotkeys such as the WinJS BackButton's
// global back hotkey from triggering while a ContentDialog is shown.
//
// Positioning and sizing of the dialog
// It was a goal of the ContentDialog's positioning and sizing implementation that the
// dialog's position and size could respond to changes to the dialog's content without
// the app having to be aware of or notify the dialog of the change. For example, suppose
// the dialog is shown and the user expands an accordion control within the dialog. Now
// that the dialog's content has changed size, the dialog should automatically adjust its
// size and position.
// To achieve this goal, the ContentDialog's positioning and sizing implementation is done
// entirely in LESS/CSS rather than in JavaScript. At the time, there was no cross browser
// element resize event so this was the only option. This solution resulted in LESS/CSS
// that is quite tricky. See styles-contentdialog.less for details.
//
// Responding to the input pane
// The ContentDialog's general strategy for getting out of the way of the input pane is to
// adhere to its normal positioning and sizing rules but to only use the visible portion
// of the window rather than the entire height of the window.
//

_Base.Namespace.define("WinJS.UI", {
/// <field>
Expand Down
61 changes: 61 additions & 0 deletions src/js/WinJS/Controls/SplitView/_SplitView.ts
Expand Up @@ -21,6 +21,67 @@ require(["require-style!less/colors-splitview"]);

"use strict";

//
// Implementation Overview
//
// SplitView's responsibilities are divided into the following:
//
// Open/close state management
// This involves firing the beforeopen, afteropen, beforeclose, and afterclose events.
// It also involves making sure the control behaves properly when things happen in a
// variety of states such as:
// - open is called while the control is already open
// - close is called while the control is in the middle of opening
// - dispose is called within a beforeopen event handler
// The SplitView relies on the _OpenCloseMachine component for most of this
// state management. The contract is:
// - The SplitView is responsible for specifying how to play the open and close
// animations
// - The _OpenCloseMachine is responsible for everything else including:
// - Ensuring that these animations get run at the appropriate times
// - Tracking the current state of the control and ensuring the right thing happens
// when a method is called
// - Firing the events
//
// Light dismiss
// The SplitView's pane is light dismissable when the pane is open and the SplitView
// is configured to openedDisplayMode:overlay. This means that the pane can be closed
// thru a variety of cues such as tapping off of the pane, pressing the escape key,
// and resizing the window. SplitView relies on the _LightDismissService component for
// most of this functionality. The only pieces the SplitView is responsible for are:
// - Describing what happens when a light dismiss is triggered on the SplitView.
// - Describing how the SplitView should take/restore focus when it becomes the
// topmost light dismissable.
//
// Open/close animations
// Much of the SplitView's implementation is dedicated to playing the open and close
// animations. The general approach is for the SplitView to calculate the current and
// final sizes and positions of its pane and content elements. Then it creates a CSS
// transition to animate the control between these two visual states.
//
// One tricky part of the animation is that the SplitView creates an animation that looks
// like the pane is changing its width/height. You cannot animate width/height changes in CSS
// so this animation is actually an illusion. It involves animating 2 elements, one which
// clips the other, to give the illusion that an element is resizing. This logic is carried
// out by Animations._resizeTransition. Take a look at that method for more details.
//
// Update DOM
// SplitView follows the Update DOM pattern. For more information about this pattern, see:
// https://github.com/winjs/winjs/wiki/Update-DOM-Pattern
//
// Note that the SplitView reads from the DOM when it needs to measure the position and size
// of its pane and content elements. When possible, it caches this information and reads from
// the cache instead of the DOM. This minimizes the performance cost.
//
// Outside of updateDom, SplitView writes to the DOM in a couple of places:
// - The initializeDom function runs during construction and creates the
// initial version of the SplitView's DOM.
// - During animations, the animations take ownership of the DOM and turn
// updateDom into a no-op. When the animation completes, updateDom begins
// running again. This disabling of updateDom during animations is carried
// out by _OpenCloseMachine.
//

interface IRect {
left: number;
top: number;
Expand Down
113 changes: 111 additions & 2 deletions src/js/WinJS/_LightDismissService.ts
Expand Up @@ -13,6 +13,108 @@ require(["require-style!less/styles-lightdismissservice"]);

"use strict";

//
// Implementation Overview
//
// The _LightDismissService was designed to coordinate light dismissables. A light
// dismissable is an element which, when shown, can be dismissed due to a variety of
// cues (listed in LightDismissalReasons):
// - Clicking/tapping outside of the light dismissable
// - Focus leaving the light dismissable
// - User pressing the escape key
// - User pressing the hardware back button
// - User resizing the window
// - User focusing on a different app
//
// The _LightDismissService's responsibilites have grown so that it can manage more than
// just light dismissables (e.g. modals which ignore most of the light dismiss cues). Its
// responsibilities include:
// - Rendering the dismissables in the correct z-order. The most recently opened dismissable
// is the topmost one. In order for this requirement to be fulfilled, apps must make
// dismissables direct children of the body. For details, see:
// https://github.com/winjs/winjs/wiki/Dismissables-and-Stacking-Contexts
// - When a different dismissable becomes the topmost, giving that dismissable focus.
// - Informing dismissables when a dismiss cue occurs so they can dismiss if they want to
// - Propagating keyboard events which occur within a dismissable to dismissables underneath
// it in the stack. When a modal dialog is in the stack, this enables the modal to prevent
// any keyboard events from escaping the light dismiss stack. Consequently, global
// hotkeys such as the WinJS BackButton's global back hotkey will be disabled while a
// modal dialog is in the stack.
// - Disabling SearchBox's type to search feature while any dismissable is in the stack.
// Consequently, type to search can only be used in the body -- it cannot be used inside
// of dismissables. If the _LightDismissService didn't do this, typing within any light
// dismissable would cause focus to move into the SearchBox in the body and for all light
// dismissables to lose focus and thus close. Type to search is provided by the event
// requestingFocusOnKeyboardInput.
//
// Controls which want to utilize the _LightDismissService must implement ILightDismissable.
// The _LightDismissService provides a couple of classes which controls can utilize that
// implement most of the methods of ILightDismissable:
// - LightDismissableElement. Used by controls which are light dismissable. These include:
// - AppBar/ToolBar
// - Flyout
// - Menu
// - SplitView
// - ModalElement. Used by controls which are modal. These include:
// - ContentDialog
//
// Debugging tip.
// Call WinJS.UI._LightDismissService._setDebug(true)
// This disables the "window blur" light dismiss cue. It enables you to move focus
// to the debugger or DOM explorer without causing the light dismissables to close.
//
// Example usage of _LightDismissService
// To implement a new light dismissable, you just need to:
// - Tell the service when you are shown
// - Tell the service when you are hidden
// - Create an object which implements ILightDismissable. In most cases, you will
// utilize the LightDismissableElement or ModalElement helper which only requires
// you to provide a DOM element, a tab index for that element, and an
// implementation of onLightDismiss.
//
// Here's what a basic light dismissable looks like in code:
//
// var SimpleOverlay = _Base.Class.define(function (element) {
// var that = this;
// this.element = element || document.createElement("div");
// this.element.winControl = this;
// _ElementUtilities.addClass(this.element, "simpleoverlay");
//
// this._dismissable = new _LightDismissService.LightDismissableElement({
// element: this.element,
// tabIndex: this.element.hasAttribute("tabIndex") ? this.element.tabIndex : -1,
// onLightDismiss: function () {
// that.hide();
// }
// });
// }, {
// show: function () {
// _ElementUtilities.addClass(this.element, "simpleoverlay-shown");
// _LightDismissService.shown(this._dismissable);
// },
// hide: function () {
// _ElementUtilities.removeClass(this.element, "simpleoverlay-shown");
// _LightDismissService.hidden(this._dismissable);
// }
// });
//
// When using LightDismissableElement/ModalElement, you may optionally override:
// - onShouldLightDismiss: This enables you to specify which light dismiss cues
// should trigger a light dismiss for your control. The defaults are:
// - LightDismissableElement: Essentially all cues trigger a dismiss.
// - ModalElement: Only the escape key and hardware back button trigger a
// dismiss.
// - onTakeFocus: This enables you to specify which element within your control
// should receive focus when it becomes the topmost dismissable. When overriding
// this method, it's common to start by calling this.restoreFocus, a helper
// provided by LightDismissableElement/ModalElement, to restore focus to the
// element within the dismissable which most recently had it. By default,
// onTakeFocus tries to give focus to elements in the following order:
// - Tries to restore focus to the element that previously had focus
// - Tries to give focus to the first focusable element in the dismissable.
// - Tries to give focus to the root element of the dismissable.
//

var baseZIndex = 1000;

var Strings = {
Expand Down Expand Up @@ -203,6 +305,11 @@ class AbstractDismissableElement implements ILightDismissable {
this._ldeOnKeyPressBound = this._ldeOnKeyPress.bind(this);
}

// Helper which can be called when implementing onTakeFocus. Restores focus to
// whatever element within the dismissable previously had focus. Returns true
// if focus was successfully restored and false otherwise. In the false case,
// onTakeFocus implementers typically try to give focus to either the first
// focusable element in the dismissable or to the root element of the dismissable.
restoreFocus(): boolean {
var activeElement = <HTMLElement>_Global.document.activeElement;
if (activeElement && this.containsElement(activeElement)) {
Expand Down Expand Up @@ -403,18 +510,20 @@ class LightDismissService implements ILightDismissService {
this._updateDom();
}

// Dismissables should call keyDown, keyUp, and keyPress when such an event occurs within the dismissable. The
// _LightDismissService takes these events and propagates them to other ILightDismissables in the stack by calling
// onKeyInStack on them. LightDismissableElement and ModalElement call keyDown, keyUp, and keyPress for you so
// if you use them while implementing an ILightDismissable, you don't have to worry about this responsibility.
keyDown(client: ILightDismissable, eventObject: KeyboardEvent) {
if (eventObject.keyCode === _ElementUtilities.Key.escape) {
this._escapePressed(eventObject);
} else {
this._dispatchKeyboardEvent(client, KeyboardInfoType.keyDown, eventObject);
}
}

keyUp(client: ILightDismissable, eventObject: KeyboardEvent) {
this._dispatchKeyboardEvent(client, KeyboardInfoType.keyUp, eventObject);
}

keyPress(client: ILightDismissable, eventObject: KeyboardEvent) {
this._dispatchKeyboardEvent(client, KeyboardInfoType.keyPress, eventObject);
}
Expand Down
66 changes: 66 additions & 0 deletions test-pages/MediaPlayer/index.html
@@ -0,0 +1,66 @@
<!doctype html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>MediaPlayer Test Page</title>

<!--<link href="../../bin/Microsoft.WinJS.4.3/css/ui-light.css" rel="stylesheet" />-->
<link href="../../bin/Microsoft.WinJS.4.3/css/ui-dark.css" rel="stylesheet" />
<script src="../../bin/Microsoft.WinJS.4.3/js/base.js"></script>
<script src="../../bin/Microsoft.WinJS.4.3/js/ui.js"></script>

<!--<link href="../../bin/Microsoft.WinJS.4.3/css/ui-light-mediaplayer.css" rel="stylesheet" />-->
<link href="../../bin/Microsoft.WinJS.4.3/css/ui-dark-mediaplayer.css" rel="stylesheet" />
<script src="../../bin/Microsoft.WinJS.4.3/js/mediaplayer.js"></script>

<style>
.win-mediaplayer {
/*position: relative;
height: 200px;*/
}
</style>
<script>
var mp;
function init() {
WinJS.UI.processAll();
mp = document.getElementById("mediaPlayerEl").winControl;
// mp.setContentMetadata("video", {
// title: "My Title",
// description: "Have you seen this description? It's amazing!"
// });
}

WinJS.Utilities.ready(init);
</script>
</head>
<body class="win-type-body">
<div id="mediaPlayerEl" data-win-control="WinJS.UI.MediaPlayer" data-win-options="{
castButtonVisible: true,
chapterSkipBackButtonVisible: true,
chapterSkipForwardButtonVisible: true,
fastForwardButtonVisible: true,
fullscreenButtonVisible: true,
goToLiveButtonVisible: true,
nextTrackButtonVisible: true,
playFromBeginningButtonVisible: true,
playPauseButtonVisible: true,
playbackRateButtonVisible: true,
previousTrackButtonVisible: true,
rewindButtonVisible: true,
seekBarVisible: true,
stopButtonVisible: true,
timeSkipBackButtonVisible: true,
timeSkipForwardButtonVisible: true,
volumeButtonVisible: true,
zoomButtonVisible: true,
fullScreen: true
}">
<video>
<source src="http://video.ch9.ms/ch9/aea3/bf8d2647-ec8c-4f0d-b214-c9ca6a48aea3/KEY01.mp4" type="video/mp4">
<track src="subtitles_en.vtt" kind="captions" srclang="en" label="English">
</video>
</div>
<!--<div data-win-control="WinJS.UI.AutoSuggestBox" data-win-options="{placeholderText: 'Search for a video'}"></div>-->
</body>
</html>

0 comments on commit fad5c5d

Please sign in to comment.