diff --git a/README.md b/README.md index 981ba4a..03392e0 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ Supports all browsers supported by React. Depends on [`Object.is()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Polyfill_for_non-ES6_browsers) and [`Object.assign()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill) -``` +Use [SWFPlayerVersion](https://github.com/syranide/swf-player-version) to determine SWF Player support. + +```jsx ``` -```js -// Test if Flash Player version is supported and output the actual version. -if (ReactSWF.isFPVersionSupported('10.0')) { - alert('Flash Player ' + ReactSWF.getFPVersion() + ' is installed'); +```jsx +const SWF_ID_PREFIX = '__MyExternalInterfaceExample_SWFID_'; +const SWF_CALL_NAME_PREFIX = '__MyExternalInterfaceExample_SWFCall_'; + +let nextUID = 0; + +class MyExternalInterfaceExample extends React.Component { + constructor(props) { + super(props); + + // For most purposes nextUID is sufficient. However, if you rely on + // non-trivial server rendering you must generate deterministic UIDs per + // React root to avoid markup mismatch. + this._uid = nextUID++; + + window[SWF_CALL_NAME_PREFIX + this._uid] = this.handleSWFCall.bind(this); + } + + componentWillUnmount() { + delete window[SWF_CALL_NAME_PREFIX + this._uid]; + } + + handleSWFCall() { + // Beware; SWF calls are executed in the context of SWF Player. + console.log('SWFCall', arguments); + return 'foobar'; + } + + invokeSWFMyCallback(arg) { + // Beware; SWF Player does not sufficiently escape serialized arguments. + return this._swfPlayerNode.myCallback(arg); + } + + render() { + // Globally unique ID is required for IE<11 for ExternalInterface callbacks. + return ( + this._swfPlayerNode = c} + id={SWF_ID_PREFIX + this._uid} + flashVars={{myCallbackName: SWF_CALL_NAME_PREFIX + this._uid}} + /> + ); + } } ``` -```js -// ExternalInterface callbacks are invoked on the DOM node as usual. -var returnValue = ref.getFPDOMNode().myEICallback(...); -``` ## Breaking changes +#### 0.13.0 + +* `getFPVersion` and `isFPVersionSupported` forked to [SWFPlayerVersion](https://github.com/syranide/swf-player-version) and dropped from ReactSWF. Replace `ReactSWF.getFPVersion => SWFPlayerVersion.get` and `ReactSWF.isFPVersionSupported => SWFPlayerVersion.isSupported`. + #### 0.12.3 -* Depends on `Object.assign()`, [polyfills are available.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill) +* Depends on `Object.assign()`, [polyfills are available](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill). #### 0.11.0 @@ -76,24 +118,6 @@ Detailed explanation of most properties found at [[Flash OBJECT and EMBED tag at ## API -#### Static methods - -``` -getFPVersion() - returns {?string} 'X.Y.Z'-version or null. - - Returns installed Flash Player version. Result is cached. - Must not be called in a non-browser environment. -``` -``` -isFPVersionSupported(versionString) - versionString {string} 'X.Y.Z', 'X.Y' or 'X'. - returns {boolean} true if supported. - - Returns if installed Flash Player meets version requirement. - Must not be called in a non-browser environment. -``` - #### Instance methods ``` diff --git a/bower.json b/bower.json index 4a4ab7d..bb3c58a 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "react-swf", - "version": "0.12.3", + "version": "0.13.0", "license": "MIT", "description": "Shockwave Flash Player component for React", "authors": ["Andreas Svensson "], @@ -12,7 +12,7 @@ "url": "https://github.com/syranide/react-swf" }, "dependencies": { - "react": "^0.14.0-beta1" + "react": "^0.14.0" }, "keywords": [ "react", diff --git a/npm-react-swf/README.md b/npm-react-swf/README.md index 1dfe201..e086230 100644 --- a/npm-react-swf/README.md +++ b/npm-react-swf/README.md @@ -6,7 +6,9 @@ Supports all browsers supported by React. Depends on [`Object.is()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Polyfill_for_non-ES6_browsers) and [`Object.assign()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill) -``` +Use [SWFPlayerVersion](https://github.com/syranide/swf-player-version) to determine SWF Player support. + +```jsx ``` -```js -// Test if Flash Player version is supported and output the actual version. -if (ReactSWF.isFPVersionSupported('10.0')) { - alert('Flash Player ' + ReactSWF.getFPVersion() + ' is installed'); +```jsx +var SWF_ID_PREFIX = '__MyExternalInterfaceExample_SWFID_'; +var SWF_CALL_NAME_PREFIX = '__MyExternalInterfaceExample_SWFCall_'; + +var nextUID = 0; + +class MyExternalInterfaceExample extends React.Component { + constructor(props) { + super(props); + + // For most purposes nextUID is sufficient. However, if you rely on + // non-trivial server rendering you must generate deterministic UIDs per + // React root to avoid markup mismatch. + this._uid = nextUID++; + + window[SWF_CALL_NAME_PREFIX + this._uid] = this.handleSWFCall.bind(this); + } + + componentWillUnmount() { + delete window[SWF_CALL_NAME_PREFIX + this._uid]; + } + + handleSWFCall() { + // Beware; SWF calls are executed in the context of SWF Player. + console.log('SWFCall', arguments); + return 'foobar'; + } + + invokeSWFMyCallback(arg) { + // Beware; SWF Player does not sufficiently escape serialized arguments. + return this._swfPlayerNode.myCallback(arg); + } + + render() { + // Globally unique ID is required for IE<11 for ExternalInterface callbacks. + return ( + this._swfPlayerNode = c} + id={SWF_ID_PREFIX + this._uid} + flashVars={{myCallbackName: SWF_CALL_NAME_PREFIX + this._uid}} + /> + ); + } } ``` -```js -// ExternalInterface callbacks are invoked on the DOM node as usual. -var returnValue = ref.getFPDOMNode().myEICallback(...); -``` ## Breaking changes +#### 0.13.0 + +* `getFPVersion` and `isFPVersionSupported` forked to [SWFPlayerVersion](https://github.com/syranide/swf-player-version) and dropped from ReactSWF. Replace `ReactSWF.getFPVersion => SWFPlayerVersion.get` and `ReactSWF.isFPVersionSupported => SWFPlayerVersion.isSupported`. + #### 0.12.3 -* Depends on `Object.assign()`, [polyfills are available.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill) +* Depends on `Object.assign()`, [polyfills are available](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill). #### 0.11.0 @@ -76,24 +118,6 @@ Detailed explanation of most properties found at [[Flash OBJECT and EMBED tag at ## API -#### Static methods - -``` -getFPVersion() - returns {?string} 'X.Y.Z'-version or null. - - Returns installed Flash Player version. Result is cached. - Must not be called in a non-browser environment. -``` -``` -isFPVersionSupported(versionString) - versionString {string} 'X.Y.Z', 'X.Y' or 'X'. - returns {boolean} true if supported. - - Returns if installed Flash Player meets version requirement. - Must not be called in a non-browser environment. -``` - #### Instance methods ``` diff --git a/npm-react-swf/package.json b/npm-react-swf/package.json index b226cef..41970d4 100644 --- a/npm-react-swf/package.json +++ b/npm-react-swf/package.json @@ -1,6 +1,6 @@ { "name": "react-swf", - "version": "0.12.3", + "version": "0.13.0", "license": "MIT", "description": "Shockwave Flash Player component for React", "author": "Andreas Svensson ", @@ -12,7 +12,7 @@ "url": "https://github.com/syranide/react-swf" }, "peerDependencies": { - "react": "^0.14.0-beta1" + "react": "^0.14.0" }, "keywords": [ "react", diff --git a/npm-react-swf/react-swf.js b/npm-react-swf/react-swf.js index 9e37cd1..b12e399 100644 --- a/npm-react-swf/react-swf.js +++ b/npm-react-swf/react-swf.js @@ -1,12 +1,10 @@ -/*! react-swf v0.12.3 | @syranide | MIT license */ +/*! react-swf v0.13.0 | @syranide | MIT license */ 'use strict'; var React = require('react'); var PropTypes = React.PropTypes; -var mimeTypeFP = 'application/x-shockwave-flash'; - /* flashVars = {key: string} or "key=value&..." @@ -98,92 +96,6 @@ function encodeFlashVarsObject(object) { } -var memoizedFPVersion; - -function getMemoizedFPVersion() { - if (memoizedFPVersion === undefined) { - memoizedFPVersion = getFPVersion(); - } - - return memoizedFPVersion; -} - -/** - * Detect installed Flash Player version. Cached. - * - * @return {?string} 'X.Y.Z'-version or null. - */ -function getFPVersion() { - if (typeof navigator !== 'undefined') { - var navFPPlugin = ( - navigator.plugins && - navigator.plugins['Shockwave Flash'] - ); - var navFPMimeType = ( - navigator.mimeTypes && - navigator.mimeTypes[mimeTypeFP] - ); - - if (navFPPlugin && navFPMimeType && navFPMimeType.enabledPlugin) { - try { - return ( - navFPPlugin - .description - .match(/(\d+)\.(\d+) r(\d+)/) - .slice(1) - .join('.') - ); - } catch (e) { - } - } - } - - // ActiveXObject-fallback for IE8-10 - if (typeof ActiveXObject !== 'undefined') { - try { - return ( - new ActiveXObject('ShockwaveFlash.ShockwaveFlash') - .GetVariable('$version') - .match(/(\d+),(\d+),(\d+)/) - .slice(1) - .join('.') - ); - } catch (e) { - } - } - - return null; -} - -/** - * Detect if installed Flash Player meets version requirement. - * - * @param {string} versionString 'X.Y.Z', 'X.Y' or 'X'. - * @return {boolean} true if supported. - */ -function isFPVersionSupported(versionString) { - var installedString = getMemoizedFPVersion(); - - if (installedString == null) { - return false; - } - - var installedFields = installedString.split('.'); - var requiredFields = versionString.split('.'); - - for (var i = 0; i < 3; i++) { - var installedNumber = +installedFields[i]; - var requiredNumber = +(requiredFields[i] || 0); - - if (installedNumber !== requiredNumber) { - return installedNumber > requiredNumber; - } - } - - return true; -} - - /** @constructor */ function ReactSWF(props) { React.Component.call(this, props); @@ -229,12 +141,9 @@ function ReactSWF(props) { }; } -Object.assign(ReactSWF, React.Component); ReactSWF.prototype = Object.create(React.Component.prototype); ReactSWF.prototype.constructor = ReactSWF; - -ReactSWF.getFPVersion = getMemoizedFPVersion; -ReactSWF.isFPVersionSupported = isFPVersionSupported; +Object.assign(ReactSWF, React.Component); ReactSWF.propTypes = { src: PropTypes.string.isRequired, @@ -313,7 +222,7 @@ ReactSWF.prototype.render = function() { var objectProps = { ref: this._refCallback, children: [], - type: mimeTypeFP, + type: 'application/x-shockwave-flash', data: state.src, // Discard `props.src` src: null diff --git a/package.json b/package.json index b226cef..41970d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-swf", - "version": "0.12.3", + "version": "0.13.0", "license": "MIT", "description": "Shockwave Flash Player component for React", "author": "Andreas Svensson ", @@ -12,7 +12,7 @@ "url": "https://github.com/syranide/react-swf" }, "peerDependencies": { - "react": "^0.14.0-beta1" + "react": "^0.14.0" }, "keywords": [ "react", diff --git a/react-swf.js b/react-swf.js index fd2908f..0633bf5 100644 --- a/react-swf.js +++ b/react-swf.js @@ -1,4 +1,4 @@ -/*! react-swf v0.12.3 | @syranide | MIT license */ +/*! react-swf v0.13.0 | @syranide | MIT license */ (function(root, factory) { if (typeof define === 'function' && define.amd) { @@ -13,8 +13,6 @@ var PropTypes = React.PropTypes; - var mimeTypeFP = 'application/x-shockwave-flash'; - /* flashVars = {key: string} or "key=value&..." @@ -106,92 +104,6 @@ } - var memoizedFPVersion; - - function getMemoizedFPVersion() { - if (memoizedFPVersion === undefined) { - memoizedFPVersion = getFPVersion(); - } - - return memoizedFPVersion; - } - - /** - * Detect installed Flash Player version. Cached. - * - * @return {?string} 'X.Y.Z'-version or null. - */ - function getFPVersion() { - if (typeof navigator !== 'undefined') { - var navFPPlugin = ( - navigator.plugins && - navigator.plugins['Shockwave Flash'] - ); - var navFPMimeType = ( - navigator.mimeTypes && - navigator.mimeTypes[mimeTypeFP] - ); - - if (navFPPlugin && navFPMimeType && navFPMimeType.enabledPlugin) { - try { - return ( - navFPPlugin - .description - .match(/(\d+)\.(\d+) r(\d+)/) - .slice(1) - .join('.') - ); - } catch (e) { - } - } - } - - // ActiveXObject-fallback for IE8-10 - if (typeof ActiveXObject !== 'undefined') { - try { - return ( - new ActiveXObject('ShockwaveFlash.ShockwaveFlash') - .GetVariable('$version') - .match(/(\d+),(\d+),(\d+)/) - .slice(1) - .join('.') - ); - } catch (e) { - } - } - - return null; - } - - /** - * Detect if installed Flash Player meets version requirement. - * - * @param {string} versionString 'X.Y.Z', 'X.Y' or 'X'. - * @return {boolean} true if supported. - */ - function isFPVersionSupported(versionString) { - var installedString = getMemoizedFPVersion(); - - if (installedString == null) { - return false; - } - - var installedFields = installedString.split('.'); - var requiredFields = versionString.split('.'); - - for (var i = 0; i < 3; i++) { - var installedNumber = +installedFields[i]; - var requiredNumber = +(requiredFields[i] || 0); - - if (installedNumber !== requiredNumber) { - return installedNumber > requiredNumber; - } - } - - return true; - } - - /** @constructor */ function ReactSWF(props) { React.Component.call(this, props); @@ -237,12 +149,9 @@ }; } - Object.assign(ReactSWF, React.Component); ReactSWF.prototype = Object.create(React.Component.prototype); ReactSWF.prototype.constructor = ReactSWF; - - ReactSWF.getFPVersion = getMemoizedFPVersion; - ReactSWF.isFPVersionSupported = isFPVersionSupported; + Object.assign(ReactSWF, React.Component); ReactSWF.propTypes = { src: PropTypes.string.isRequired, @@ -320,7 +229,7 @@ var objectProps = { ref: this._refCallback, children: [], - type: mimeTypeFP, + type: 'application/x-shockwave-flash', data: state.src, // Discard `props.src` src: null diff --git a/react-swf.min.js b/react-swf.min.js index 7c21b16..6ebc6e0 100644 --- a/react-swf.min.js +++ b/react-swf.min.js @@ -1,2 +1,2 @@ -/*! react-swf v0.12.3 | @syranide | MIT license */ -!function(e,r){"function"==typeof define&&define.amd?define(["react"],r):"object"==typeof exports?module.exports=r(require("react")):e.ReactSWF=r(e.React)}(this,function(e){"use strict";function r(e){return d[e]}function n(e){return(""+e).replace(h,r)}function o(e){var r=[];for(var o in e)if(e.hasOwnProperty(o)){var t=e[o];null!=t&&r.push(n(o)+"="+n(t))}return r.join("&")}function t(){return void 0===s&&(s=a()),s}function a(){if("undefined"!=typeof navigator){var e=navigator.plugins&&navigator.plugins["Shockwave Flash"],r=navigator.mimeTypes&&navigator.mimeTypes[p];if(e&&r&&r.enabledPlugin)try{return e.description.match(/(\d+)\.(\d+) r(\d+)/).slice(1).join(".")}catch(n){}}if("undefined"!=typeof ActiveXObject)try{return new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version").match(/(\d+),(\d+),(\d+)/).slice(1).join(".")}catch(n){}return null}function l(e){var r=t();if(null==r)return!1;for(var n=r.split("."),o=e.split("."),a=0;3>a;a++){var l=+n[a],i=+(o[a]||0);if(l!==i)return l>i}return!0}function i(r){e.Component.call(this,r);var n=this;this._refCallback=function(e){n._node=e};var t={movie:r.src};for(var a in u)if(u.hasOwnProperty(a)&&r.hasOwnProperty(a)){var l=r[a];if(null!=l){var i=u[a];"flashvars"===i&&"object"==typeof l?l=o(l):f.hasOwnProperty(a)&&(l=!!l),t[i]=""+l}}this._node=null,this.state={src:r.src,params:t}}var s,c=e.PropTypes,p="application/x-shockwave-flash",u={flashVars:"flashvars",allowFullScreen:"allowfullscreen",allowNetworking:"allownetworking",allowScriptAccess:"allowscriptaccess",align:"align",base:"base",bgcolor:"bgcolor",fullScreenAspectRatio:"fullscreenaspectratio",loop:"loop",menu:"menu",play:"play",quality:"quality",salign:"salign",scale:"scale",seamlessTabbing:"seamlesstabbing",wmode:"wmode"},f={allowFullScreen:!0,loop:!0,menu:!0,play:!0,seamlessTabbing:!0},h=/[\r%&+=]/g,d={"\r":"%0D","%":"%25","&":"%26","+":"%2B","=":"%3D"};return Object.assign(i,e.Component),i.prototype=Object.create(e.Component.prototype),i.prototype.constructor=i,i.getFPVersion=t,i.isFPVersionSupported=l,i.propTypes={src:c.string.isRequired,flashVars:c.oneOfType([c.object,c.string]),allowFullScreen:c.bool,allowNetworking:c.oneOf(["all","internal","none"]),allowScriptAccess:c.oneOf(["always","sameDomain","never"]),align:c.oneOf(["l","t","r"]),base:c.string,bgcolor:c.string,fullScreenAspectRatio:c.oneOf(["portrait","landscape"]),loop:c.bool,menu:c.bool,play:c.bool,quality:c.oneOf(["low","autolow","autohigh","medium","high","best"]),salign:c.oneOf(["l","t","r","tl","tr"]),scale:c.oneOf(["default","noborder","exactfit","noscale"]),seamlessTabbing:c.bool,wmode:c.oneOf(["window","direct","opaque","transparent","gpu"])},i.prototype.getFPDOMNode=function(){return this._node},i.prototype.shouldComponentUpdate=function(e){var r=this.props;for(var n in r)if(!(!r.hasOwnProperty(n)||u.hasOwnProperty(n)||e.hasOwnProperty(n)&&Object.is(r[n],e[n])))return!0;for(var n in e)if(e.hasOwnProperty(n)&&!u.hasOwnProperty(n)&&!r.hasOwnProperty(n))return!0;return!1},i.prototype.componentWillUnmount=function(){if(document.documentMode<9){var e=this._node;for(var r in e)"function"==typeof e[r]&&(e[r]=null)}},i.prototype.render=function(){var r=this.props,n=this.state,o={ref:this._refCallback,children:[],type:p,data:n.src,src:null};for(var t in r)!r.hasOwnProperty(t)||u.hasOwnProperty(t)||o.hasOwnProperty(t)||(o[t]=r[t]);var a=o.children;for(var l in n.params)a.push(e.createElement("param",{key:l,name:l,value:n.params[l]}));return null!=r.children&&a.push(r.children),e.createElement("object",o)},i}); +/*! react-swf v0.13.0 | @syranide | MIT license */ +!function(e,o){"function"==typeof define&&define.amd?define(["react"],o):"object"==typeof exports?module.exports=o(require("react")):e.ReactSWF=o(e.React)}(this,function(e){"use strict";function o(e){return p[e]}function r(e){return(""+e).replace(i,o)}function n(e){var o=[];for(var n in e)if(e.hasOwnProperty(n)){var t=e[n];null!=t&&o.push(r(n)+"="+r(t))}return o.join("&")}function t(o){e.Component.call(this,o);var r=this;this._refCallback=function(e){r._node=e};var t={movie:o.src};for(var a in l)if(l.hasOwnProperty(a)&&o.hasOwnProperty(a)){var i=o[a];if(null!=i){var p=l[a];"flashvars"===p&&"object"==typeof i?i=n(i):s.hasOwnProperty(a)&&(i=!!i),t[p]=""+i}}this._node=null,this.state={src:o.src,params:t}}var a=e.PropTypes,l={flashVars:"flashvars",allowFullScreen:"allowfullscreen",allowNetworking:"allownetworking",allowScriptAccess:"allowscriptaccess",align:"align",base:"base",bgcolor:"bgcolor",fullScreenAspectRatio:"fullscreenaspectratio",loop:"loop",menu:"menu",play:"play",quality:"quality",salign:"salign",scale:"scale",seamlessTabbing:"seamlesstabbing",wmode:"wmode"},s={allowFullScreen:!0,loop:!0,menu:!0,play:!0,seamlessTabbing:!0},i=/[\r%&+=]/g,p={"\r":"%0D","%":"%25","&":"%26","+":"%2B","=":"%3D"};return t.prototype=Object.create(e.Component.prototype),t.prototype.constructor=t,Object.assign(t,e.Component),t.propTypes={src:a.string.isRequired,flashVars:a.oneOfType([a.object,a.string]),allowFullScreen:a.bool,allowNetworking:a.oneOf(["all","internal","none"]),allowScriptAccess:a.oneOf(["always","sameDomain","never"]),align:a.oneOf(["l","t","r"]),base:a.string,bgcolor:a.string,fullScreenAspectRatio:a.oneOf(["portrait","landscape"]),loop:a.bool,menu:a.bool,play:a.bool,quality:a.oneOf(["low","autolow","autohigh","medium","high","best"]),salign:a.oneOf(["l","t","r","tl","tr"]),scale:a.oneOf(["default","noborder","exactfit","noscale"]),seamlessTabbing:a.bool,wmode:a.oneOf(["window","direct","opaque","transparent","gpu"])},t.prototype.getFPDOMNode=function(){return this._node},t.prototype.shouldComponentUpdate=function(e){var o=this.props;for(var r in o)if(!(!o.hasOwnProperty(r)||l.hasOwnProperty(r)||e.hasOwnProperty(r)&&Object.is(o[r],e[r])))return!0;for(var r in e)if(e.hasOwnProperty(r)&&!l.hasOwnProperty(r)&&!o.hasOwnProperty(r))return!0;return!1},t.prototype.componentWillUnmount=function(){if(document.documentMode<9){var e=this._node;for(var o in e)"function"==typeof e[o]&&(e[o]=null)}},t.prototype.render=function(){var o=this.props,r=this.state,n={ref:this._refCallback,children:[],type:"application/x-shockwave-flash",data:r.src,src:null};for(var t in o)!o.hasOwnProperty(t)||l.hasOwnProperty(t)||n.hasOwnProperty(t)||(n[t]=o[t]);var a=n.children;for(var s in r.params)a.push(e.createElement("param",{key:s,name:s,value:r.params[s]}));return null!=o.children&&a.push(o.children),e.createElement("object",n)},t});