home *** CD-ROM | disk | FTP | other *** search
Text File | 2006-01-01 | 64.3 KB | 2,381 lines |
- // ADOBE SYSTEMS INCORPORATED
- // Copyright 2003 Adobe Systems Incorporated
- // All Rights Reserved
-
- // NOTICE: Adobe permits you to use, modify, and distribute this file in
- // accordance with the terms of the Adobe license agreement accompanying it.
- // If you have received this file from a source other than Adobe, then your use,
- // modification, or distribution of it requires the prior written permission of Adobe.
-
- // media.js - Adobe Acrobat multimedia support
-
- // The app and app.media properties and methods in this file are part of the public Acrobat
- // Multimedia API and may be used in your PDF JavaScript, EXCEPT where the name includes "priv"
- // or is otherwise noted as private. DO NOT USE any of these private properties or methods in
- // a PDF file, or YOUR PDF WILL BREAK in a future version of Acrobat.
-
- // Greetings and thanks from the Acrobat Multimedia development team:
- // Dylan Ashe - Multimedia Framework and QuickTime
- // Michael Geary - JavaScript, Windows Media, Flash
- // Scott Grant - User Interface, Authoring, Sound, PDF File Access
- // Vivek Hebbar - Windows Built-in Player and Authoring
- // Liz McQuarrie - RealOne Player and Browsers
- // Ed Rowe - Project Lead and Mr. PDF
- // Jason Beique, Paul Herrin, Renato Maschion, Jason Reuer, Xintai Chang - QA and Developer Tech
-
-
- // Multimedia version number
-
- console.println( 'Acrobat Multimedia Version 6.0' );
-
- app.media.version = 6.0;
-
-
- // Set app.media.trace = true to enable method and event tracing in this code.
- // Note: app.media.trace is for testing only and will change in future versions.
-
- app.media.trace = false;
-
-
- // The app.media.* constants below are passed back and forth between C++ and JavaScript code.
- // Always use these symbolic definitions instead of hard coded values, e.g.
- // settings.windowType = app.media.windowType.floating; /* NOT settings.windowType = 2; */
-
- // PDF files may be opened under both newer and older versions of Acrobat.
- // Future versions of Acrobat may add new values to these lists. Your JavaScript code should
- // gracefully handle any values it encounters beyond those listed here.
- // Similarly, if you write JavaScript code for a future version of Acrobat, that code should
- // check app.media.version before it depends on new app.media.* constant values added in
- // that version. The lists below are marked to indicate which versions support which constants.
-
-
- // Values for settings.layout
-
- app.media.layout =
- {
- meet: 1, // scale to fit all content, preserve aspect, no clipping, background fill
- slice: 2, // scale to fill window, preserve aspect, clip X or Y as needed
- fill: 3, // scale X and Y separately to fill window
- scroll: 4, // natural size with scrolling
- hidden: 5, // natural size with clipping
- standard: 6 // use player's default settings
- // End 6.0 values
- }
-
-
- // Values for settings.windowType
-
- app.media.windowType =
- {
- docked: 1,
- floating: 2,
- fullScreen: 3
- // End 6.0 values
- }
-
-
- // Values for settings.monitorType
-
- app.media.monitorType =
- {
- document: 1,
- nonDocument: 2,
- primary: 3,
- bestColor: 4,
- largest: 5,
- tallest: 6,
- widest: 7
- // End 6.0 values
- }
-
-
- // Values for settings.floating.align
-
- app.media.align =
- {
- topLeft: 1,
- topCenter: 2,
- topRight: 3,
- centerLeft: 4,
- center: 5,
- centerRight: 6,
- bottomLeft: 7,
- bottomCenter: 8,
- bottomRight: 9
- // End 6.0 values
- }
-
-
- // Values for settings.floating.canResize
-
- app.media.canResize =
- {
- no: 1,
- keepRatio: 2,
- yes: 3
- // End 6.0 values
- }
-
-
- // Values for settings.floating.over
-
- app.media.over =
- {
- pageWindow: 1,
- appWindow: 2,
- desktop: 3,
- monitor: 4
- // End 6.0 values
- }
-
-
- // Values for settings.floating.ifOffScreen
-
- app.media.ifOffScreen =
- {
- allow: 1,
- forceOnScreen: 2,
- cancel: 3
- // End 6.0 values
- }
-
-
- // Default value for settings.visible
-
- app.media.defaultVisible = true;
-
-
- // Values for rendition.type
-
- app.media.renditionType =
- {
- unknown: 0, // rendition type not recognized by Acrobat
- media: 1,
- selector: 2
- // End 6.0 values
- }
-
-
- // Values for event.media.code in Status event
-
- app.media.status =
- { // event.media.text contains:
- clear: 1, // empty string - clears any message
- message: 2, // general message
- contacting: 3, // hostname being contacted
- buffering: 4, // percent complete
- init: 5, // name of the player being initialized
- seeking: 6 // nothing
- // End 6.0 values
- }
-
-
- // Values for event.media.closeReason in Close event
-
- app.media.closeReason =
- {
- general: 1,
- error: 2,
- done: 3,
- stop: 4,
- play: 5,
- uiGeneral: 6,
- uiScreen: 7,
- uiEdit: 8,
- docClose: 9,
- docSave: 10,
- docChange: 11
- // End 6.0 values
- }
-
-
- // Values for player.open() return value
-
- app.media.openCode =
- {
- success: 0,
- failGeneral: 1,
- failSecurityWindow: 2,
- failPlayerMixed: 3,
- failPlayerSecurityPrompt: 4,
- failPlayerNotFound: 5,
- failPlayerMimeType: 6,
- failPlayerSecurity: 7,
- failPlayerData: 8
- // End 6.0 values
- }
-
-
- // Values for Error.raiseSystem
-
- app.media.raiseSystem =
- {
- fileError: 10
- }
-
-
- // Values for Error.raiseCode
-
- app.media.raiseCode =
- {
- fileNotFound: 17,
- fileOpenFailed: 18
- }
-
-
- // In a PDF event, these event.name values indicate page-level actions.
-
- app.media.pageEventNames =
- {
- Open: true,
- Close: true,
- InView: true,
- OutView: true
- }
-
-
- // Create and return a MediaPlayer without opening it. Explicit values can be provided for all
- // the arguments listed below. If this function is called from a rendition action, it will get
- // the annot, rendition, and doc values from the event object if they are not explicitly provided.
- // Returns player, or null on failure. Does not throw exceptions.
- // Any failures are reported to user, and result in null being returned.
- // Events may be fired as a result of closing an existing player (see below).
- // Unless noStockEvents is true, stock event handlers are added to the returned player,
- // and will be added to the annot (if present) when player.open() is called later.
-
- // player = app.media.createPlayer({
- // doc: Doc, /* Required if both annot and rendition are omitted, e.g. for URL playback */
- // annot: ScreenAnnot, /* Required for docked playback unless it is found in the Event object
- // or settings.page is provided. The new player is associated with the annot. If a player
- // was already associated with the annot, it is stopped and closed. */
- // rendition: MediaRendition or RenditionList, /* Required unless rendition found in Event,
- // or URL is present */
- // URL: String, /* Either URL or rendition is required, with URL taking precedence */
- // mimeType: string, /* Optional, ignored unless URL is present. If URL is present, either
- // mimeType or settings.players, as returned by app.media.getPlayers(), is required */
- // settings: MediaSettings, /* Optional, overrides the rendition settings */
- // events: EventListener, /* Optional (if stock events are used, added after stock events) */
- // noStockEvents: Boolean, /* Optional, default = false, use stock events or not */
- // fromUser: Boolean, /* Optional, default depends on Event object */
- // showAltText: Boolean, /* Optional, default = true */
- // showEmptyAltText: Boolean /* Optional, default= ! fromUser */
- // });
-
- app.media.createPlayer = function( args )
- {
- try
- {
- return app.media.priv.createPlayer( app.media.argsDWIM( args ) );
- }
- catch( e )
- {
- app.media.alert( 'Exception', args, { error: e } );
- return null;
- }
- }
-
-
- // Create, open, and return a MediaPlayer. See app.media.createPlayer() for argument details
- // and other information.
- // This method fires several events which may include Open, Ready, Play and Focus.
- // Returns player, or null on failure. Does not throw exceptions.
- // Any failures are reported to user.
-
- app.media.openPlayer = function( args )
- {
- var player = null;
- try
- {
- // Do our own DWIM here to make sure args.doc is set in case of error
- args = app.media.argsDWIM( args );
-
- player = app.media.createPlayer( args );
- if( player )
- {
- var result = player.open();
- if( result.code != app.media.openCode.success )
- {
- player = null;
- app.media.alert( 'Open', args, { code: result.code } );
- }
- else if( player.visible )
- player.setFocus(); // fires Focus event
- }
- }
- catch( e )
- {
- player = null;
- app.media.alert( 'Exception', args, { error: e } );
- }
-
- return player;
- }
-
-
- // Open a new media player using the current event or explicit args as in app.media.createPlayer().
- // If an annot is provided or found in event object, and there is already a player open for that
- // annot, then start or resume playback on that player.
- // See app.media.openPlayer for argument details and other information.
- // Returns player, or null on failure. Does not throw exceptions.
- // Any failures are reported to user.
-
- app.media.startPlayer = function( args )
- {
- try
- {
- args = app.media.argsDWIM( args );
-
- var player = args.annot && args.annot.player;
- if( player && player.isOpen )
- player.play(); // already opened, resume play
- else
- player = app.media.openPlayer( args ); // open a new player
-
- return player;
- }
- catch( e )
- {
- app.media.alert( 'Exception', args, { error: e } );
- return null;
- }
- },
-
-
- // app.media.Events constructor and prototype
-
- // The Events constructor, events.add, and events.remove methods each takes any number of
- // arguments, where each argument can be either an event listener object or a previously
- // constructed app.media.Events object. The constructor and add() method add each listener
- // object to the Events object, and the remove() method removes listener objects.
-
- // An event listener object is a collection of event methods and optional custom properties or
- // methods. Any method whose name matches /^on[A-Z]/ or /^after[A-Z]/ is an event method. Custom
- // methods and properties in an event listener should use names that do not match these case
- // sensitive patterns.
-
- // When an event listener method is called, 'this' is the event listener object. The event
- // listener can have custom properties like any other object. If an event listener is nested
- // inside another function (such as a constructor), then the event methods can also directly
- // access any variables defined in the parent function, even when the event method is called
- // after the parent function returns.
-
- // The same event listener object may be added into more than one Events object. The object is
- // not copied; each Events object has a reference to the original event listener object, so any
- // properties of the listener object are shared by every Events object it is added to.
-
- // Implementation note:
- // An app.media.Events object has a listeners property which is a object containing
- // onVariousEvent and afterVariousEvent properties. Each of these properties is an array of
- // references to event listener objects which contain the event methods.
- // So, for example, after this call:
- // var events = new app.media.Events({ onPlay: function(e){} });
- // events.listeners.onPlay[0] is a reference to a new { onPlay: function(e){} } object, and
- // events.listeners.onPlay[0].onPlay is a reference to the onPlay method.
-
- app.media.Events = function()
- {
- this.listeners = {}; // start with empty listeners object
- this.dispatching = 0; // not currently dispatching any events
- this.removed = {}; // listener names that need delayed removal
- this.privAddRemove( arguments, this.privAdd ); // add any event listener object arguments
- }
-
-
- app.media.Events.prototype =
- {
-
-
- // Add any number of event listener objects or other app.media.Events objects.
- // events.add() may be called inside an event listener method, and any new listeners that are
- // added for the current event will be called for that same event.
- // If the same listener object is added twice, the second add is ignored.
- // Listeners for a given event (e.g. onClose) are called in the order in which they were added.
-
- // events.add( event listener or app.media.Events object(s) )
-
- add: function()
- {
- this.privAddRemove( arguments, this.privAdd );
- },
-
-
- // Remove any number of event listener objects or other app.media.Events objects.
- // events.remove() may be called inside an event listener method, to remove the current listener
- // or any other.
-
- // events.remove( event listener or app.media.Events object(s) )
-
- remove: function()
- {
- this.privAddRemove( arguments, this.privRemove );
- },
-
-
- // Private method for events.add() and events.remove().
- // Loop through all the listener methods in each argument and call doAddRemove for every one.
-
- privAddRemove: function( args, doAddRemove )
- {
- for( var i = 0; i < args.length; i++ ) // for each event listener object argument in passed array
- {
- var events = args[i];
- if( events.listeners )
- {
- // It's an app.media.Events object, add or remove every listener in each array
- for( var name in events.listeners )
- {
- var array = events.listeners[name];
- for( var i = 0; i < array.length; i++ )
- {
- doAddRemove.call( this, array[i], name );
- }
- }
- }
- else
- {
- // It's an event listener object, add or remove each method
- for( var name in events )
- {
- // Only interested in onFoo and afterFoo methods, not custom properties
- if( name.search(/^on[A-Z]/) == 0 || name.search(/^after[A-Z]/) == 0 )
- {
- doAddRemove.call( this, events, name );
- }
- }
- }
- }
-
- this.privSetDispatch(); // Add or remove the dispatch() method as needed
- },
-
-
- // Private method for events.add().
- // Adds a reference to a listener object into events.listeners[name]
- // Does nothing if listener already added.
-
- privAdd: function( listener, name )
- {
- if( typeof(listener) != "object" || typeof(listener[name]) != "function" )
- return; // not a valid object and method
-
- var array = this.listeners[name]; // get our existing event listener array
- if( ! array )
- {
- this.listeners[name] = [ listener ]; // no array yet, add array with one listener object
- }
- else // we have a listener array, append listener to it if it's not already present
- {
- for( var i = 0; i < array.length; i++ )
- {
- if( array[i] === listener )
- return; // already present, don't add another
- }
-
- array[i] = listener; // append listener to array
- }
- },
-
-
- // Private method for events.remove().
- // Removes a listener object reference from events.listeners[name]
-
- privRemove: function( listener, name )
- {
- var array = this.listeners[name]; // existing event listener array
- if( ! array )
- return; // no listeners with this name
-
- for( var i = 0; i < array.length; i++ ) // Look for the listener object in the array
- {
- if( array[i] === listener )
- {
- // Found the listener in the array, decide what to do with it
- if( this.dispatching ) // Can't remove while dispatching, mark for later removal
- array[i] = null, this.removed[name] = this.needCleanup = true;
- else if( array.length > 1 )
- array.splice( i, 1 ); // Remove listener from array
- else
- delete this.listeners[name]; // Last one, remove array entirely
-
- return; // Listener is already in the array
- }
- }
- },
-
-
- // Private function for events.add(), events.remove() and events.privCleanup().
- // Sets or deletes the dispatch method depending on whether there are any event listeners.
-
- privSetDispatch: function()
- {
- for( var name in this.listeners ) // are there any listeners?
- {
- this.dispatch = this.privDispatch; // found a listener, set the dispatch method
- return;
- }
-
- delete this.dispatch; // no listeners, remove the dispatch method
- },
-
-
- // events.privDispatch() is a private method that contains the code for events.dispatch().
- // To dispatch an event, C++ code calls events.dispatch(), only if that method exists.
- // The rest of the event dispatching machinery is implemented in JavaScript.
- // We turn event dispatching on and off dynamically by setting and removing the events.dispatch
- // property, which is a reference to events.privDispatch().
- // If you call events.dispatch() directly from JavaScript, event.target.doc or event.media.doc
- // must match the current document.
- // You can implement your own event dispatcher from scratch by providing an events object
- // with a dispatch method that takes an event argument as this method does.
- // This function is reentrant.
-
- privDispatch: function( event )
- {
- if( !event.media )
- event.media = {};
-
- // PDF events may have spaces in their names, so make a copy of event.name with spaces removed
- event.media.id = event.name.replace( / /, '' );
-
- // Use doc and events properties in either event.target or in the event object itself
- if( event.target )
- {
- event.media.doc = event.target.doc;
- event.media.events = event.target.events;
- }
-
- ++this.dispatching; // if this.dispatching > 0, events.remove() will use deferred removal
-
- try
- {
- // First call immediate (onFoo) listener methods
- this.privDispatchNow( 'on', event ); // may reenter this function
-
- // Turn stopDispatch off in case an immediate listener turned it on
- delete event.stopDispatch;
-
- // If there are any deferred (afterFoo) listeners, post event to queue,
- // but don't bother if an immediate listener stopped all dispatching
- if( ! event.stopAllDispatch )
- if( this.listeners[ 'afterEveryEvent' ] || this.listeners[ 'after' + event.media.id ] )
- app.media.priv.postEvent( event );
- }
- catch( e )
- {
- app.media.priv.trace( 'di throw: ' + e.message );
- }
-
- --this.dispatching;
-
- // If any event listeners were marked for removal while we were dispatching events,
- // and we are done with any nested dispatch calls, then clean up the listener arrays.
- if( this.needCleanup && ! this.dispatching )
- this.privCleanup();
- },
-
-
- // Private method for events.dispatch().
- // Clean up any event listener arrays that have had entries marked for removal.
- // Each event name that needs to be cleaned up has an entry in this.removed.
- // Each listener that is to be removed has been set to null.
-
- privCleanup: function()
- {
- for( var name in this.removed )
- {
- var array = this.listeners[name];
- for( var i = 0; i < array.length; i++ )
- {
- if( ! array[i] )
- array.splice( i--, 1 ); // Remove listener from array and back up index
- }
-
- if( array.length == 0 )
- delete this.listeners[name]; // Remove listener array if it's now empty
- }
-
- this.removed = {};
-
- this.privSetDispatch(); // Remove the dispatch method if there are no more listeners
-
- delete this.needCleanup;
- },
-
-
- // Private method for events.dispatch().
- // Immediately dispatch a single event to all listeners for that event.
- // prefix is 'on' or 'after', and event is the event object.
- // Calls both EveryEvent listener methods and any specific listener methods for the event.
- // This function is reentrant.
-
- privDispatchNow: function( prefix, event )
- {
- this.privCallMethods( event, prefix + 'EveryEvent' ); // may reenter this function
- this.privCallMethods( event, prefix + event.media.id ); // may reenter this function
- },
-
-
- // Private method for events.dispatch().
- // Loop through the events.listeners[name] array and call each event listener method found
- // there, with 'this' as the event listener object that contains the method.
- // If new listeners are added while dispatching, they will also be called.
- // This function is reentrant.
-
- privCallMethods: function( event, name )
- {
- var array = this.listeners[name];
- if( array )
- {
- // Call each listener method in the array
- for( var i = 0; i < array.length; i++ )
- {
- if( event.stopDispatch || event.stopAllDispatch )
- break;
-
- var listener = array[i];
- if( listener ) // listener is null if removed while dispatching
- {
- listener[name]( event ); // may reenter this function
- }
- }
- }
- },
-
-
- }
- // end app.media.Events.prototype
-
-
- // A simple event queue.
-
- // app.media.priv.postEvent(event) and app.media.priv.dispatchQueuedEvents() use
- // doc.media.priv.queue to manage a per-doc event queue.
- // This private method has the same restrictions on calling it as app.media.Events.privDispatch().
-
- app.media.priv.postEvent = function( event )
- {
- var q = event.media.doc.media.priv.queue;
- if( ! q )
- q = event.media.doc.media.priv.queue = {};
-
- if( ! q.list )
- q.list = [];
-
- q.list.push( event );
-
- if( ! q.timer )
- {
- q.timer = app.setTimeOut( 'app.media.priv.dispatchPostedEvents(this);', 1, false ); // no disp while modal dlg up
- q.timer.media = { doc: event.media.doc }; // allow access to doc from timer obj
- }
- }
-
-
- // Called from the short timer set by app.media.priv.postEvent() to dispatch all posted events.
-
- app.media.priv.dispatchPostedEvents = function( doc )
- {
- try
- {
- // If doc already closed, bail! Closing doc does NOT unregister timeouts!
- // They may or may not fire depending on whether they are GCed before getting fired.
- // Event.target is our timeout obj.
- if ( event.target.media.doc.closed )
- return;
-
- // Grab and delete queue--any new event queued while dispatching will be dispatched later
- var q = doc.media.priv.queue;
- var list = q.list;
- delete q.list;
- delete q.timer;
-
- for( var i = 0; i < list.length; i++ )
- {
- // Stop dispatching "after" events if the doc is closed, checked here in case an
- // event method closes the doc. Do not check in privCallMethods--an event method
- // that closes the doc should set event.stopDispatchAll.
- if( doc.closed )
- return;
-
- var e = list[i];
- if( e.media.events )
- e.media.events.privDispatchNow( "after", e );
- }
- }
- catch( e )
- {
- app.media.priv.trace( 'dpe throw: ' + e.message );
- }
- }
-
-
- // app.media.Markers constructor and prototype
-
- app.media.Markers = function( player )
- {
- this.player = player;
- }
-
-
- app.media.Markers.prototype =
- {
-
- // Finds a marker by name, index number, time, or frame.
- // Index numbers are not in any guaranteed order.
- // If a time or frame is given, returns the nearest marker at or before that location.
- // Returns null if no matching marker is found.
- //
- // marker = markers.get( cName );
- // marker = markers.get({ name: cName });
- // marker = markers.get({ index: nIndex });
- // marker = markers.get({ time: nSeconds });
- // marker = markers.get({ frame: nFrame });
-
- get: function( m )
- {
- if( ! this.privByIndex )
- this.player.privLoadMarkers();
-
- var retMarker = null;
-
- if( this.privByIndex.length > 0 )
- {
- switch( typeof(m) )
- {
- case 'string':
- retMarker = this.privByName[m];
- break;
-
- case 'object':
- retMarker = (
- m.name !== undefined ? this.privByName[ m.name ] :
- m.index !== undefined ? this.privByIndex[ m.index ] :
- m.time !== undefined ? this.privFind( 'time', m.time ) :
- m.frame !== undefined ? this.privFind( 'frame', m.frame ) :
- undefined );
- break;
- }
- }
-
- if( retMarker === undefined )
- retMarker = null;
-
- return retMarker;
- },
-
-
- // Private method for markers.get() to find a marker by time or frame.
-
- privFind: function( prop, value )
- {
- if( value < 0 )
- return; // negative time or frame not allowed
-
- var array = this.privByIndex;
- var length = array.length;
-
- // Search for nearest marker <= passed value; does not assume any sort order.
- var nearIdx;
- var nearDist = Infinity;
- for( var i = 0; i < length; i++ )
- {
- // Test for undefined in case some markers have time and some have frame
- var v = array[i][prop];
- if( v !== undefined )
- {
- var dist = ( value - v );
- if( dist >= 0 && dist < nearDist )
- {
- // have a new "nearest marker <= value"
- nearIdx = i;
- nearDist = dist;
- }
- }
- }
-
- if( nearIdx !== undefined )
- return array[ nearIdx ];
- },
-
-
- }
- // end app.media.Markers.prototype
-
-
- // app.Monitors constructor and prototype
-
- app.Monitors = function()
- {
- this.length = 0;
- }
-
-
- app.Monitors.prototype =
- {
-
-
- // monitors.clear()
-
- clear: function()
- {
- while( this.length > 0 )
- delete this[ --this.length ];
- },
-
-
- // monitors.push( value )
- // Appends a reference to a monitor object to the array.
-
- push: function( value )
- {
- this[ this.length++ ] = value;
- },
-
-
- // monitors = monitors.select( monitorType, doc )
- // Filter a Monitors array based on an app.media.monitorType value as used in PDF.
- // doc is required if monitorType is app.media.monitorType.document or
- // app.media.monitorType.nonDocument, otherwise it is ignored.
- // Returns new array of references to the selected monitor objects.
-
- select: function( monitorType, doc )
- {
- switch( monitorType )
- {
- default:
- case app.media.monitorType.document: return this.document(doc).primary();
- case app.media.monitorType.nonDocument: return this.nonDocument(doc).primary();
- case app.media.monitorType.primary: return this.primary();
- case app.media.monitorType.bestColor: return this.bestColor().primary();
- case app.media.monitorType.largest: return this.largest().primary();
- case app.media.monitorType.tallest: return this.tallest().primary();
- case app.media.monitorType.widest: return this.widest().primary();
- }
- },
-
-
- // monitors.filter( ranker, minRank )
- // Returns a Monitors array containing the monitors that score the highest rank according to
- // the ranker function. The ranker function takes a Monitor parameter and returns a numeric or
- // boolean rank for it (or any type that can be converted to a number).
- // A numeric rank may be any finite value.
- // If minRank is not specified, the array returned always contains at least one element (unless
- // the original array was already empty).
- // If minRank is specified but the final rank is less, the array returned is empty.
- // If multiple monitors tie for the highest rank, the returned array contains those monitors in
- // the same order as the original array.
-
- filter: function( ranker, minRank )
- {
- var r = new app.Monitors;
- var rank = ( minRank != undefined ? minRank : -Infinity );
-
- for( var i = 0; i < this.length; i++ )
- {
- // Rank the next Monitor object
- var m = this[i];
- var mRank = ranker( m );
-
- // If it outranks the best previous ranking, clear the result list.
- // If it's the same rank, add it to the result list.
- if( mRank >= rank )
- {
- if( mRank > rank )
- r.clear(); // new outranks old, clear result array
-
- r.push( m ); // append new result to any same-ranked results
- rank = mRank; // save new rank
- }
- }
-
- return r;
- },
-
-
- // monitors.bestColor( minColor )
- // Returns a Monitors array containing the monitor(s) that have the greatest color depth.
- // Returns empty array if minColor is specified and no monitor in the array has a color depth of
- // at least minColor bits.
-
- bestColor: function( minColor )
- {
- return this.filter(
- function( m ) { return m.colorDepth; },
- minColor );
- },
-
-
- // monitors.bestFit( width, height, bRequire )
- // Returns a Monitors array containing the monitor(s) that have at least the specified width and
- // height with the least amount of excess area. If all monitors are smaller than the specified
- // width and height, then returns an empty array if bRequire is true, or an array of the largest
- // available monitors if bRequire is false.
-
- bestFit: function( width, height, bRequire )
- {
- var tiny = -1000000000;
- var area = ( width * height );
-
- return this.filter(
- function( m )
- {
- var mWidth = m.rect[2] - m.rect[0];
- var mHeight = m.rect[3] - m.rect[1];
-
- // Rank lowest if it doesn't fit at all, else rank by least excess area
- return(
- width > mWidth || height > mHeight ? tiny :
- area - ( mWidth * mHeight ) );
- },
- bRequire ? ( tiny + 1 ) : tiny );
- },
-
-
- // monitors.desktop()
- // Returns a Monitors array with a single Monitor that represents the entire virtual desktop:
- // rect = the union of all Monitor.rect values
- // workRect = the union of all the workRect values (may include parts of monitors that are
- // outside their workRects).
- // colorDepth = the least color depth of any monitor
- // isPrimary = (not present)
-
- desktop: function()
- {
- if( ! this.length )
- return [];
-
- var r = { rect: [0,0,0,0], workRect: [0,0,0,0], colorDepth: Number.MAX_VALUE };
-
- for( var i = 0; i < this.length; i++ )
- {
- var m = this[i];
-
- r.rect = app.media.priv.rectUnion( r.rect, m.rect );
- r.workRect = app.media.priv.rectUnion( r.workRect, m.workRect );
- r.colorDepth = Math.min( r.colorDepth, m.colorDepth );
- }
-
- var result = new app.Monitors;
- result.push( r );
-
- return result;
- },
-
-
- // monitors.document( doc, bRequire )
- // Returns a Monitors array containing the monitor(s) that display the greatest amount of the
- // specified document.
- // If bRequire is true, returns empty array if the document does not appear on any monitor.
- // If bRequire is false and document does not appear on any monitor, returns array containing
- // all monitors.
-
- document: function( doc, bRequire )
- {
- return this.mostOverlap( doc.outerDocWindowRect, bRequire ? 1 : undefined );
- },
-
-
- // monitors.largest( minArea )
- // Returns a Monitors array containing the monitor(s) with the greatest area.
- // Returns empty array if minArea is specified and the greatest area is less than that.
-
- largest: function( minArea )
- {
- return this.filter(
- function( m ) { return app.media.priv.rectArea( m.rect ); },
- minArea );
- },
-
-
- // monitors.leastOverlap( rect, maxOverlapArea )
- // Returns a Monitors array containing the monitor(s) which have the least area overlapping rect.
- // Returns empty array if maxOverlapArea is specified and all monitors overlap rect by a greater
- // amount.
-
- leastOverlap: function( rect, maxOverlapArea )
- {
- if( maxOverlapArea !== undefined ) // if undefined must stay undefined (-undefined is NAN)
- maxOverlapArea = -maxOverlapArea;
-
- return this.filter(
- function( m ) { return -app.media.priv.rectIntersectArea( m.rect, rect ); }, maxOverlapArea );
- },
-
-
- // monitors.mostOverlap( rect, minOverlapArea )
- // Returns a Monitors array containing the monitor(s) which have the most area overlapping rect.
- // Returns empty array if minOverlapArea is specified and there is no monitor with at least that
- // much overlap.
-
- mostOverlap: function( rect, minOverlapArea )
- {
- return this.filter(
- function( m ) { return app.media.priv.rectIntersectArea( m.rect, rect ); },
- minOverlapArea );
- },
-
-
- // monitors.nonDocument( doc, bRequire )
- // Returns a Monitors array containing the monitor(s) that display none of, or the least amount
- // of the specified document.
- // If parts of the document appear on every monitor, then returns empty array if bRequire is true
- // or a copy of the original monitors array if bRequire is false.
-
- nonDocument: function( doc, bRequire )
- {
- return this.leastOverlap( doc.outerDocWindowRect, bRequire ? 0 : undefined );
- },
-
-
- // monitors.primary()
- // Returns a Monitors array containing at most one entry, the primary monitor.
-
- primary: function()
- {
- return this.filter(
- function( m ) { return m.isPrimary; },
- 1 );
- },
-
-
- // monitors.secondary()
- // Returns a Monitors array containing all secondary (non-primary) monitors.
-
- secondary: function()
- {
- return this.filter(
- function( m ) { return ! m.isPrimary; },
- 1 );
- },
-
-
- // monitors.tallest( minHeight )
- // Returns a Monitors array containing the monitor(s) with the greatest height.
-
- tallest: function( minHeight )
- {
- return this.filter(
- function( m ) { return m.rect[3] - m.rect[1]; },
- minHeight );
- },
-
-
- // monitors.widest( minWidth )
- // Returns a Monitors array containing the monitor(s) with the greatest width.
-
- widest: function( minWidth )
- {
- return this.filter(
- function( m ) { return m.rect[2] - m.rect[0]; },
- minWidth );
- },
-
-
- }
- // end app.Monitors.prototype
-
-
- // app.media.Players constructor and prototype
-
- app.media.Players = function()
- {
- this.length = 0;
- }
-
-
- app.media.Players.prototype =
- {
-
-
- // Players.clear()
-
- clear: function()
- {
- while( this.length > 0 )
- delete this[ --this.length ];
- },
-
-
- // Players.push( value )
- // Appends a reference to a Player object to the array.
-
- push: function( value )
- {
- this[ this.length++ ] = value;
- },
-
-
- // players = Players.select( args )
- // Filters a Players array based on any of the PlayerInfo properties.
- // The array is not in any particular order.
- // The object argument lists the properties to filter on.
- // String properties (id, name, version) can use either strings or regular expressions.
- // Example: get all players with 'QuickTime' in the id:
- // var p = app.media.getPlayers().select({ id: /QuickTime/ });
- // All specified properties must be present and must match exactly (or must pass regex match).
- // If no properties are specified, all Players in the array will be present in the returned array.
- // If no players in the array match the search criteria, returns an empty Players array.
-
- select: function( args )
- {
- var r = new app.media.Players;
-
- for( var i = 0; i < this.length; i++ )
- {
- var info = this[i]; // Get the PlayerInfo object
- var ok = true;
-
- for( var prop in args ) // check each property that the caller passed in
- {
- if( !( prop in info ) )
- return []; // unknown selection property, probably future PDF version, give up
-
- // Handle either a regular expression or a string, number, or boolean comparison
- if( args[prop].exec ? args[prop].exec(info[prop]) == null : args[prop] != info[prop] )
- {
- ok = false;
- break;
- }
- }
-
- if( ok )
- r.push( info ); // passed all tests, append reference to PlayerInfo
- }
-
- return r;
- },
-
-
- }
- // end app.media.Players.prototype
-
-
- // app.media.MediaPlayer constructor and prototype
-
- app.media.MediaPlayer = function()
- {
- }
-
-
- app.media.MediaPlayer.prototype =
- {
-
-
- // MediaPlayer.open()
-
- open: function()
- {
- var ret;
-
- try
- {
- // Add stock annot events and cross-references only if we open the movie
- if( this.annot )
- {
- app.media.priv.AddStockEventsHelper( this.annot, app.media.getAnnotStockEvents( this.settings.windowType ) );
- this.annot.player = this;
- }
-
- ret = this.privOpen.apply( this, arguments );
- if( ret.code != app.media.openCode.success )
- app.media.removeStockEvents( this );
- }
- catch( e )
- {
- app.media.removeStockEvents( this );
- throw e;
- }
-
- return ret;
- },
-
-
- }
- // end app.media.MediaPlayer.prototype
-
-
- // Determine whether any media playback is allowed and return true if it is.
- // If playback is not allowed, then alert the user and return false.
-
- // bCanPlay = app.media.canPlayOrAlert({ doc: Doc });
-
- app.media.canPlayOrAlert = function( args )
- {
- var canPlay = args.doc.media.canPlay;
- if( canPlay.yes )
- return true; // Playback is allowed
-
- app.media.alert( 'CannotPlay', args, { canPlayResult: canPlay } );
-
- return false;
- }
-
-
- // Return a settings object to play a rendition, if all playback requirements are met.
- // Otherwise return settings to "play" alt text, if showAltText and showEmptyAltText allow and
- // alt text is available, or else return null.
-
- // settings = app.media.getRenditionSettings({
- // doc: Doc,
- // settings: MediaSettings, /* Optional, shallow-copied into returned settings */
- // rendition: MediaRendition or RenditionList,
- // showAltText: Boolean, /* Optional, default = false */
- // showEmptyAltText: boolean, /* Optional, default = false */
- // fromUser: boolean /* Optional, default = false */
- // });
-
- app.media.getRenditionSettings = function( args )
- {
- var settings;
-
- var selection = args.rendition.select( true );
- if( selection.rendition )
- {
- try
- {
- // Get playback settings from rendition - throws on failure, never returns null
- settings = selection.rendition.getPlaySettings( true );
- settings.players = selection.players;
- app.media.priv.copyProps( args.settings, settings ); // copy the user's settings
-
- return settings;
- }
- catch( e )
- {
- // FNF or open failure? Rethrow the exception unless we can handle it here
- if( e.name != "RaiseError" )
- throw e;
-
- if( e.raiseSystem != app.media.raiseSystem.fileError )
- throw e;
-
- if( e.raiseCode != app.media.raiseCode.fileNotFound &&
- e.raiseCode != app.media.raiseCode.fileOpenFailed )
- throw e;
-
- app.media.alert( 'FileNotFound', args, { fileName: selection.rendition.fileName } );
- }
- }
- else // no rendition in selection
- {
- app.media.alert( 'SelectFailed', args, { selection: selection } );
- }
-
- // Did we fail after finding a rendition? If so, use its alt text if allowed
- return app.media.getAltTextSettings( args, selection );
- }
-
-
- // Return the first media rendition in a rendition list, or null if there is no match.
-
- app.media.getFirstRendition = function( list )
- {
- for( var i = 0; i < list.length; i++ )
- {
- if( list[i].rendition.type == app.media.renditionType.media )
- return list[i].rendition;
- }
-
- return null;
- }
-
-
- // Return a settings object with a data property to play a URL.
- // Any properties in args.settings are shallow-copied into the returned settings object.
-
- // settings = app.media.getURLSettings({
- // URL: String, /* required */
- // mimeType: String, /* optional */
- // settings: MediaSettings /* optional */
- // });
-
- app.media.getURLSettings = function( args )
- {
- // Get a data object for the URL and MIME type
- var settings =
- {
- data: app.media.getURLData( args.URL, args.mimeType )
- }
-
- app.media.priv.copyProps( args.settings, settings ); // copy the user's settings
-
- return settings;
- }
-
-
- // Return an alt text settings object for a selection, or null if there's no alt text available,
- // or if alt text should not be used in this situation.
- // Arguments are the same as app.media.getRenditionSettings().
- // Any properties in args.settings are shallow-copied into the returned settings object.
-
- app.media.getAltTextSettings = function( args, selection )
- {
- if( ! args.showAltText )
- return null;
-
- var rendition = selection.rendition || app.media.getFirstRendition( selection.rejects );
- if( ! rendition )
- return null;
-
- settings = rendition.getPlaySettings( false );
- app.media.priv.copyProps( args.settings, settings ); // copy the user's settings
-
- // Use alt text only when docked (compute default windowType first if needed)
- if( ! settings.windowType )
- settings.windowType = app.media.priv.computeDefaultWindowType( args, settings );
- if( settings.windowType != app.media.windowType.docked )
- return null;
-
- // Get the alt text, or default text if none specified and showEmptyAltText is true
- var text = rendition.altText;
- if( text.length == 0 )
- {
- if( ! args.showEmptyAltText )
- return null;
-
- text = app.media.priv.getString( "IDS_ERROR_NO_ALT_TEXT_SPECIFIED" );
- }
-
- settings.data = app.media.getAltTextData( text );
-
- settings.players = [ app.media.priv.altTextPlayerID ];
-
- return settings;
- }
-
-
- // Add the standard event listeners to a player.
- // If annot is specified, set up so when the player is opened, the annot will have its standard
- // event listeners attached. The player.annot and annot.player cross-references will be
- // installed at the same time.
- // The player must have a settings property. In the settings property, windowType and visible
- // are the only values used here. The visible property may be modified here and restored later
- // in the afterReady listener.
-
- app.media.addStockEvents = function( player, annot )
- {
- if( player.stockEvents )
- return; // already added stock events
-
- app.media.priv.AddStockEventsHelper( player, app.media.getPlayerStockEvents( player.settings ) );
-
- if( annot )
- {
- // remember that annot needs stock events attached when player is opened
- player.annot = annot;
- }
- }
-
-
- // Private function to add stock events to an object. Saves a reference to the original stock
- // events in object.stockEvents for later removal. object.stockEvents must not be modified after
- // it is saved here, or removal will not work correctly.
-
- app.media.priv.AddStockEventsHelper = function( object, events )
- {
- object.stockEvents = events;
-
- if( ! object.events )
- object.events = new app.media.Events;
-
- object.events.add( events )
- }
-
-
- // Remove the standard event listeners and cross-references from a player and its associated annot.
- // Does nothing if no stock events (never added or already removed).
-
- app.media.removeStockEvents = function( player )
- {
- if( ! player || ! player.stockEvents )
- return;
-
- function removeProps( object )
- {
- if( object.events )
- {
- object.events.remove( object.stockEvents );
- delete object.stockEvents;
- }
- }
-
- removeProps( player );
-
- if( player.annot )
- {
- if( player.annot.stockEvents )
- removeProps( player.annot );
-
- delete player.annot.player;
- delete player.annot;
- }
- }
-
-
- // Return floating window rect for a doc, floating params, monitor to play on, and
- // optional array containing the dimensions [l,r,t,b] of any additional controller UI.
-
- // NOTE: this method is called from both JS and C++ code, so do not change its signature
- // without great care!
-
- app.media.computeFloatWinRect = function( doc, floating, whichMonitor, uiSize )
- {
- // Figure out rect in virtual desktop space that we are positioning relative to
- var overRect;
- switch( floating.over )
- {
- default:
- case app.media.over.pageWindow:
- overRect = doc.pageWindowRect;
- break;
-
- case app.media.over.appWindow:
- // Inner more consistent placement because no borders etc.
- overRect = doc.innerAppWindowRect;
- break;
-
- case app.media.over.desktop:
- overRect = app.monitors.desktop()[0].rect;
- break;
-
- case app.media.over.monitor:
- overRect = app.monitors.select( whichMonitor, doc )[0].workRect;
- break;
- }
-
- // Get the border sizes for this window
- var border = app.media.getWindowBorderSize( floating );
-
- // Align floating window with overRect according to align, using the
- // floating window rect plus the border sizes
- rect = app.media.priv.rectAlign(
- overRect, floating.align,
- floating.width + border[0] + border[2],
- floating.height + border[1] + border[3] );
-
- // Grow the rect by the UI size (if any)
- if( uiSize )
- rect = app.media.priv.rectGrow( rect, uiSize );
-
- return rect;
- }
-
-
- // Return a new instance of the standard player events for the given settings.
- // In the settings property, windowType and visible are the only values used here.
- // The settings.visible property may be modified here, and restored later in an afterReady event.
- // If you call this method directly and there is an annot associated with the player, you must
- // set player.annot and annot.player as shown in addStockEvents().
-
- app.media.getPlayerStockEvents = function( settings )
- {
- var events = new app.media.Events;
-
- if( app.media.trace )
- events.add( app.media.getPlayerTraceEvents() );
-
- events.add(
- {
- onClose: function( e )
- {
- var annot = e.target.annot;
-
- app.media.removeStockEvents( e.target ); // must do this before setFocus call below
-
- if( annot )
- {
- annot.extFocusRect = null;
-
- // If docked screen had focus when closed, and further playback is allowed,
- // put focus back on annot
- if( e.media.hadFocus &&
- e.target.settings.windowType == app.media.windowType.docked &&
- e.media.doc.media.canPlay.yes )
- {
- // Allow async setFocus since we're in event method
- // Does not fire stock annot Focus event because stock events removed above
- annot.setFocus( true );
- }
- }
- },
-
- afterDone: function( e )
- {
- e.target.close( app.media.closeReason.done ); // fires Close and may fire Blur
- },
-
- afterError: function( e )
- {
- app.media.alert( 'PlayerError', e.target.args, { errorText: e.media.text } );
- e.target.close( app.media.closeReason.error ); // fires Close and may fire Blur
- },
-
- afterEscape: function( e )
- {
- e.target.close( app.media.closeReason.uiScreen ); // fires Close and may fire Blur
- }
- });
-
- // Add player event listeners for specific window types
- switch( settings.windowType )
- {
- case app.media.windowType.docked:
- {
- events.add(
- {
- onGetRect: function( e )
- {
- if( e.target.annot )
- {
- // Get the annot's rectangle and expand it to include any
- // visible media player user interface. Return this rectangle in
- // the event object, and also use it as the annot's focus rect.
- e.target.annot.extFocusRect = e.media.rect =
- app.media.priv.rectGrow(
- e.target.annot.innerDeviceRect, e.target.uiSize );
- }
- },
-
- onBlur: function( e )
- {
- if( e.target.annot )
- e.target.annot.alwaysShowFocus = false;
- },
-
- onFocus: function( e )
- {
- if( e.target.annot )
- e.target.annot.alwaysShowFocus = true;
- }
- });
- }
- break;
-
- case app.media.windowType.floating:
- {
- // Need either a rect or a width and height
- if ( !settings.floating.rect && ( !settings.floating.width || !settings.floating.height ) )
- app.media.priv.throwBadArgs(); // throw exception
-
- if( settings.visible === undefined )
- settings.visible = app.media.defaultVisible;
-
- if( settings.visible )
- {
- // Hide floating window while it's being created, then show it after the
- // controller dimensions are available
- settings.visible = false;
-
- events.add(
- {
- afterReady: function( e )
- {
- var floating = e.target.settings.floating;
- var rect = floating.rect; // take user-provided rect, or calculate one
- if( ! rect )
- {
- rect = app.media.computeFloatWinRect( e.media.doc, floating,
- e.target.settings.monitorType, e.target.uiSize );
- }
- else
- {
- // Grow passed rect by UI size
- rect = app.media.priv.rectGrow( rect, e.target.uiSize );
- }
-
- // Are we supposed to move the window onscreen if it is offscreen?
- if( floating.ifOffScreen == app.media.ifOffScreen.forceOnScreen )
- {
- // Make sure window rect is totally onscreen, NOP if onscreen already
- rect = app.media.constrainRectToScreen( rect,
- app.media.priv.rectAnchorPt( rect, floating.align ) );
- }
-
- // Set the outer rect
- e.target.outerRect = rect;
-
- // Show the window and give it the focus
- e.target.visible = true;
- e.target.setFocus(); // fires Focus event
- }
- });
- }
- }
- break;
- }
-
- return events;
- }
-
-
- // Return a new instance of the debug trace event listeners for a player.
-
- app.media.getPlayerTraceEvents = function()
- {
- return new app.media.Events(
- {
- onEveryEvent: function( e )
- {
- if( e.media.id != 'GetRect' ) // cannot trace inside onGetRect, it can hang Acrobat
- app.media.priv.trace( 'player event: on' + e.media.id );
- },
-
- afterEveryEvent: function( e )
- {
- app.media.priv.trace( 'player event: after' + e.media.id );
- },
-
- onScript: function( e )
- {
- app.media.priv.trace( "player onScript('" + e.media.command + "','" + e.media.param + "')" );
- },
-
- afterScript: function( e )
- {
- app.media.priv.trace( "player afterScript('" + e.media.command + "','" + e.media.param + "')" );
- },
-
- onStatus: function( e )
- {
- app.media.priv.trace( "player onStatus: " +
- ( e.media.progress >= 0 ? e.media.progress + "/" + e.media.total + ", " : "" ) +
- " status code: " + e.media.code + ": '" + e.media.text + "'" );
- },
-
- afterStatus: function( e )
- {
- app.media.priv.trace( "player afterStatus: " +
- ( e.media.progress >= 0 ? e.media.progress + "/" + e.media.total + ", " : "" ) +
- " status code: " + e.media.code + ": '" + e.media.text + "'" );
- }
- });
- }
-
-
- // Return a new instance of the standard annot events:
- // For a docked player, handle Focus and Blur to give the player the focus instead of the annot.
- // For any type of player, close the player on Destroy.
-
- app.media.getAnnotStockEvents = function( windowType )
- {
- var events = new app.media.Events;
-
- if( app.media.trace )
- events.add( app.media.getAnnotTraceEvents() );
-
- events.add(
- {
- onDestroy: function( e )
- {
- if( e.target.player )
- {
- // NOP if not open
- // fires Close and possibly other events
- e.target.player.close( app.media.closeReason.docChange );
- }
- },
- } );
-
- if( windowType == app.media.windowType.docked )
- {
- events.add(
- {
- onFocus: function( e )
- {
- // If player is open, give it the focus. This event could be fired while doing
- // UI inside player.open() or the like.
- if( e.target.player.isOpen )
- {
- e.target.player.setFocus(); // fires Focus for player and Blur for annot
- }
-
- // Prevent any action from being fired for the Focus event, since focus
- // has already been removed inside setFocus(). If setFocus() not called,
- // we're in the process of some sort of UI and we don't want random actions
- // firing either.
- e.stopDispatch = true;
- },
-
- onBlur: function( e )
- {
- // As with the Focus event, prevent any action from being fired for the Blur event.
- // This also prevents anybody after us from seeing onBlur before onFocus because
- // of our setFocus() call within onFocus.
- e.stopDispatch = true;
- }
- });
- }
-
- return events;
- }
-
-
- // Return a new instance of the debug trace event listeners for an annot.
-
- app.media.getAnnotTraceEvents = function()
- {
- return new app.media.Events(
- {
- onEveryEvent: function( e )
- {
- app.media.priv.trace( 'annot event: on' + e.media.id );
- },
-
- afterEveryEvent: function( e )
- {
- app.media.priv.trace( 'annot event: after' + e.media.id );
- }
- });
- }
-
-
- // Make a shallow copy of args and run our "Do What I Mean" logic on it, to fill in default values
- // used in app.media.createPlayer(). The original args object is not modified, and changes made
- // later to the copy do not affect the original. Objects inside args are shared between the
- // original and the copy, and changes made inside these objects are visible from both args objects.
-
- // If args.annot or args.rendition are not defined, gets them from current event object.
- // If args.doc is not defined, gets it from args.annot or args.rendition.
- // Throws exception on failure.
-
- app.media.argsDWIM = function( args )
- {
- if( args && args.privDWIM )
- return args; // already did a DWIM copy
-
- args = app.media.priv.copyProps( args );
- args.privDWIM = true;
-
- // Use annot and rendition passed in parameters, or get them from event object
- if( event && event.action )
- {
- if( ! args.annot )
- args.annot = event.action.annot; // TODO: it'd be nice to verify type of annot here...
-
- if( ! args.rendition )
- args.rendition = event.action.rendition;
- }
-
- // Get doc from rendition or annot if args.doc not provided
- if( ! args.doc )
- {
- if( args.rendition && args.annot )
- if( args.rendition.doc != args.annot.doc )
- app.media.priv.throwBadArgs();
-
- if( args.rendition )
- args.doc = args.rendition.doc;
- else if( args.annot )
- args.doc = args.annot.doc;
- }
-
- // If fromUser is not specified, use !! to set it to true or false based on event name
- if( args.fromUser === undefined )
- args.fromUser = !!( event && event.name && ! app.media.pageEventNames[event.name] );
-
- if( args.showAltText === undefined )
- args.showAltText = true;
-
- if( args.showEmptyAltText === undefined )
- args.showEmptyAltText = ! args.fromUser;
-
- return args;
- }
-
-
- // Private function for app.media.priv.createPlayer().
-
- app.media.priv.createPlayer = function( args )
- {
- app.media.priv.trace( "app.media.priv.createPlayer" );
-
- if( ! args.doc )
- app.media.priv.throwBadArgs(); // doc is required
-
- if( ! app.media.canPlayOrAlert( args ) )
- return null; // playback is not allowed, user has been notified
-
- if( args.annot && args.annot.player )
- {
- args.annot.player.close( app.media.closeReason.play ); // fires events
- // args.annot.player presumably is null now, unless onClose didn't null it out.
- // Cannot create new player in onClose so shouldn't have any issues there
- }
-
- var player = args.doc.media.newPlayer({ args: args });
-
- // Get a settings object for either a URL or a rendition, whichever was provided
- // URL wins if both present.
- player.settings =
- args.URL ? app.media.getURLSettings( args ) :
- args.rendition ? app.media.getRenditionSettings( args ) :
- app.media.priv.throwBadArgs(); // need either rendition or URL
-
- if( ! player.settings )
- return null; // user has been notified already
-
- // If no windowType, compute default value
- if( ! player.settings.windowType )
- player.settings.windowType = app.media.priv.computeDefaultWindowType( args, player.settings );
-
- // If windowType couldn't be computed, throw
- if( ! player.settings.windowType )
- app.media.priv.throwBadArgs();
-
- switch( player.settings.windowType )
- {
- case app.media.windowType.docked:
- {
- if( player.settings.page === undefined )
- {
- if( ! args.annot )
- app.media.priv.throwBadArgs(); // need either an annot or a page number
-
- player.settings.page = args.annot.page;
- }
- }
- break;
-
- case app.media.windowType.fullScreen:
- {
- player.settings.monitor = app.monitors.select( player.settings.monitorType, args.doc );
- }
- break;
- }
-
- // Add any stock events to the player (and set up to add them to the annot later if needed).
- // Even if the player is never opened, no need to remove them since they won't get used.
- if( ! args.noStockEvents )
- app.media.addStockEvents( player, args.annot );
-
- if( args.events )
- {
- if( ! player.events )
- player.events = new app.media.Events;
-
- player.events.add( args.events ); // Add caller's custom events
- }
-
- return player;
- }
-
-
- // Private function to get default windowType:
- // docked if there is an annot,
- // floating if there is no annot and there is a floating settings obj,
- // undefined otherwise
-
- app.media.priv.computeDefaultWindowType = function( args, settings )
- {
- var retWT;
- if( args.annot )
- retWT = app.media.windowType.docked;
- else if( settings.floating )
- retWT = app.media.windowType.floating;
-
- return retWT;
- }
-
- // Display an alert, given an alert name (e.g. FileNotFound), createArgs object (the type of object
- // processed by app.media.argsDWIM()), and optional specificArgs object which contains information
- // specific to the alert. The only property that MUST be present in createArgs is doc.
- // NOTE: This implementation of app.media.alert is for private use within media.js only.
- // Do not use it in PDF files! It will be changed in a future release.
-
- app.media.alert = function( name, createArgs, specificArgs )
- {
- var alertArgs = { name: name, createArgs: createArgs, alerterData: {} }
- app.media.priv.copyProps( specificArgs, alertArgs );
-
- // Set the default doc alerts if they are not already set
- if( !( 'alerts' in createArgs.doc.media ) )
- createArgs.doc.media.alerts = new app.media.Alerts;
-
- // If there are no alerts, use the doc alerts
- var curAlerts = createArgs.alerts;
- if ( !curAlerts )
- curAlerts = createArgs.doc.media.alerts;
-
- // Keep on dispatching up parent chain of alerters until finally handled or no more alerters
- var gpMN = 'getParent';
- var handled = false;
- var lastAlerts = null;
- while ( curAlerts && !handled )
- {
- lastAlerts = curAlerts;
-
- // dispatch
- handled = dispatchAlert( curAlerts );
- if ( !handled )
- {
- // not handled, try to move to parent alerters
- var newAlerts = null;
- if ( gpMN in curAlerts )
- {
- newAlerts = curAlerts[ gpMN ]( alertArgs );
- if ( newAlerts )
- {
- // setup so parent alter can see child's alertArgs if it wants to
- // and to give reference to child alerter
- var newCI = {};
- newCI.alerts = curAlerts;
- newCI.alerterData = alertArgs.alerterData;
- newCI.next = alertArgs.childInfo;
- alertArgs.childInfo = newCI;
-
- // Clear stuff out so new alerter gets fresh shot
- alertArgs.alerterData = {};
- delete alertArgs.stop;
- delete alertArgs.sendToParent;
- }
- }
-
- curAlerts = newAlerts;
- }
- }
- // In inverse order, call afterParent as needed (not for top of course)
- curAlerts = lastAlerts;
- while ( alertArgs.childInfo ) {
-
- var newPI = {};
- newPI.alerts = curAlerts;
- newPI.alerterData = alertArgs.alerterData;
- newPI.next = alertArgs.parentInfo;
- alertArgs.parentInfo = newPI;
-
- alertArgs.alerterData = alertArgs.childInfo.alerterData;
- curAlerts = alertArgs.childInfo.alerts;
- alertArgs.childInfo = alertArgs.childInfo.next;
-
- var method = curAlerts[ 'afterParent' ];
- if( typeof method == 'function' )
- method.call( curAlerts, alertArgs, handled );
- }
-
-
-
- // Local function to dispatch to an alerts object and return true if handled
- function dispatchAlert( alerts )
- {
- var other = 'alertOther';
- var alertMtdName = 'alert' + name;
-
- if( alertMtdName in alerts || other in alerts )
- {
- callMethod( 'before' );
-
- if ( ! callMethod( alertMtdName ) )
- callMethod( other );
-
- callMethod( 'after' );
-
- return !alertArgs.sendToParent;
- }
-
- // Local function to call a single alerts method and return true if it is present
- function callMethod( methodName )
- {
- if( methodName in alerts && ! alertArgs.stop && !alertArgs.sendToParent )
- {
- var method = alerts[methodName];
- if( typeof method == 'function' )
- method.call( alerts, alertArgs );
-
- return true;
- }
- }
-
- return false;
- }
- }
-
-
- // app.media.Alerts - constructs a stock alerts object
- // If an alert is the result of a user-triggered event, always show alert with no checkbox.
- // If not user triggered, we include a "don't show again" checkbox and save its state,
- // but don't show the alert if the user has previously checked that box in this document.
- // The "don't show again" state is shared by all alerts (not tracked for each different type).
- // NOTE: This implementation of app.media.Alerts is for private use within media.js only.
- // Do not use it in PDF files! It will be changed in a future release.
-
- app.media.Alerts = function()
- {
- return {
-
- before: function( alertArgs )
- {
- if( ! alertArgs.createArgs.fromUser && this.skip )
- alertArgs.stop = true;
- },
-
- after: function( alertArgs )
- {
- if( ! alertArgs.createArgs.fromUser )
- this.skip = alertArgs.alerterData.skip;
- },
-
- alertCannotPlay: function( alertArgs )
- {
- var canPlay = alertArgs.canPlayResult;
- if( ! canPlay.canShowUI )
- alertArgs.stop = true; // Playback not allowed and may not show UI -- suppress after method
- else
- {
- if( canPlay.no.authoring )
- {
- // Playback is not allowed while authoring
- alertArgs.alerterData.skip = this.privOK( alertArgs.createArgs, "IDS_PLAYBACK_DISALLOWED_WHILE_AUTHORING" );
- }
- else if( canPlay.no.security )
- {
- // User prefs say "no multimedia"
- alertArgs.alerterData.skip = this.privOK( alertArgs.createArgs, "IDS_PLAYBACK_DISALLOWED_CONFIGURATION" );
- }
- else
- {
- // as of now, will not get here -- should probably put up generic error though just in case...
- }
- }
- },
-
- alertException: function( alertArgs )
- {
- // No don't show again checkbox for exceptions
- app.alert( alertArgs.error.message );
- },
-
- alertFileNotFound: function( alertArgs )
- {
- alertArgs.alerterData.skip = app.media.alertFileNotFound( alertArgs.createArgs.doc, alertArgs.fileName, ! alertArgs.createArgs.fromUser );
- },
-
- alertOpen: function( alertArgs )
- {
- // TODO - show UI here, except if already shown (e.g. for failPlayerSecurityPrompt)
- // may want more info from open (e.g. to tell us if UI was shown already)
- },
-
- alertPlayerError: function( alertArgs )
- {
- alertArgs.alerterData.skip = this.privOK( alertArgs.createArgs, "IDS_JS_PLAYBACK_ERROR", alertArgs.errorText );
- },
-
- alertSelectFailed: function( alertArgs )
- {
- alertArgs.alerterData.skip = app.media.alertSelectFailed(
- alertArgs.createArgs.doc, alertArgs.selection.rejects, ! alertArgs.createArgs.fromUser, alertArgs.createArgs.fromUser );
- },
-
- alertOther: null, // ignore any other alerts
-
- // Display a simple "OK" alert with optional "don't show again" checkbox.
- // Return checkbox result value, or undefined if no checkbox.
- privOK: function( createArgs, idMsg, strAppend )
- {
- var a = { cMsg: app.media.priv.getString(idMsg) + ( strAppend || '' ),
- nIcon: 0, nType: 0, oDoc: createArgs.doc };
-
- if( ! createArgs.fromUser )
- a.oCheckbox = { cMsg: app.media.priv.getString("IDS_DONOT_SHOW_AGAIN_DOC"),
- bInitialValue: false };
-
- app.alert( a );
-
- if( a.oCheckbox )
- return a.oCheckbox.bAfterValue;
- },
- }
- }
-
-
- // Debugging code
-
- app.media.priv.dumpObject = function( obj, str, bValues )
- {
- if( ! str )
- str = "";
- else
- str += " ";
-
- str += "(" + obj + ") [" + typeof(obj) + "]\n";
-
- for( var prop in obj )
- str += " " + prop + ( bValues ? ": " + obj[prop] : "" ) + "\n";
-
- app.media.priv.trace( str );
- }
-
-
- app.media.priv.dumpNames = function( obj, str )
- {
- app.media.priv.dumpObject( obj, str, false );
- }
-
-
- app.media.priv.dumpValues = function( obj, str )
- {
- app.media.priv.dumpObject( obj, str, true );
- }
-
-
- app.media.priv.dumpArray = function( array, str )
- {
- if( ! str )
- str = "";
- else
- str += " ";
-
- str += "(" + array + ") [" + typeof(array) + "]\n{ ";
-
- /*
- if( array.length )
- app.alert( "has length" );
- else
- app.alert( "no length" );
- */
-
- for( var i = 0; i < array.length; i++ )
- str += array[i] + ( i < array.length - 1 ? ", " : " }" );
-
- app.media.priv.trace( str );
- }
-
-
- app.media.priv.trace = function( str )
- {
- if( app.media.trace )
- console.println( str );
- }
-
-
- // Private function called in a rendition action. Puts up UI on failure.
-
- app.media.priv.stopAnnotPlayer = function()
- {
- try
- {
- annot = event.action.annot;
- if( annot.player )
- annot.player.close( app.media.closeReason.stop ); // fires Close event, may fire Blur
- }
- catch( e )
- {
- app.alert( e.message );
- }
- }
-
-
- // Private function called in a rendition action. NOP if already paused. Puts up UI on failure.
-
- app.media.priv.pauseAnnotPlayer = function()
- {
- try
- {
- annot = event.action.annot;
- if( annot.player )
- annot.player.pause();
- }
- catch( e )
- {
- app.alert( e.message );
- }
- }
-
-
- // Private function called in a rendition action. NOP if not paused. Puts up UI on failure.
-
- app.media.priv.resumeAnnotPlayer = function()
- {
- try
- {
- annot = event.action.annot;
- if( annot.player )
- annot.player.play();
- }
- catch( e )
- {
- app.alert( e.message );
- }
- }
-
-
- // Enumerate and copy properties from one object to another object or to a new object, and
- // return the resulting object. This is a shallow copy: any objects referenced by the from
- // object will now be referenced by both the from and to objects.
-
- app.media.priv.copyProps = function( from, to )
- {
- if( ! to )
- to = {};
-
- if( from )
- {
- for( var name in from )
- to[name] = from[name];
- }
-
- return to;
- }
-
-
- // Rectangle utility functions
-
-
- // Tables used by rectAlign to map app.media.align values to window positioning multipliers.
-
- // see app.media.align.* un tl tc tr cl c cr bl bc br
- app.media.priv.xPosTable = [ 0.5, 0.0, 0.5, 1.0, 0.0, 0.5, 1.0, 0.0, 0.5, 1.0 ];
- app.media.priv.yPosTable = [ 0.5, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0 ];
-
-
- // Given an app.media.align value and a desired width and height, return a rectangle
- // aligned with rect according to the align value.
-
- app.media.priv.rectAlign = function( rect, align, width, height )
- {
- if( ! align )
- align = app.media.align.center;
-
- var x = rect[0] + ( rect[2] - rect[0] - width ) * app.media.priv.xPosTable[align];
- var y = rect[1] + ( rect[3] - rect[1] - height ) * app.media.priv.yPosTable[align];
-
- return [ x, y, x + width, y + height ];
- }
-
-
- // Given an app.media.align value and a rect, return an anchor point
-
- app.media.priv.rectAnchorPt = function( rect, align )
- {
- if( ! align )
- align = app.media.align.center;
-
- var x = rect[0] + ( ( rect[2] - rect[0] ) * app.media.priv.xPosTable[align] );
- var y = rect[1] + ( ( rect[3] - rect[1] ) * app.media.priv.yPosTable[align] );
-
- return [ x, y ];
- }
-
-
- // Returns the area of a rectangle. If rect is empty, 0 is returned.
-
- app.media.priv.rectArea = function( rect )
- {
- if( app.media.priv.rectIsEmpty( rect ) ) // empty rect might be [10,10,0,0] so cannot just do the math
- return 0;
- else
- return ( rect[2] - rect[0] ) * ( rect[3] - rect[1] );
- }
-
-
- // Returns rect grown by size, an array of four values giving the amount to grow each edge.
- // Returned rect is a new object, input rect is not modified.
-
- app.media.priv.rectGrow = function( rect, size )
- {
- return [
- rect[0] - size[0],
- rect[1] - size[1],
- rect[2] + size[2],
- rect[3] + size[3]
- ];
- }
-
-
- // Returns the intersection of two rectangles.
- // If either input rect is empty, or there is no intersection, [0,0,0,0] is returned.
- // Returned rect is a new object, input rects are not modified.
-
- app.media.priv.rectIntersect = function( rectA, rectB )
- {
- var newRect;
-
- if( app.media.priv.rectIsEmpty(rectA) || app.media.priv.rectIsEmpty(rectB) )
- {
- newRect = [ 0, 0, 0, 0 ];
- }
- else
- {
- newRect =
- [
- Math.max( rectA[0], rectB[0] ),
- Math.max( rectA[1], rectB[1] ),
- Math.min( rectA[2], rectB[2] ),
- Math.min( rectA[3], rectB[3] )
- ];
-
- if( app.media.priv.rectIsEmpty( newRect ) )
- newRect = [ 0, 0, 0, 0 ];
- }
-
- return newRect;
- }
-
-
- // Take the intersection of two rectangles and return its area.
-
- app.media.priv.rectIntersectArea = function( rectA, rectB )
- {
- return app.media.priv.rectArea( app.media.priv.rectIntersect( rectA, rectB ) );
- }
-
-
- // Is a rectangle empty?
-
- app.media.priv.rectIsEmpty = function( rect )
- {
- return ! rect || rect[0] >= rect[2] || rect[1] >= rect[3];
- }
-
-
- // Returns new object that contains same values (not custom values) as input rect.
-
- app.media.priv.rectCopy = function( rect )
- {
- return [ rect[0], rect[1], rect[2], rect[3] ];
- }
-
-
- // Returns the union of two rectangles.
- // Returned rect is a new object, input rects are not modified.
-
- app.media.priv.rectUnion = function( rectA, rectB )
- {
- return(
- app.media.priv.rectIsEmpty(rectA) ? app.media.priv.rectCopy( rectB ) :
- app.media.priv.rectIsEmpty(rectB) ? app.media.priv.rectCopy( rectA ) :
- [
- Math.min( rectA[0], rectB[0] ),
- Math.min( rectA[1], rectB[1] ),
- Math.max( rectA[2], rectB[2] ),
- Math.max( rectA[3], rectB[3] )
- ]
- );
- }
-
-
- // Get a resource string
-
- app.media.priv.getString = function( idString )
- {
- return app.getString( 'Multimedia', idString );
- }
-
-
- // Return a value or a default if the value is undefined
-
- app.media.priv.valueOr = function( value, def )
- {
- return value !== undefined ? value : def;
- }
-
-
- // Private constants
-
- app.media.priv.altTextPlayerID = 'vnd.adobe.swname:ADBE_AltText';
-
-
- // End of media.js
-
-
-