diff --git a/api/migrations/0002_add_additional_info_to_Comic.py b/api/migrations/0002_add_additional_info_to_Comic.py
new file mode 100644
index 0000000..4438c00
--- /dev/null
+++ b/api/migrations/0002_add_additional_info_to_Comic.py
@@ -0,0 +1,38 @@
+# Generated by Django 2.0.7 on 2018-11-18 00:03
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='comic',
+ name='frames_mode',
+ field=models.PositiveIntegerField(default=0),
+ ),
+ migrations.AddField(
+ model_name='comic',
+ name='image_assessment_mode',
+ field=models.PositiveIntegerField(default=0),
+ ),
+ migrations.AddField(
+ model_name='comic',
+ name='rl_mode',
+ field=models.PositiveIntegerField(default=0),
+ ),
+ migrations.AddField(
+ model_name='comic',
+ name='style_transfer_mode',
+ field=models.PositiveIntegerField(default=0),
+ ),
+ migrations.AddField(
+ model_name='comic',
+ name='yt_url',
+ field=models.URLField(blank=True, null=True),
+ ),
+ ]
diff --git a/api/migrations/0003_add_timestamp.py b/api/migrations/0003_add_timestamp.py
new file mode 100644
index 0000000..ebe5ab7
--- /dev/null
+++ b/api/migrations/0003_add_timestamp.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.0.7 on 2018-11-18 10:55
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0002_add_additional_info_to_Comic'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='comic',
+ name='timestamp',
+ field=models.DateTimeField(auto_now_add=True),
+ ),
+ ]
diff --git a/api/models.py b/api/models.py
index 5578d62..bad1e35 100644
--- a/api/models.py
+++ b/api/models.py
@@ -34,7 +34,7 @@ class Video(models.Model):
else:
raise TooLargeFile()
- def create_comic(self, frames_mode=0, rl_mode=0, image_assessment_mode=0, style_transfer_mode=0):
+ def create_comix(self, yt_url='', frames_mode=0, rl_mode=0, image_assessment_mode=0, style_transfer_mode=0):
(keyframes, keyframes_timings), keyframes_extraction_time = KeyFramesExtractor.get_keyframes(
video=self,
frames_mode=frames_mode,
@@ -45,29 +45,49 @@ class Video(models.Model):
style_transfer_mode=style_transfer_mode)
comic_image, layout_generation_time = LayoutGenerator.get_layout(frames=stylized_keyframes)
+ comic, from_nparray_time = Comic.create_from_nparray(nparray=comic_image,
+ video=self,
+ yt_url=yt_url,
+ frames_mode=frames_mode,
+ rl_mode=rl_mode,
+ image_assessment_mode=image_assessment_mode,
+ style_transfer_mode=style_transfer_mode)
+
timings = {
+ 'from_nparray_time': from_nparray_time,
'keyframes_extraction_time': keyframes_extraction_time,
'stylization_time': stylization_time,
'layout_generation_time': layout_generation_time,
'keyframes_extraction_time_details': keyframes_timings
}
- return comic_image, timings
+ return comic, timings
class Comic(models.Model):
file = models.FileField(blank=False, null=False, upload_to="comic")
video = models.ForeignKey(Video, on_delete=models.CASCADE, related_name="comic")
+ yt_url = models.URLField(blank=True, null=True)
+ frames_mode = models.PositiveIntegerField(default=0)
+ rl_mode = models.PositiveIntegerField(default=0)
+ image_assessment_mode = models.PositiveIntegerField(default=0)
+ style_transfer_mode = models.PositiveIntegerField(default=0)
+ timestamp = models.DateTimeField(auto_now_add=True)
@classmethod
@profile
- def create_from_nparray(cls, nparray_file, video):
- if nparray_file.max() <= 1:
- nparray_file = (nparray_file).astype(int)
+ def create_from_nparray(cls, nparray, video, yt_url, frames_mode,
+ rl_mode, image_assessment_mode, style_transfer_mode):
tmp_name = uuid.uuid4().hex + ".png"
- cv2.imwrite(jj(settings.TMP_DIR, tmp_name), nparray_file)
+ cv2.imwrite(jj(settings.TMP_DIR, tmp_name), nparray)
with open(jj(settings.TMP_DIR, tmp_name), mode="rb") as tmp_file:
comic_image = File(tmp_file, name=tmp_name)
- comic = Comic.objects.create(file=comic_image, video=video)
+ comic = Comic.objects.create(file=comic_image,
+ video=video,
+ yt_url=yt_url,
+ frames_mode=frames_mode,
+ rl_mode=rl_mode,
+ image_assessment_mode=image_assessment_mode,
+ style_transfer_mode=style_transfer_mode)
os.remove(jj(settings.TMP_DIR, tmp_name))
return comic
diff --git a/api/serializers.py b/api/serializers.py
index 5510527..04977ef 100644
--- a/api/serializers.py
+++ b/api/serializers.py
@@ -16,9 +16,9 @@ class VideoSerializer(serializers.Serializer):
def validate(self, attrs):
file = attrs.get("file")
if file.name.split(".")[-1] not in settings.PERMITTED_VIDEO_EXTENSIONS:
- raise FileExtensionError
+ raise FileExtensionError()
if file.size > settings.MAX_FILE_SIZE:
- raise TooLargeFile
+ raise TooLargeFile()
return attrs
diff --git a/api/views.py b/api/views.py
index 9d6fdbe..bda67c5 100644
--- a/api/views.py
+++ b/api/views.py
@@ -16,21 +16,24 @@ class Comixify(APIView):
serializer = VideoSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
-
video_file = serializer.validated_data["file"]
+ frames_mode = serializer.validated_data["frames_mode"]
+ rl_mode = serializer.validated_data["rl_mode"]
+ image_assessment_mode = serializer.validated_data["image_assessment_mode"]
+ style_transfer_mode = serializer.validated_data["style_transfer_mode"]
+
video = Video.objects.create(file=video_file)
- comic_image, timings = video.create_comic(
- frames_mode=serializer.validated_data["frames_mode"],
- rl_mode=serializer.validated_data["rl_mode"],
- image_assessment_mode=serializer.validated_data["image_assessment_mode"],
- style_transfer_mode=serializer.validated_data["style_transfer_mode"],
+ comix, timings = video.create_comix(
+ yt_url='',
+ frames_mode=frames_mode,
+ rl_mode=rl_mode,
+ image_assessment_mode=image_assessment_mode,
+ style_transfer_mode=style_transfer_mode
)
- comic, from_nparray_time = Comic.create_from_nparray(comic_image, video)
- timings['from_nparray_time'] = from_nparray_time
response = {
"status_message": "ok",
- "comic": comic.file.url,
+ "comic": comix.file.url,
"timings": timings,
}
# Remove to spare storage
@@ -39,7 +42,6 @@ class Comixify(APIView):
class ComixifyFromYoutube(APIView):
-
def post(self, request):
"""
Receives video, and returns comic image
@@ -48,25 +50,40 @@ class ComixifyFromYoutube(APIView):
serializer = YouTubeDownloadSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
yt_url = serializer.validated_data["url"]
+ frames_mode = serializer.validated_data["frames_mode"]
+ rl_mode = serializer.validated_data["rl_mode"]
+ image_assessment_mode = serializer.validated_data["image_assessment_mode"]
+ style_transfer_mode = serializer.validated_data["style_transfer_mode"]
- video = Video()
- _, yt_download_time = video.download_from_youtube(yt_url)
- video.save()
- comic_image, timings = video.create_comic(
- frames_mode=serializer.validated_data["frames_mode"],
- rl_mode=serializer.validated_data["rl_mode"],
- image_assessment_mode=serializer.validated_data["image_assessment_mode"],
- style_transfer_mode=serializer.validated_data["style_transfer_mode"],
- )
- comic, from_nparray_time = Comic.create_from_nparray(comic_image, video)
- timings['from_nparray_time'] = from_nparray_time
- timings['yt_download_time'] = yt_download_time
+ try:
+ comix = Comic.objects.filter(yt_url=yt_url,
+ frames_mode=frames_mode,
+ rl_mode=rl_mode,
+ image_assessment_mode=image_assessment_mode,
+ style_transfer_mode=style_transfer_mode
+ ).latest('timestamp')
+ response = {
+ "status_message": "ok",
+ "comic": comix.file.url,
+ }
+ except Comic.DoesNotExist:
+ video = Video()
+ _, yt_download_time = video.download_from_youtube(yt_url)
+ video.save()
+ comix, timings = video.create_comix(
+ yt_url=yt_url,
+ frames_mode=frames_mode,
+ rl_mode=rl_mode,
+ image_assessment_mode=image_assessment_mode,
+ style_transfer_mode=style_transfer_mode
+ )
- response = {
- "status_message": "ok",
- "comic": comic.file.url,
- "timings": timings,
- }
- # Remove to spare storage
- video.file.delete()
+ timings['yt_download_time'] = yt_download_time
+ response = {
+ "status_message": "ok",
+ "comic": comix.file.url,
+ "timings": timings,
+ }
+ # Remove to spare storage
+ video.file.delete()
return Response(response)
diff --git a/frontend/client/App.js b/frontend/client/App.js
index 3df767a..db72939 100644
--- a/frontend/client/App.js
+++ b/frontend/client/App.js
@@ -5,12 +5,14 @@ import { post } from "axios";
import Dropzone from "react-dropzone";
import { BarLoader } from "react-spinners";
import { css } from "react-emotion";
+import debounce from "lodash/debounce"
import {
COMIXIFY_API,
MAX_FILE_SIZE,
PERMITTED_VIDEO_EXTENSIONS,
- FROM_YOUTUBE_API
+ FROM_YOUTUBE_API,
+ MIN_RESPONSE_DELAY
} from "./constants";
class App extends React.Component {
@@ -37,7 +39,7 @@ class App extends React.Component {
};
this.onVideoDrop = this.onVideoDrop.bind(this);
this.onModelChange = this.onModelChange.bind(this);
- this.handleResponse = this.handleResponse.bind(this);
+ this.handleResponse = debounce(this.handleResponse.bind(this), MIN_RESPONSE_DELAY);
this.onYouTubeSubmit = this.onYouTubeSubmit.bind(this);
this.onSamplingChange = this.onSamplingChange.bind(this);
this.onImageAssessmentChange = this.onImageAssessmentChange.bind(this);
@@ -74,6 +76,9 @@ class App extends React.Component {
})
}
handleResponse(res) {
+ if(res === "debounce") {
+ return
+ }
if (res.data["status_message"] === "ok") {
this.setState({
state: App.appStates.FINISHED,
@@ -93,6 +98,7 @@ class App extends React.Component {
data.set('rl_mode', parseInt(rlMode));
data.set("image_assessment_mode", parseInt(imageAssessment));
data.set('style_transfer_mode', parseInt(styleTransferMode));
+ this.handleResponse("debounce");
post(COMIXIFY_API, data, {
headers: { "content-type": "multipart/form-data" },
onUploadProgress: App.onVideoUploadProgress
@@ -121,6 +127,7 @@ class App extends React.Component {
}
submitYouTube(link) {
let { framesMode, rlMode, imageAssessment, styleTransferMode } = this.state;
+ this.handleResponse("debounce");
post(FROM_YOUTUBE_API, {
url: link,
frames_mode: parseInt(framesMode),
@@ -317,14 +324,14 @@ class App extends React.Component {
/>
diff --git a/frontend/client/constants.js b/frontend/client/constants.js
index 8160bf1..27385d6 100644
--- a/frontend/client/constants.js
+++ b/frontend/client/constants.js
@@ -2,3 +2,4 @@ export const COMIXIFY_API = "/comixify/";
export const FROM_YOUTUBE_API = "/comixify/from_yt/";
export const MAX_FILE_SIZE = 50000000;
export const PERMITTED_VIDEO_EXTENSIONS = "video/*";
+export const MIN_RESPONSE_DELAY = 7500;
diff --git a/frontend/static/frontend/css/app.css b/frontend/static/frontend/css/app.css
index ea88537..cf648ff 100644
--- a/frontend/static/frontend/css/app.css
+++ b/frontend/static/frontend/css/app.css
@@ -35,6 +35,10 @@ img {
width: 100%;
}
+#demo {
+ overflow: hidden;
+}
+
.bar-loader {
margin: 0 auto;
}
diff --git a/frontend/static/frontend/js/app.client.js b/frontend/static/frontend/js/app.client.js
index f2becd4..2396183 100644
--- a/frontend/static/frontend/js/app.client.js
+++ b/frontend/static/frontend/js/app.client.js
@@ -1,9 +1,9 @@
-!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=267)}([function(e,t,n){"use strict";e.exports=n(100)},function(e,t,n){e.exports=n(107)()},function(e,t,n){"use strict";n.r(t),function(e){n.d(t,"flush",function(){return i}),n.d(t,"hydrate",function(){return u}),n.d(t,"cx",function(){return l}),n.d(t,"merge",function(){return s}),n.d(t,"getRegisteredStyles",function(){return c}),n.d(t,"injectGlobal",function(){return f}),n.d(t,"keyframes",function(){return p}),n.d(t,"css",function(){return d}),n.d(t,"sheet",function(){return h}),n.d(t,"caches",function(){return m});var r=n(99),o=void 0!==e?e:{},a=Object(r.a)(o),i=a.flush,u=a.hydrate,l=a.cx,s=a.merge,c=a.getRegisteredStyles,f=a.injectGlobal,p=a.keyframes,d=a.css,h=a.sheet,m=a.caches}.call(this,n(41))},function(e,t,n){"use strict";t.__esModule=!0;var r=function(e){return e&&e.__esModule?e:{default:e}}(n(87));t.default=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!==(void 0===t?"undefined":(0,r.default)(t))&&"function"!=typeof t?e:t}},function(e,t,n){"use strict";n.r(t);var r=n(0),o=n.n(r),a=n(8),i=n.n(a),u=n(13),l=n.n(u),s=n(5),c=n.n(s),f=n(3),p=n.n(f),d=n(6),h=n.n(d);function m(){var e=this.constructor.getDerivedStateFromProps(this.props,this.state);null!==e&&void 0!==e&&this.setState(e)}function y(e){this.setState(function(t){var n=this.constructor.getDerivedStateFromProps(e,t);return null!==n&&void 0!==n?n:null}.bind(this))}function v(e,t){try{var n=this.props,r=this.state;this.props=e,this.state=t,this.__reactInternalSnapshotFlag=!0,this.__reactInternalSnapshot=this.getSnapshotBeforeUpdate(n,r)}finally{this.props=n,this.state=r}}function g(e){var t=e.prototype;if(!t||!t.isReactComponent)throw new Error("Can only polyfill class components");if("function"!=typeof e.getDerivedStateFromProps&&"function"!=typeof t.getSnapshotBeforeUpdate)return e;var n=null,r=null,o=null;if("function"==typeof t.componentWillMount?n="componentWillMount":"function"==typeof t.UNSAFE_componentWillMount&&(n="UNSAFE_componentWillMount"),"function"==typeof t.componentWillReceiveProps?r="componentWillReceiveProps":"function"==typeof t.UNSAFE_componentWillReceiveProps&&(r="UNSAFE_componentWillReceiveProps"),"function"==typeof t.componentWillUpdate?o="componentWillUpdate":"function"==typeof t.UNSAFE_componentWillUpdate&&(o="UNSAFE_componentWillUpdate"),null!==n||null!==r||null!==o){var a=e.displayName||e.name,i="function"==typeof e.getDerivedStateFromProps?"getDerivedStateFromProps()":"getSnapshotBeforeUpdate()";throw Error("Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n"+a+" uses "+i+" but also contains the following legacy lifecycles:"+(null!==n?"\n "+n:"")+(null!==r?"\n "+r:"")+(null!==o?"\n "+o:"")+"\n\nThe above lifecycles should be removed. Learn more about this warning here:\nhttps://fb.me/react-async-component-lifecycle-hooks")}if("function"==typeof e.getDerivedStateFromProps&&(t.componentWillMount=m,t.componentWillReceiveProps=y),"function"==typeof t.getSnapshotBeforeUpdate){if("function"!=typeof t.componentDidUpdate)throw new Error("Cannot polyfill getSnapshotBeforeUpdate() for components that do not define componentDidUpdate() on the prototype");t.componentWillUpdate=v;var u=t.componentDidUpdate;t.componentDidUpdate=function(e,t,n){var r=this.__reactInternalSnapshotFlag?this.__reactInternalSnapshot:n;u.call(this,e,t,r)}}return e}m.__suppressDeprecationWarning=!0,y.__suppressDeprecationWarning=!0,v.__suppressDeprecationWarning=!0;var b=n(58),w=n.n(b),x=n(37),_=n.n(x),E=n(97),k=n.n(E),S=n(59),P=n(38);n.d(t,"mapProps",function(){return j}),n.d(t,"withProps",function(){return A}),n.d(t,"withPropsOnChange",function(){return z}),n.d(t,"withHandlers",function(){return D}),n.d(t,"defaultProps",function(){return M}),n.d(t,"renameProp",function(){return I}),n.d(t,"renameProps",function(){return V}),n.d(t,"flattenProp",function(){return B}),n.d(t,"withState",function(){return W}),n.d(t,"withStateHandlers",function(){return H}),n.d(t,"withReducer",function(){return q}),n.d(t,"branch",function(){return K}),n.d(t,"renderComponent",function(){return Y}),n.d(t,"renderNothing",function(){return Q}),n.d(t,"shouldUpdate",function(){return Z}),n.d(t,"pure",function(){return J}),n.d(t,"onlyUpdateForKeys",function(){return ee}),n.d(t,"onlyUpdateForPropTypes",function(){return te}),n.d(t,"withContext",function(){return ne}),n.d(t,"getContext",function(){return re}),n.d(t,"lifecycle",function(){return oe}),n.d(t,"toClass",function(){return ie}),n.d(t,"withRenderProps",function(){return ue}),n.d(t,"setStatic",function(){return C}),n.d(t,"setPropTypes",function(){return le}),n.d(t,"setDisplayName",function(){return O}),n.d(t,"compose",function(){return se}),n.d(t,"getDisplayName",function(){return T}),n.d(t,"wrapDisplayName",function(){return N}),n.d(t,"isClassComponent",function(){return ae}),n.d(t,"createSink",function(){return ce}),n.d(t,"componentFromProp",function(){return fe}),n.d(t,"nest",function(){return pe}),n.d(t,"hoistStatics",function(){return de}),n.d(t,"componentFromStream",function(){return ge}),n.d(t,"componentFromStreamWithConfig",function(){return ve}),n.d(t,"mapPropsStream",function(){return xe}),n.d(t,"mapPropsStreamWithConfig",function(){return we}),n.d(t,"createEventHandler",function(){return Ee}),n.d(t,"createEventHandlerWithConfig",function(){return _e}),n.d(t,"setObservableConfig",function(){return me}),n.d(t,"shallowEqual",function(){return l.a});var C=function(e,t){return function(n){return n[e]=t,n}},O=function(e){return C("displayName",e)},T=function(e){return"string"==typeof e?e:e?e.displayName||e.name||"Component":void 0},N=function(e,t){return t+"("+T(e)+")"},j=function(e){return function(t){var n=Object(r.createFactory)(t);return function(t){return n(e(t))}}},A=function(e){return j(function(t){return i()({},t,"function"==typeof e?e(t):e)})},U=function(e,t){for(var n={},r=0;r1?r-1:0),a=1;a1&&void 0!==arguments[1]?arguments[1]:$;return t.setState(function(t){var r=t.stateValue;return{stateValue:n(r,e)}},function(){return r(t.state.stateValue)})},o=e,p()(t,o)}return h()(a,r),a.prototype.initializeStateValue=function(){return void 0!==o?"function"==typeof o?o(this.props):o:n(void 0,{type:"@@recompose/INIT"})},a.prototype.render=function(){var n;return u(i()({},this.props,((n={})[e]=this.state.stateValue,n[t]=this.dispatch,n)))},a}(r.Component)}},G=function(e){return e},K=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:G;return function(o){var a=void 0,i=void 0;return function(u){return e(u)?(a=a||Object(r.createFactory)(t(o)))(u):(i=i||Object(r.createFactory)(n(o)))(u)}}},Y=function(e){return function(t){var n=Object(r.createFactory)(e);return function(e){return n(e)}}},X=function(e){function t(){return c()(this,t),p()(this,e.apply(this,arguments))}return h()(t,e),t.prototype.render=function(){return null},t}(r.Component),Q=function(e){return X},Z=function(e){return function(t){var n=Object(r.createFactory)(t);return function(t){function r(){return c()(this,r),p()(this,t.apply(this,arguments))}return h()(r,t),r.prototype.shouldComponentUpdate=function(t){return e(this.props,t)},r.prototype.render=function(){return n(this.props)},r}(r.Component)}},J=function(e){return Z(function(e,t){return!l()(e,t)})(e)},ee=function(e){return Z(function(t,n){return!l()(U(n,e),U(t,e))})},te=function(e){var t=e.propTypes,n=_()(t||{});return ee(n)(e)},ne=function(e,t){return function(n){var o=Object(r.createFactory)(n),a=function(e){function n(){var r,o,a;c()(this,n);for(var i=arguments.length,u=Array(i),l=0;l=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};r.forEach(["delete","get","head"],function(e){u.headers[e]={}}),r.forEach(["post","put","patch"],function(e){u.headers[e]=r.merge(a)}),e.exports=u}).call(this,n(75))},function(e,t,n){var r=n(18);e.exports=function(e,t){if(!r(e))return e;var n,o;if(t&&"function"==typeof(n=e.toString)&&!r(o=n.call(e)))return o;if("function"==typeof(n=e.valueOf)&&!r(o=n.call(e)))return o;if(!t&&"function"==typeof(n=e.toString)&&!r(o=n.call(e)))return o;throw TypeError("Can't convert object to primitive value")}},function(e,t){e.exports=function(e){if(void 0==e)throw TypeError("Can't call method on "+e);return e}},function(e,t){var n=Math.ceil,r=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?r:n)(e)}},function(e,t,n){var r=n(48)("keys"),o=n(34);e.exports=function(e){return r[e]||(r[e]=o(e))}},function(e,t,n){var r=n(10),o=n(11),a=o["__core-js_shared__"]||(o["__core-js_shared__"]={});(e.exports=function(e,t){return a[e]||(a[e]=void 0!==t?t:{})})("versions",[]).push({version:r.version,mode:n(33)?"pure":"global",copyright:"© 2018 Denis Pushkarev (zloirock.ru)"})},function(e,t){e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(e,t){t.f=Object.getOwnPropertySymbols},function(e,t,n){var r=n(45);e.exports=function(e){return Object(r(e))}},function(e,t){e.exports={}},function(e,t,n){var r=n(24),o=n(221),a=n(49),i=n(47)("IE_PROTO"),u=function(){},l=function(){var e,t=n(83)("iframe"),r=a.length;for(t.style.display="none",n(222).appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write("