<commit_before>define(['js/core/DomElement', 'js/core/List', 'js/core/Bindable'], function (DomElement, List, Bindable) {

    var undefined,
        SvgElement = DomElement.inherit("js.svg.SvgElement", {

            defaults: {
                transformations: List,
                _transform: null,

                translateX: undefined,
                translateY: undefined,

                scaleX: undefined,
                scaleY: undefined,

                rotation: undefined,
                rotationX: undefined,
                rotationY: undefined
            },

            $classAttributes: ["transformations", "translateX", "translateY", "scaleX", "scaleY", "rotation", "rotationX", "rotationY"],

            ctor: function () {
                // default namespace
                this.$namespace = SvgElement.SVG_NAMESPACE;
                this.callBase();

                this.bind("transformations", '*', this._refreshTransform, this);
            },

            transform: function (transform) {
                if (this.$transform) {
                    this.$transform.set("transform", transform);
                } else {
                    this.$transform = new SvgElement.Transform({
                        transform: transform
                    });
                    this.$.transformations.add(this.$transform);
                }

                return this;
            },

            addChild: function (child) {
                this.callBase();
                child.$svgRoot = this.$svgRoot;
            },

            getSvgRoot: function () {
                if(!this.$svgRoot){
                    var parent = this.$parent;
                    while(parent instanceof SvgElement && !parent.$svgRoot){
                        parent = parent.$parent;
                    }

                    if (!parent) {
                        return null;
                    }

                    return parent.$svgRoot;

                }
                return this.$svgRoot;
            },

            _commitChangedAttributes: function ($) {
                var x, y, r;

                if (this._hasSome($, ["translateX", "translateY"])) {
                    x = this.$.translateX;
                    y = this.$.translateY;

                    !(x === undefined && y === undefined) && this.translate(x, y);
                }

                if (this._hasSome($, ["scaleX", "scaleY"])) {
                    x = this.$.scaleX;
                    y = this.$.scaleY;

                    !(x === undefined && y === undefined) && this.scale(x, y);
                }

                if (this._hasSome($, ["rotation", "rotationX", "rotationY"])) {
                    r = this.$.rotation;
                    x = this.$.rotationX;
                    y = this.$.rotationY;

                    !(x === undefined && y === undefined && r === undefined) && this.rotate(r, x, y);
                }

                this.callBase();
            },

            scale: function (sx, sy) {

                if (sx === null || sy === null) {
                    return;
                }

                if (!sy && sy !== 0) {
                    sy = sx;
                }

                if (this.$scale) {
                    this.$scale.set({
                        sx: sx,
                        sy: sy
                    });
                } else {
                    this.$scale = new SvgElement.Scale({
                        sx: sx,
                        sy: sy
                    });
                    this.$.transformations.add(this.$scale);
                }

                return this;
            },

            translate: function (x, y) {

                if (this.$translate) {
                    this.$translate.set({
                        x: x,
                        y: y
                    });
                } else {
                    this.$translate = new SvgElement.Translate({
                        x: x,
                        y: y
                    });
                    this.$.transformations.add(this.$translate);
                }

                return this;
            },

            rotate: function (r, x, y) {
                if (this.$rotate) {
                    this.$rotate.set({
                        r: r,
                        x: x,
                        y: y
                    });
                } else {
                    this.$rotate = new SvgElement.Rotate({
                        r: r,
                        x: x,
                        y: y
                    });
                    this.$.transformations.add(this.$rotate);
                }

                return this;
            },

            _refreshTransform: function () {

                var transformations = [];

                this.$.transformations.each(function (t) {
                    if (t instanceof SvgElement.TransformBase) {
                        transformations.push(t.toString());
                    } else {
                        transformations.push(t);
                    }
                });

                this.set('_transform', transformations.join(" "));
            },

            _renderTransform: function () {
                // transforms needs to be set via transformations
            },

            _render_transform: function (transform) {
                this._setAttribute("transform", transform);
            },

            // render each href Element in xlink namespace
            _renderHref: function (href, oldHref) {
                this._setAttribute("href", href, SvgElement.XLINK_NAMESPACE);
            },

            localToGlobalFactor: function () {
                if (this.$parent) {
                    // TODO: add scale
                    return this.$parent.localToGlobalFactor();
                }

                return {
                    x: 1,
                    y: 1
                };
            },

            globalToLocalFactor: function () {
                if (this.$parent) {
                    // TODO: add scale
                    return this.$parent.globalToLocalFactor();
                }

                return {
                    x: 1,
                    y: 1
                };
            },

            localPointToGlobal: function (point) {

                var factor = this.localToGlobalFactor();

                return {
                    x: point.x * factor.x,
                    y: point.y * factor.y
                };
            },

            globalPointToLocal: function (point) {

                var factor = this.globalToLocalFactor();

                return {
                    x: point.x * factor.x,
                    y: point.y * factor.y
                };

            }
        });

    SvgElement.XLINK_NAMESPACE = "http://www.w3.org/1999/xlink";
    SvgElement.SVG_NAMESPACE = "http://www.w3.org/2000/svg";

    SvgElement.Matrix = function (a, b, c, d, e, f) {
        if (arguments.length) {
            this.a = a;
            this.b = b;
            this.c = c;
            this.d = d;
            this.e = e;
            this.f = f;
        } else {
            this.a = 1;
            this.b = 0;
            this.c = 0;
            this.d = 1;
            this.e = 0;
            this.f = 0;
        }
    };

    SvgElement.TransformBase = Bindable.inherit('js.svg.SvgElement.TransformBase', {
    });

    SvgElement.Transform = SvgElement.TransformBase.inherit('js.svg.SvgElement.TransformBase', {
        defaults: {
            transform: null
        },

        toString: function () {
            return this.$.transform || "";
        }
    });

    SvgElement.Translate = SvgElement.TransformBase.inherit("js.svg.SvgElement.Translate", {
        toString: function () {
            return "translate(" + (this.$.x || 0) + "," + (this.$.y || 0) + ")";
        }
    });

    SvgElement.Scale = SvgElement.TransformBase.inherit("js.svg.SvgElement.Scale", {

        defaults: {
            sx: 1,
            sy: 1
        },

        toString: function () {
            return "scale(" + this.$.sx + "," + this.$.sy + ")";
        }
    });

    SvgElement.Rotate = SvgElement.TransformBase.inherit("js.svg.SvgElement.Rotate", {

        defaults: {
            r: 0,
            x: 0,
            y: 0
        },

        toString: function () {
            return "rotate(" + (this.$.r || 0) + "," + (this.$.x || 0) + "," + (this.$.y || 0) + ")";
        }
    });

    return SvgElement;
});<commit_msg>added renderId method and remove unused var<commit_after>define(['js/core/DomElement', 'js/core/List', 'js/core/Bindable'], function (DomElement, List, Bindable) {

    var undefined,
        SvgElement = DomElement.inherit("js.svg.SvgElement", {

            defaults: {
                transformations: List,
                _transform: null,

                translateX: undefined,
                translateY: undefined,

                scaleX: undefined,
                scaleY: undefined,

                rotation: undefined,
                rotationX: undefined,
                rotationY: undefined
            },

            $classAttributes: ["transformations", "translateX", "translateY", "scaleX", "scaleY", "rotation", "rotationX", "rotationY"],

            ctor: function () {
                // default namespace
                this.$namespace = SvgElement.SVG_NAMESPACE;
                this.callBase();

                this.bind("transformations", '*', this._refreshTransform, this);
            },

            transform: function (transform) {
                if (this.$transform) {
                    this.$transform.set("transform", transform);
                } else {
                    this.$transform = new SvgElement.Transform({
                        transform: transform
                    });
                    this.$.transformations.add(this.$transform);
                }

                return this;
            },

            addChild: function (child) {
                this.callBase();
                child.$svgRoot = this.$svgRoot;
            },

            getSvgRoot: function () {
                if(!this.$svgRoot){
                    var parent = this.$parent;
                    while(parent instanceof SvgElement && !parent.$svgRoot){
                        parent = parent.$parent;
                    }

                    if (!parent) {
                        return null;
                    }

                    return parent.$svgRoot;

                }
                return this.$svgRoot;
            },

            _commitChangedAttributes: function ($) {
                var x, y, r;

                if (this._hasSome($, ["translateX", "translateY"])) {
                    x = this.$.translateX;
                    y = this.$.translateY;

                    !(x === undefined && y === undefined) && this.translate(x, y);
                }

                if (this._hasSome($, ["scaleX", "scaleY"])) {
                    x = this.$.scaleX;
                    y = this.$.scaleY;

                    !(x === undefined && y === undefined) && this.scale(x, y);
                }

                if (this._hasSome($, ["rotation", "rotationX", "rotationY"])) {
                    r = this.$.rotation;
                    x = this.$.rotationX;
                    y = this.$.rotationY;

                    !(x === undefined && y === undefined && r === undefined) && this.rotate(r, x, y);
                }

                this.callBase();
            },

            scale: function (sx, sy) {

                if (sx === null || sy === null) {
                    return;
                }

                if (!sy && sy !== 0) {
                    sy = sx;
                }

                if (this.$scale) {
                    this.$scale.set({
                        sx: sx,
                        sy: sy
                    });
                } else {
                    this.$scale = new SvgElement.Scale({
                        sx: sx,
                        sy: sy
                    });
                    this.$.transformations.add(this.$scale);
                }

                return this;
            },

            translate: function (x, y) {

                if (this.$translate) {
                    this.$translate.set({
                        x: x,
                        y: y
                    });
                } else {
                    this.$translate = new SvgElement.Translate({
                        x: x,
                        y: y
                    });
                    this.$.transformations.add(this.$translate);
                }

                return this;
            },

            rotate: function (r, x, y) {
                if (this.$rotate) {
                    this.$rotate.set({
                        r: r,
                        x: x,
                        y: y
                    });
                } else {
                    this.$rotate = new SvgElement.Rotate({
                        r: r,
                        x: x,
                        y: y
                    });
                    this.$.transformations.add(this.$rotate);
                }

                return this;
            },

            _refreshTransform: function () {

                var transformations = [];

                this.$.transformations.each(function (t) {
                    if (t instanceof SvgElement.TransformBase) {
                        transformations.push(t.toString());
                    } else {
                        transformations.push(t);
                    }
                });

                this.set('_transform', transformations.join(" "));
            },

            _renderTransform: function () {
                // transforms needs to be set via transformations
            },

            _render_transform: function (transform) {
                this._setAttribute("transform", transform);
            },

            // render each href Element in xlink namespace
            _renderHref: function (href) {
                this._setAttribute("href", href, SvgElement.XLINK_NAMESPACE);
            },

            _renderId: function(id) {
                this._setAttribute("id", id);
            },

            localToGlobalFactor: function () {
                if (this.$parent) {
                    // TODO: add scale
                    return this.$parent.localToGlobalFactor();
                }

                return {
                    x: 1,
                    y: 1
                };
            },

            globalToLocalFactor: function () {
                if (this.$parent) {
                    // TODO: add scale
                    return this.$parent.globalToLocalFactor();
                }

                return {
                    x: 1,
                    y: 1
                };
            },

            localPointToGlobal: function (point) {

                var factor = this.localToGlobalFactor();

                return {
                    x: point.x * factor.x,
                    y: point.y * factor.y
                };
            },

            globalPointToLocal: function (point) {

                var factor = this.globalToLocalFactor();

                return {
                    x: point.x * factor.x,
                    y: point.y * factor.y
                };

            }
        });

    SvgElement.XLINK_NAMESPACE = "http://www.w3.org/1999/xlink";
    SvgElement.SVG_NAMESPACE = "http://www.w3.org/2000/svg";

    SvgElement.Matrix = function (a, b, c, d, e, f) {
        if (arguments.length) {
            this.a = a;
            this.b = b;
            this.c = c;
            this.d = d;
            this.e = e;
            this.f = f;
        } else {
            this.a = 1;
            this.b = 0;
            this.c = 0;
            this.d = 1;
            this.e = 0;
            this.f = 0;
        }
    };

    SvgElement.TransformBase = Bindable.inherit('js.svg.SvgElement.TransformBase', {
    });

    SvgElement.Transform = SvgElement.TransformBase.inherit('js.svg.SvgElement.TransformBase', {
        defaults: {
            transform: null
        },

        toString: function () {
            return this.$.transform || "";
        }
    });

    SvgElement.Translate = SvgElement.TransformBase.inherit("js.svg.SvgElement.Translate", {
        toString: function () {
            return "translate(" + (this.$.x || 0) + "," + (this.$.y || 0) + ")";
        }
    });

    SvgElement.Scale = SvgElement.TransformBase.inherit("js.svg.SvgElement.Scale", {

        defaults: {
            sx: 1,
            sy: 1
        },

        toString: function () {
            return "scale(" + this.$.sx + "," + this.$.sy + ")";
        }
    });

    SvgElement.Rotate = SvgElement.TransformBase.inherit("js.svg.SvgElement.Rotate", {

        defaults: {
            r: 0,
            x: 0,
            y: 0
        },

        toString: function () {
            return "rotate(" + (this.$.r || 0) + "," + (this.$.x || 0) + "," + (this.$.y || 0) + ")";
        }
    });

    return SvgElement;
});<|endoftext|>


<commit_before>Ext.ns('Garp');

Garp.ImagePickerWindow = Ext.extend(Ext.Window, {

	/**
	 * @cfg image grid query, to instantiate the grid with (search for current images' id for example)
	 */
	imgGridQuery: null,

	/**
	 * @cfg the current image caption value
	 */
	captionValue: null,

	/**
	 * @cfg the current image align value
	 */
	alignValue: '',

	/**
	 * @cfg curent crop template value
	 */
	cropTemplateName: null,

	/**
	 * @cfg hide the wizard text
	 */
	hideWizardText: false,

	/**
	 * @cfg model reference. Usefull for extending imagePickerWindow for other datatypes
	 */
	model: 'Image',

	/**
	 * @cfg Title for dialog
	 */
	title: __('Image'),

	/**
	 * @cfg Icon for dialog
	 */
	iconCls: 'icon-image',

	// 'prviate' :
	layout: 'card',
	activeItem: 0,
	width: 640,
	height: 500,
	border: false,
	modal: true,
	buttonAlign: 'left',
	resizable: false,
	defaults: {
		border: false
	},

	/**
	 * pageHandler navigates to page
	 * @param {Number} direction (-1 = previous / 1 = next)
	 */
	navHandler: function(dir){
		var page = this.getLayout().activeItem.id;
		page = parseInt(page.substr(5, page.length), 10);
		page += dir;
		if(page <= 0){
			page = 0;
		}
		switch(page){
			case 1:

				this.tplGrid.getStore().on({

					load: {
						single: true,
						scope: this,
						fn: function(){
							if (!this.cropTemplateName) {
								this.tplGrid.getSelectionModel().selectFirstRow();
							} else {
								var rec = this.tplGrid.getStore().getAt(this.tplGrid.getStore().find('name', this.cropTemplateName));
								this.tplGrid.getSelectionModel().selectRecords([rec]);
							}
							if (!this.captionValue) {
								var caption = this.imgGrid.getSelectionModel().getSelected().get('caption');
								if (caption && CURRENT_LANGUAGE && caption.hasOwnProperty(CURRENT_LANGUAGE)) {
									caption = caption[CURRENT_LANGUAGE];
								}
								this.caption.getForm().findField('caption').setValue(caption);
							}
						}
					}
				});
				this.tplGrid.getStore().load();
				this.prevBtn.enable();
				this.nextBtn.setText(__('Ok'));

			break;
			case 2:
				var img = this.imgGrid.getSelectionModel().getSelected();
				var tpl = this.tplGrid.getSelectionModel().getSelected();
				this.fireEvent('select', {
					image: img,
					template: tpl,
					src: IMAGES_CDN + 'scaled/' + tpl.get('name') + '/' +  img.get('id'),
					align: this.alignment.getForm().getValues().align,
					caption: this.caption.getForm().getValues().caption
				});

				this.close();
			break;
			//case 0:
			default:
				this.prevBtn.disable();
				this.nextBtn.setDisabled( this.imgGrid.getSelectionModel().getCount() < 1 );
				this.nextBtn.setText(__('Next'));
			break;
		}

		this.getLayout().setActiveItem('page-' + page);
	},

	/**
	 * @function getStoreCfg
	 * @return store Cfg object
	 */
	getStoreCfg: function(){
		return {
			autoLoad: false,
			autoSave: false,
			remoteSort: true,
			restful: true,
			autoDestroy: true,
			root: 'rows',
			idProperty: 'id',
			fields: Garp.dataTypes[this.model].getStoreFieldsFromColumnModel(),
			totalProperty: 'total',
			sortInfo: Garp.dataTypes[this.model].sortInfo || null,
			baseParams: {
				start: 0,
				limit: Garp.pageSize,
				query: this.query || null
			},
			api: {
				create: Ext.emptyFn,
				read: Garp[this.model].fetch || Ext.emptyFn,
				update: Ext.emptyFn,
				destroy: Ext.emptyFn
			}
		};
	},

	/**
	 * @function getGridCfg
	 * @param hideHeader
	 * @return defaultGridObj
	 */
	getGridCfg: function(hideHeaders){
		return {
			border: true,
			region: 'center',
			hideHeaders: hideHeaders,
			enableDragDrop: false,
			//ddGroup: 'dd',
			sm: new Ext.grid.RowSelectionModel({
				singleSelect: true
			}),
			cm: new Ext.grid.ColumnModel({
				defaults:{
					sortable: true
				},
				columns: (function(){
					var cols = [];
					var cmClone = Garp.dataTypes[this.model].columnModel;
					for (var c = 0, l = cmClone.length; c < l; c++) {
						var col = Ext.apply({},cmClone[c]);
						cols.push(col);
					}
					return cols;
				}).call(this)
			}),
			pageSize: Garp.pageSize,
			viewConfig: {
				scrollOffset: -1, // No reserved space for scrollbar. Share it with last column
				forceFit: true,
				autoFill: true
			}
		};
	},

	/**
	 * update Preview panel
	 */
	updatePreview: function(){
		var tpl = this.tplGrid.getSelectionModel().getSelected().get('name');
		var w = 170;
		var h = 170;
		var id = this.imgGrid.getSelectionModel().getSelected().get('id');
		this.previewpanel.update({
			IMAGES_CDN: IMAGES_CDN,
			BASE: BASE,
			tpl: tpl,
			id: id,
			width: w,
			height: h,
			'float': this.alignment.getForm().getValues().align
		});

	},

	/**
	 * clears a current selection
	 */
	clearSelection: function(){
		this.fireEvent('select', {
			selected: null
		});
		this.close();
	},

	/**
	 * initComponent
	 */
	initComponent: function(){
		this.addEvents('select');

		this.imgStore = new Ext.data.DirectStore(Ext.apply({}, {
			writer: new Ext.data.JsonWriter({
				paramsAsHash: false,
				writeAllFields: true,
				encode: false
			}),
			api: {
				create: Ext.emptyFn,
				read: Garp[this.model].fetch,
				update: Ext.emptyFn,
				destroy: Ext.emptyFn
			}
		}, this.getStoreCfg()));

		this.imgGrid = new Ext.grid.GridPanel(Ext.apply({}, {
			region: 'center',
			title: __('Available'),
			itemId: 'imgPanel',
			margins: '15 15 15 15',
			store: this.imgStore,
			bbar: new Ext.PagingToolbar({
				pageSize: Garp.pageSize,
				store: this.imgStore,
				beforePageText: '',
				displayInfo: false
			}),
			tbar: new Ext.ux.Searchbar({
				xtype: 'searchbar',
				store: this.imgStore
			}),
			listeners: {
				'rowdblclick': this.navHandler.createDelegate(this, [1])
			}
		}, this.getGridCfg(true)));

		this.tplGrid = new Ext.grid.GridPanel({
			itemId: 'tplPanel',
			margins: '15 15 0 15',
			title: __('Crop'),
			region: 'center',
			hideHeaders: true,
			store: new Ext.data.DirectStore({
				autoLoad: true,
				autoSave: false,
				autoDestroy: true,
				remoteSort: true,
				restful: true,
				root: 'rows',
				idProperty: 'id',
				fields: [{
					name: 'name'
				}, {
					name: 'w'
				}, {
					name: 'h'
				}],
				api: {
					create: Ext.emptyFn,
					read: Garp.CropTemplate.fetch,
					update: Ext.emptyFn,
					destroy: Ext.emptyFn
				}
			}),
			sm: new Ext.grid.RowSelectionModel({
				singleSelect: true,
				listeners: {
					'rowselect': this.updatePreview,
					scope: this
				}
			}),
			cm: new Ext.grid.ColumnModel({
				defaults: {
					sortable: true
				},
				columns: [{
					id: 'name',
					dataIndex: 'name',
					header: __('Name'),
					width: 200
				}, {
					id: 'w',
					dataIndex: 'w',
					header: __('Width')
				},{
					id: 'h',
					dataIndex: 'h',
					header: __('Height')
				}, {
					id: 'preview',
					dataIndex: 'name',
					header: __('Preview'),
					renderer: Garp.renderers.cropPreviewRenderer
				}]
			}),
			viewConfig: {
				scrollOffset: -1, // No reserved space for scrollbar. Share it with last column
				forceFit: true,
				autoFill: true
			},
			listeners: {
				'rowdblclick': this.navHandler.createDelegate(this, [1])
			}
		});

		// "validate" upon selection:
		this.imgGrid.getSelectionModel().on('selectionchange', this.navHandler.createDelegate(this,[0]));
		this.tplGrid.getSelectionModel().on('selectionchange', function(){
			this.nextBtn.setDisabled(this.tplGrid.getSelectionModel().getCount() === 0 );
		}, this);

		var headerTpl = new Ext.Template('
',__('Step'),' {step} ', __('of'), ' 2

{description}
');

		this.items = [{
			id: 'page-0',
			layout: 'border',
			bodyBorder: true,
			border: true,
			split: false,
			items: [{
				region: 'north',
				ref: '../northpanel',
				border: false,
				html: this.hideWizardText ? '' : headerTpl.apply({
					description: __('Specify an image or add new one'),
					step: 1
				}),
				margins: '10 15 0 15',
				bodyBorder: false,
				bbar: new Ext.Toolbar({
					style: 'border:0; margin-top: 10px;',
					border: false,
					items: [{
						iconCls: 'icon-new',
						ref: 'newBtn',
						hidden: !Garp.dataTypes[this.model].quickCreatable,
						text: __('New ' + Garp.dataTypes[this.model].text),
						handler: function(){
							var win = new Garp.RelateCreateWindow({
								model: this.model,
								iconCls: this.iconCls,
								title: this.title,
								listeners: {
									scope: this,
									'aftersave': function(rcwin, rec){
										this.imgStore.insert(0, rec);
										this.imgGrid.getSelectionModel().selectRecords([rec], true);
										this.imgStore.reload();
									}
								}
							});
							win.show();
						},
						scope: this
					}]
				})
			}, this.imgGrid]
		},{
			id: 'page-1',
			layout: 'border',
			bodyBorder: true,
			border: true,
			split: false,
			items: [{
				region: 'north',
				border: false,
				margins: '10 15 0 15',
				html: headerTpl.apply({
					description: __('Specify a crop template, set aligning and/or add a caption'),
					step: 2
				})
			},this.tplGrid, {
				region: 'east',
				margins: '15 15 0 0',
				ref: '../previewpanel',
				width: 260,
				bodyStyle: 'background:#e0e0e0;',
				title: __('Preview'),
				tpl: '
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
'
			},{
				region: 'south',
				border: false,
				bodyBorder: false,
				height: 145,
				margins: '15 15 15 15',
				bodyStyle: 'background-color: #eee',
				layout: 'border',
				defaults: {
					xtype: 'form',
					layout: 'form',
					labelWidth: 180,
					labelSeparator: '',
					border: true,
					bodyStyle: 'padding: 5px 0 0 5px'
				},
				items: [{
					title: __('Caption'),
					region: 'center',
					ref: '../../caption',
					bodyStyle: 'margin:0; padding:0;',
					items: [{
						border: false,
						fieldLabel: __('Caption'),
						hideLabel: true,
						xtype: 'textarea',
						style: 'border: 0;',
						height: 118,
						anchor: '100%',
						name: 'caption',
						value: this.captionValue
					}]
				},{
					title: __('Alignment'),
					region: 'east',
					width: 260,
					margins: '0 0 0 15',
					bodyStyle: 'padding: 10px',
					ref: '../../alignment',
					defaults:{
						handler: this.updatePreview,
						scope: this
					},
					items: [{
						fieldLabel: __('No alignment'),
						xtype: 'radio',
						name: 'align',
						checked: this.alignValue === '',
						inputValue: ''
					}, {
						fieldLabel: __('Align left'),
						xtype: 'radio',
						name: 'align',
						checked: this.alignValue == 'left',
						inputValue: 'left'
					}, {
						fieldLabel: __('Align right'),
						xtype: 'radio',
						name: 'align',
						checked: this.alignValue == 'right',
						inputValue: 'right'
					}]
				}]
			}]
		}];

		this.buttons = [{
			text: __('Previous'),
			ref: '../prevBtn',
			handler: this.navHandler.createDelegate(this, [-1])
		}, '->',{
			text: __('Cancel'),
			handler: function(){
				this.close();
			},
			scope: this
		},{
			text: __('Clear selection'),
			ref: '../clearBtn',
			hidden: true,
			handler: this.clearSelection.createDelegate(this)
		},{
			text: __('Next'),
			ref: '../nextBtn',
			handler: this.navHandler.createDelegate(this, [1])
		}];

		if(this.imgGridQuery){
			this.imgGrid.getStore().setBaseParam('query', this.imgGridQuery);
			this.imgGrid.getStore().on({
				load: {
					single: true,
					scope: this,
					fn: function(){
						this.imgGrid.getSelectionModel().selectFirstRow();
						this.navHandler(1); // go to page-2
					}
				}
			});
			var f = this.imgGrid.getTopToolbar().searchField;
			f.setValue(this.imgGridQuery.id);
			f.hasSearch = true;
			f.fireEvent('change');

		}

		Garp.ImagePickerWindow.superclass.initComponent.call(this);
		this.on('show', function(){
			this.navHandler(-1);
			if(!this.imgGridQuery){
				var keyNav = new Ext.KeyNav(this.imgGrid.getEl(), {
					'enter': this.navHandler.createDelegate(this, [1])
				});
			}
			this.imgStore.load();
			if(this.allowBlank){
				this.clearBtn.show();
			}
		},this);
	}
});
<commit_msg>Fixed align when editing image in ckeditor<commit_after>CKEDITOR.plugins.add('garpimages', {
	init: function(editor) {
		// Open an image dialog
		editor.addCommand('garpimageDialog', {
			exec: function(editor, data) {
				var command = this;

				var existingImage = {};
				if (data && data.existingImage) {
					existingImage = data.existingImage;
				}
				var elementToReplace;
				if (data && data.existingElement) {
					elementToReplace = data.existingElement;
				}

				// Open Extjs image picker
				var win = new Garp.ImagePickerWindow(existingImage);
				win.on('select', function(data) {
					// Insert image element
					var tpl = Garp.imageTpl;

					var imgHtml = tpl.apply({
						path: data.src,
						width: data.template.get('w'),
						height: data.template.get('h'),
						align: data.align,
						caption: data.caption || false
					});
					var element = CKEDITOR.dom.element.createFromHtml(imgHtml);

					element.setAttribute('contenteditable', false);

					// Insert or replace
					if (elementToReplace) {
						element.replace(elementToReplace);
					} else {
						editor.insertElement(element);
					}

					// Return to CKEditor
					editor.fire('afterCommandExec', {
						name: command.name,
						command: command
					});
				}, this);
				win.show();

			},
			async: true
		});

		// Open a dialog and replace the element with the results
		function editExistingImage(element) {
			if (!element) {
				return;
			}

			// Retrieve values from existing html
			var path = element.findOne('img') ? element.findOne('img').getAttribute('src') : element.getAttribute('src');
			path = path.split('/');
			if (path[path.length - 1] === '') {
				path.splice(path.length - 1, 1); // remove last ,if it's a trailing slash
			}
			var fileId = path[path.length - 1];
			var tplName = path[path.length - 2];
			var caption = element.findOne('figcaption') ? element.findOne('figcaption').getText() : null;
			var align = element.getStyle("float") || element.hasClass('right') ? 'right' : element.hasClass('left') ? 'left' : '';

			// Exec with parameters from existing image
			editor.getCommand('garpimageDialog').exec({
				existingImage: {
					imgGridQuery: {
						id: fileId
					},
					cropTemplateName: tplName,
					captionValue: caption,
					alignValue: align == 'none' ? '' : align
				},
				existingElement: element
			});
		}

		// Get a valid image element from a valid image element or its children
		function getGarpImageElement(element) {
			var parent = element.getParent();
			if (element.getName() !== "figure" && parent.getName() === "figure") {
				element = element.getParent();
			}
			if (element.getName() !== "figure" && element.getName() !== "img") {
				return;
			}
			return element;
		}

		// Add remove option to right clicked 'images'
		// The last element to be right clicked
		var rightclickedElement;

		// Removing the image from the editor after right click
		editor.addCommand('garpimageRemove', {
			exec: function(editor) {
				rightclickedElement.remove();
			}
		});
		// Editing an image after rightclick
		editor.addCommand('garpimageEdit', {
			exec: function(editor) {
				editExistingImage(rightclickedElement);
			}
		});

		// Open dialog on doubleclick
		editor.on('doubleclick', function(evt) {
			var element = getGarpImageElement(evt.data.element);
			editExistingImage(element);
		});

		// Add insert button to top bar
		editor.ui.addButton('Garpimage', {
			label: 'Insert Image',
			command: 'garpimageDialog',
			icon: this.path + 'picture.png',
			toolbar: 'insert'
		});

		// Define the menu options
		editor.addMenuGroup('garp');
		editor.addMenuItem('GarpimageRemove', {
			label: 'Remove Image',
			command: 'garpimageRemove',
			icon: this.path + 'picture.png',
			group: 'garp'
		});
		editor.addMenuItem('GarpimageEdit', {
			label: 'Edit Image',
			command: 'garpimageEdit',
			icon: this.path + 'picture.png',
			group: 'garp'
		});

		// Show option when right clicking on an image
		editor.contextMenu.addListener(function(element, selection) {
			rightclickedElement = getGarpImageElement(element);
			if (!rightclickedElement) {
				return;
			}
			return {
				"GarpimageRemove": CKEDITOR.TRISTATE_ON,
				"GarpimageEdit": CKEDITOR.TRISTATE_ON
			};
		});
	}
});

<|endoftext|>


<commit_before>'use strict';

// https://codelabs.developers.google.com/codelabs/webrtc-web/#6
var api = "";

$("#start").click(function(){
    var napi = document.getElementById("api").value;
    if (napi != "/") {
        api = "http://" + napi;
    }

    new Promise(function(resolve, reject){
        navigator.webkitGetUserMedia({
            video:true,audio:true
        },function(stream){
            resolve(stream);
        }, function(error){
            reject(error);
        });
    }).then(function(stream){
        var lv = document.getElementById("local");
        lv.src = window.URL.createObjectURL(stream);
        console.log("[navigator.webkitGetUserMedia] lv.src= " + lv.src);

        // Use a peer connection to share stream to responder.
        var conn = new window.webkitRTCPeerConnection({iceServers:[{urls:["turn:stun.ossrs.net"], username:"guest", credential:"12345678"}]});
        conn.addStream(stream);
        console.log("[conn.addStream] add stream to peer connection");

        return conn;
    }).then(function(conn){
        // Render the remote initiator stream.
        conn.onaddstream = function(e) {
            var rv = document.getElementById("remote");
            rv.src = window.URL.createObjectURL(e.stream);
            console.log("[conn.onaddstream] rv.src=remoteStream " + rv.src);
        };

        callInitiator(conn, api);
    });
});

function callInitiator(conn, api) {
    Promise.all([new Promise(function(resolve, reject){
        // Request the candidates of initiator.
        var requestCandidates = function() {
            $.ajax({
                type:"GET", async:true, url:api+"/api/webrtc/icandidates", contentType:"application/json",
                success:function(data){
                    data = JSON.parse(data) || [];

                    // Wait util the rcandidates are completed, we should got 2+ candidates.
                    if (data.length < 2) {
                        setTimeout(requestCandidates, 1000);
                        return;
                    }

                    resolve(data);
                }, error:function(xhr,err){
                    reject(err);
                }
            });
        };
        requestCandidates();
    }), new Promise(function(resolve, reject){
        // Query the offer of initiator from signaling server.
        $.ajax({
            type:"GET", async:true, url:api+"/api/webrtc/offer", contentType:"application/json",
            success:function(data){
                var offer = JSON.parse(data);
                offer = offer[0];
                offer = JSON.parse(offer);
                offer = unescapeOffer(offer);
                resolve(offer);
            },
            error: function(xhr, err) {
                reject(err);
            }
        });
    })]).then(function([candidates,offer]){
        // Transmit the responder candidates to signaling server.
        conn.onicecandidate = function(e) {
            if (!e.candidate) {
                return;
            }
            if (e.candidate.candidate.indexOf("relay") == -1) {
                console.log("[conn.onicecandidate] ignore " + e.candidate.candidate);
                return;
            }
            console.log("[conn.onicecandidate] " + e.candidate.candidate);
            console.log(e.candidate);

            var data = JSON.stringify(escapeCandicate(e.candidate));
            $.ajax({type:"POST", async:true, url:api+"/api/webrtc/rcandidates", contentType:"application/json", data:data});
        };

        // once got the peer offer(SDP), we can generate our answer(SDP).
        conn.setRemoteDescription(offer); // trigger conn.onaddstream
        console.log("[onRemoteGotOffer] Got offer " + offer.sdp.length + "B sdp as bellow:");
        console.log(offer); console.log(offer.sdp);

        // Since the 'remote' side has no media stream we need
        // to pass in the right constraints in order for it to
        // accept the incoming offer of audio and video.
        conn.createAnswer(function(answer){
            // For chrome new API, we can delay set the TURN.
            //conn.setConfiguration({iceServers:[{urls:["turn:stun.ossrs.net"], username:"guest", credential:"12345678"}]});

            conn.setLocalDescription(answer); // trigger conn.onicecandidate().
            console.log("[conn.createAnswer] answer " + answer.sdp.length + "B sdp as bellow:");
            console.log(answer); console.log(answer.sdp);

            var data = JSON.stringify(escapeOffer(answer));
            $.ajax({type:"POST", async:true, url:api+"/api/webrtc/answer", contentType:"application/json", data:data});

            // before addIceCandidate, we must setRemoteDescription
            for (var i = 0; i < candidates.length; i++) {
                var candidate = unescapeCandicate(JSON.parse(candidates[i]));
                conn.addIceCandidate(new window.RTCIceCandidate(candidate));
                console.log("[requestCandidates] Got initiator candidate " + JSON.stringify(candidate));
                console.log(candidate);
            }
        }, function(error){
            console.log(error);
        });
    });
}

function escapeOffer(offer) {
    return {type:offer.type, sdp:escape(offer.sdp)};
}

function escapeCandicate(candidate) {
    return {sdpMid:candidate.sdpMid, sdpMLineIndex:candidate.sdpMLineIndex, candidate:escape(candidate.candidate)};
}

function unescapeOffer(offer) {
    return {type:offer.type, sdp:unescape(offer.sdp)};
}

function unescapeCandicate(candidate) {
    return {sdpMid:candidate.sdpMid, sdpMLineIndex:candidate.sdpMLineIndex, candidate:unescape(candidate.candidate)};
}<commit_msg>Fix responder candidates missing<commit_after>'use strict';

// https://codelabs.developers.google.com/codelabs/webrtc-web/#6
var api = "";

$("#start").click(function(){
    var napi = document.getElementById("api").value;
    if (napi != "/") {
        api = "http://" + napi;
    }

    new Promise(function(resolve, reject){
        navigator.webkitGetUserMedia({
            video:true,audio:true
        },function(stream){
            resolve(stream);
        }, function(error){
            reject(error);
        });
    }).then(function(stream){
        var lv = document.getElementById("local");
        lv.src = window.URL.createObjectURL(stream);
        console.log("[navigator.webkitGetUserMedia] lv.src= " + lv.src);

        // Use a peer connection to share stream to responder.
        var conn = new window.webkitRTCPeerConnection({iceServers:[{urls:["turn:stun.ossrs.net"], username:"guest", credential:"12345678"}]});
        conn.addStream(stream);
        console.log("[conn.addStream] add stream to peer connection");

        return conn;
    }).then(function(conn){
        // Render the remote initiator stream.
        conn.onaddstream = function(e) {
            var rv = document.getElementById("remote");
            rv.src = window.URL.createObjectURL(e.stream);
            console.log("[conn.onaddstream] rv.src=remoteStream " + rv.src);
        };

        callInitiator(conn, api);
    });
});

function callInitiator(conn, api) {
    Promise.all([new Promise(function(resolve, reject){
        // Request the candidates of initiator.
        var requestCandidates = function() {
            $.ajax({
                type:"GET", async:true, url:api+"/api/webrtc/icandidates", contentType:"application/json",
                success:function(data){
                    data = JSON.parse(data) || [];

                    // Wait util the rcandidates are completed, we should got 2+ candidates.
                    if (data.length < 2) {
                        setTimeout(requestCandidates, 1000);
                        return;
                    }

                    resolve(data);
                }, error:function(xhr,err){
                    reject(err);
                }
            });
        };
        requestCandidates();
    }), new Promise(function(resolve, reject){
        // Query the offer of initiator from signaling server.
        $.ajax({
            type:"GET", async:true, url:api+"/api/webrtc/offer", contentType:"application/json",
            success:function(data){
                var offer = JSON.parse(data);
                offer = offer[0];
                offer = JSON.parse(offer);
                offer = unescapeOffer(offer);
                resolve(offer);
            },
            error: function(xhr, err) {
                reject(err);
            }
        });
    })]).then(function([candidates,offer]){
        // once got the peer offer(SDP), we can generate our answer(SDP).
        conn.setRemoteDescription(offer); // trigger conn.onaddstream
        console.log("[onRemoteGotOffer] Got offer " + offer.sdp.length + "B sdp as bellow:");
        console.log(offer); console.log(offer.sdp);

        // before addIceCandidate, we must setRemoteDescription
        for (var i = 0; i < candidates.length; i++) {
            var candidate = unescapeCandicate(JSON.parse(candidates[i]));
            conn.addIceCandidate(new window.RTCIceCandidate(candidate));
            console.log("[requestCandidates] Got initiator candidate " + JSON.stringify(candidate));
            console.log(candidate);
        }

        // Transmit the responder candidates to signaling server.
        conn.onicecandidate = function(e) {
            if (!e.candidate) {
                return;
            }
            if (e.candidate.candidate.indexOf("relay") == -1) {
                console.log("[conn.onicecandidate] ignore " + e.candidate.candidate);
                return;
            }
            console.log("[conn.onicecandidate] " + e.candidate.candidate);
            console.log(e.candidate);

            var data = JSON.stringify(escapeCandicate(e.candidate));
            $.ajax({type:"POST", async:true, url:api+"/api/webrtc/rcandidates", contentType:"application/json", data:data});
        };

        // Since the 'remote' side has no media stream we need
        // to pass in the right constraints in order for it to
        // accept the incoming offer of audio and video.
        conn.createAnswer(function(answer){
            // For chrome new API, we can delay set the TURN.
            //conn.setConfiguration({iceServers:[{urls:["turn:stun.ossrs.net"], username:"guest", credential:"12345678"}]});

            conn.setLocalDescription(answer); // trigger conn.onicecandidate().
            console.log("[conn.createAnswer] answer " + answer.sdp.length + "B sdp as bellow:");
            console.log(answer); console.log(answer.sdp);

            var data = JSON.stringify(escapeOffer(answer));
            $.ajax({type:"POST", async:true, url:api+"/api/webrtc/answer", contentType:"application/json", data:data});
        }, function(error){
            console.log(error);
        });
    });
}

function escapeOffer(offer) {
    return {type:offer.type, sdp:escape(offer.sdp)};
}

function escapeCandicate(candidate) {
    return {sdpMid:candidate.sdpMid, sdpMLineIndex:candidate.sdpMLineIndex, candidate:escape(candidate.candidate)};
}

function unescapeOffer(offer) {
    return {type:offer.type, sdp:unescape(offer.sdp)};
}

function unescapeCandicate(candidate) {
    return {sdpMid:candidate.sdpMid, sdpMLineIndex:candidate.sdpMLineIndex, candidate:unescape(candidate.candidate)};
}<|endoftext|>


<commit_before>// self-made adapter for socket.io
(function(){

	// export module
	window.sock = {};

	// underlying socket
    var socket = io();

    // socket connected
    socket.on('connect', function(){
    	// socket id
    	sock.id = socket.id;
        console.log('[Socket] connected. ID:', sock.id);
    });

    // receive data
    sock.on = function(event, fn){
        socket.on(event, function(data1, data2){
            console.log('[Socket] got', event, data1, data2);
            fn(data1, data2);
        });
    };

    // emit data
    sock.emit = function(event, data1, data2){
    	socket.emit(event, data1, data2);
        console.log('[Socket] send', event, data1, data2);
    };

    // disconnect
    sock.disconnect = function(){
    	socket.disconnect();
        console.log('[Socket] disconnected.');
    };

    // original socket object
    sock.raw = socket;

    // when page closes, disconnect socket
    window.onbeforeunload = function(e){
        sock.emit('disconnect');
    };

})();

<commit_msg>unnecessary<commit_after>// self-made adapter for socket.io
(function(){

	// export module
	window.sock = {};

	// underlying socket
    var socket = io();

    // socket connected
    socket.on('connect', function(){
    	// socket id
    	sock.id = socket.id;
        console.log('[Socket] connected. ID:', sock.id);
    });

    // receive data
    sock.on = function(event, fn){
        socket.on(event, function(data){
            console.log('[Socket] got', event, data);
            fn(data);
        });
    };

    // emit data
    sock.emit = function(event, data){
    	socket.emit(event, data);
        console.log('[Socket] send', event, data);
    };

    // disconnect
    sock.disconnect = function(){
    	socket.disconnect();
        console.log('[Socket] disconnected.');
    };

    // original socket object
    sock.raw = socket;

    // when page closes, disconnect socket
    window.onbeforeunload = function(e){
        sock.emit('disconnect');
    };

})();

<|endoftext|>


<commit_before>define(["js/html/HtmlElement", "js/core/Content", "underscore"], function (HtmlElement, Content, _) {
    return HtmlElement.inherit({

        defaults: {
            /***
             * the unique identifier for the placeholder within this component
             *
             * @type String
             * @required
             */
            name: null
        },

        render: function () {
            if (this.isRendered()) {
                return this.$el;
            }

            this.$textNode = this.$stage.$document.createTextNode("");

            var el;
            if (this.$.content) {
                el = this._getDomElementForContent(this.$.content);
            }
            if (!el) {
                el = this.$textNode;
            }

            this.$el = el;

            return this.$el;
        },

        clear: function () {
            this.set({content: null});
        },

        _renderContent: function (content) {

            var parentNode = this.$el.parentNode;
            if (parentNode) {
                var el = this._getDomElementForContent(content);
                if (el) {
                    parentNode.replaceChild(el, this.$el);
                } else if (this.$textNode !== this.$el) {
                    parentNode.replaceChild(this.$textNode, this.$el);
                    this.$el = this.$textNode;
                }
            }

        },
        _getDomElementForContent: function (content) {
            var children;
            if (content instanceof Content) {
                children = content.$children;
            } else if (_.isArray(content)) {
                children = content;
            } else if (content) {
                children = [content];
            } else {
                children = [];
            }
            var child, el;
            for (var i = 0; i < children.length; i++) {
                child = children[i];
                if (child.render) {
                    el = child.render();
                    return el;
                }
            }

            return null;

        }
    });
});<commit_msg>fixed rendering of invisible PlaceHolder<commit_after>define(["js/html/HtmlElement", "js/core/Content", "underscore"], function (HtmlElement, Content, _) {
    return HtmlElement.inherit({

        defaults: {
            /***
             * the unique identifier for the placeholder within this component
             *
             * @type String
             * @required
             */
            name: null
        },

        render: function () {
            if (this.isRendered()) {
                return this.$el;
            }

            this.$textNode = this.$stage.$document.createTextNode("");

            var el;
            if (this.$.content) {
                el = this._getDomElementForContent(this.$.content);
            }
            if (!el) {
                el = this.$textNode;
            }

            this.$el = el;

            return this.$el;
        },

        clear: function () {
            this.set({content: null});
        },

        _renderContent: function (content) {

            var parentNode = this.$el.parentNode;
            if (parentNode) {
                var el = this._getDomElementForContent(content);
                if (el) {
                    parentNode.replaceChild(el, this.$el);
                    this.$el = el;
                } else if (this.$textNode !== this.$el) {
                    parentNode.replaceChild(this.$textNode, this.$el);
                    this.$el = this.$textNode;
                }
            }

        },
        _getDomElementForContent: function (content) {
            var children;
            if (content instanceof Content) {
                children = content.$children;
            } else if (_.isArray(content)) {
                children = content;
            } else if (content) {
                children = [content];
            } else {
                children = [];
            }
            var child, el;
            for (var i = 0; i < children.length; i++) {
                child = children[i];
                if (child.render) {
                    el = child.render();
                    return el;
                }
            }

            return null;

        }
    });
});<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const path = require('path');
const ini = require('ini');
const {dialog} = require('electron');
const constants = require('./constants');
const log = require('./log-module').getLog();

const setIniOnPlayCharacter = (userdat, character) => {
	return new Promise(function(resolve, reject) {
		let config = ini.parse(fs.readFileSync(userdat.value, constants.utf8));
		let xy;
		try {
			xy = character.resolution.split('x');
			config.main.screen_width = xy[0];
			config.main.screen_height = xy[1];
		}
		catch (e) {
			log.error(e);
			dialog.showErrorBox(constants.error, e.toString());
		}
		config.main.windowed = character.windowed ? 1 : 0;
		config.main.fullscreen_windowed = character.fullscreen_windowed ? 1 : 0;
		config.keyboard.forward_breaks_runlock = character.forward_breaks_runlock ? 1 : 0;
		fs.writeFileSync(path.dirname(userdat.value) + "\\user.dat", ini.stringify(config, {}));
		resolve();
	});
}

const setIniOnPlayAccount = (userdat, account) => {
	return new Promise(function(resolve, reject) {
		let config = ini.parse(fs.readFileSync(userdat.value, constants.utf8));
		let xy;
		try {
			xy = account.resolution.split('x');
			config.main.screen_width = xy[0];
			config.main.screen_height = xy[1];
		}
		catch (e) {
			log.error(e);
			dialog.showErrorBox(constants.error, e.toString());
		}
		config.main.windowed = account.windowed ? 1 : 0;
		fs.writeFileSync(path.dirname(userdat.value) + '\\user.dat', ini.stringify(config, {}));
		resolve();
	});
}

const setIniOnPlayTeam = (userdat, res, windowed) => {
	return new Promise(function(resolve, reject) {
		let config = ini.parse(fs.readFileSync(userdat.value, constants.utf8));
		let xy;
		try {
			xy = res.split('x');
			config.main.screen_width = xy[0];
			config.main.screen_height = xy[1];
		}
		catch (e) {
			log.error(e);
			dialog.showErrorBox(constants.error, e.toString());
		}
		config.main.windowed = windowed ? 1 : 0;
		fs.writeFileSync(path.dirname(userdat.value) + "\\user.dat", ini.stringify(config, {}));
		resolve();
	});
}

module.exports = {setIniOnPlayCharacter, setIniOnPlayAccount, setIniOnPlayTeam};
<commit_msg>https://github.com/simon387/daocstarter/issues/18h<commit_after>'use strict';

const fs = require('fs');
const path = require('path');
const ini = require('ini');
const {dialog} = require('electron');
const constants = require('./constants');
const log = require('./log-module').getLog();

const setIniOnPlayCharacter = (userdat, character) => {
	return new Promise(function(resolve, reject) {
		let config = ini.parse(fs.readFileSync(userdat.value, constants.utf8));
		let xy;
		try {
			xy = character.resolution.split('x');
			config.main.screen_width = xy[0];
			config.main.screen_height = xy[1];
			config.main.fullscreen_windowed = character.windowed;
		}
		catch (e) {
			log.error(e);
			dialog.showErrorBox(constants.error, e.toString());
		}
		config.main.windowed = character.windowed ? 1 : 0;
		config.main.fullscreen_windowed = character.fullscreen_windowed ? 1 : 0;
		config.keyboard.forward_breaks_runlock = character.forward_breaks_runlock ? 1 : 0;
		fs.writeFileSync(path.dirname(userdat.value) + "\\user.dat", ini.stringify(config, {}));
		resolve();
	});
}

const setIniOnPlayAccount = (userdat, account) => {
	return new Promise(function(resolve, reject) {
		let config = ini.parse(fs.readFileSync(userdat.value, constants.utf8));
		let xy;
		try {
			xy = account.resolution.split('x');
			config.main.screen_width = xy[0];
			config.main.screen_height = xy[1];
			config.main.fullscreen_windowed = account.windowed;
		}
		catch (e) {
			log.error(e);
			dialog.showErrorBox(constants.error, e.toString());
		}
		config.main.windowed = account.windowed ? 1 : 0;
		fs.writeFileSync(path.dirname(userdat.value) + '\\user.dat', ini.stringify(config, {}));
		resolve();
	});
}

const setIniOnPlayTeam = (userdat, res, windowed) => {
	return new Promise(function(resolve, reject) {
		let config = ini.parse(fs.readFileSync(userdat.value, constants.utf8));
		let xy;
		try {
			xy = res.split('x');
			config.main.screen_width = xy[0];
			config.main.screen_height = xy[1];
		}
		catch (e) {
			log.error(e);
			dialog.showErrorBox(constants.error, e.toString());
		}
		config.main.windowed = windowed ? 1 : 0;
		fs.writeFileSync(path.dirname(userdat.value) + "\\user.dat", ini.stringify(config, {}));
		resolve();
	});
}

module.exports = {setIniOnPlayCharacter, setIniOnPlayAccount, setIniOnPlayTeam};
<|endoftext|>


<commit_before>/**
 * @ngdoc page
 * @name keyboard
 * @module ionic
 * @description
 * On both Android and iOS, Ionic will attempt to prevent the keyboard from obscuring inputs and
 * focusable elements when it appears by scrolling them into view.  In order for this to work,
 * any focusable elements must be within a [Scroll View](http://ionicframework.com/docs/api/directive/ionScroll/)
 * or a directive such as [Content](http://ionicframework.com/docs/api/directive/ionContent/) that has a Scroll View.
 *
 * It will also attempt to prevent the native overflow scrolling on focus, which can cause layout issues such as
 * pushing headers up and out of view.
 *
 * The keyboard fixes work best in conjunction with the [Ionic Keyboard Plugin](https://github.com/driftyco/ionic-plugins-keyboard),
 * although it will perform reasonably well without.  However, if you are using Cordova there is no reason not to use the plugin.
 *
 * ### Hide when keyboard shows
 *
 * To hide an element when the keyboard is open, add the class `hide-on-keyboard-open`.
 *
 * ```html
 * <div class="hide-on-keyboard-open">
 *   <div id="google-map"></div>
 * </div>
 * ```
 *
 *
 * ### Android Notes
 * - If your app is running in fullscreen, i.e. you have `<preference name="Fullscreen" value="true" />` in your `config.xml` file
 * you will need to set `ionic.Platform.isFullScreen = true` manually.
 *
 * - You can configure the behavior of the web view when the keyboard shows by setting
 *   [android:windowSoftInputMode](http://developer.android.com/reference/android/R.attr.html#windowSoftInputMode) to either `adjustPan`, `adjustResize` or `adjustNothing` in your app's activity in `AndroidManifest.xml`. `adjustResize` is the recommended setting for Ionic, but if for some reason you do use `adjustPan` you will need to set `ionic.Platform.isFullScreen = true`.
 *
 *   ```xml
 *   <activity android:windowSoftInputMode="adjustResize">
 *
 *   ```
 *
 * ### iOS Notes
 * - if you are not using the keyboard plugin, switching to inputs below the keyboard using the accessory bar will automatically use the native browser's
 * overflow scrolling and push headers out of view
 *
 */

/*
IONIC KEYBOARD
---------------

*/

var keyboardViewportHeight = window.innerHeight;
var keyboardIsOpen;
var keyboardActiveElement;
var keyboardFocusOutTimer;
var keyboardFocusInTimer;
var keyboardLastShow = 0;

var KEYBOARD_OPEN_CSS = 'keyboard-open';
var SCROLL_CONTAINER_CSS = 'scroll';

ionic.keyboard = {
  isOpen: false,
  height: null,
  landscape: false,
};

function keyboardInit() {
  if( keyboardHasPlugin() ) {
    window.addEventListener('native.showkeyboard', keyboardNativeShow);
    window.addEventListener('native.hidekeyboard', keyboardFocusOut);
  }
  else {
    document.body.addEventListener('focusout', keyboardFocusOut);
  }

  document.body.addEventListener('ionic.focusin', keyboardBrowserFocusIn);
  document.body.addEventListener('focusin', keyboardBrowserFocusIn);

  document.body.addEventListener('orientationchange', keyboardOrientationChange);

  document.removeEventListener('touchstart', keyboardInit);
}

function keyboardNativeShow(e) {
  clearTimeout(keyboardFocusOutTimer);
  ionic.keyboard.height = e.keyboardHeight;
}

function keyboardBrowserFocusIn(e) {
  if( !e.target || !ionic.tap.isTextInput(e.target) || !keyboardIsWithinScroll(e.target) ) return;

  document.addEventListener('keydown', keyboardOnKeyDown, false);

  document.body.scrollTop = 0;
  document.body.querySelector('.scroll-content').scrollTop = 0;

  keyboardActiveElement = e.target;

  keyboardSetShow(e);
}

function keyboardSetShow(e) {
  clearTimeout(keyboardFocusInTimer);
  clearTimeout(keyboardFocusOutTimer);

  keyboardFocusInTimer = setTimeout(function(){
    if ( keyboardLastShow + 350 > Date.now() ) return;
    keyboardLastShow = Date.now();
    var keyboardHeight;
    var elementBounds = keyboardActiveElement.getBoundingClientRect();
    var count = 0;

    var pollKeyboardHeight = setInterval(function(){

      keyboardHeight = keyboardGetHeight();
      if (count > 10){
        clearInterval(pollKeyboardHeight);
        //waited long enough, just guess
        keyboardHeight = 275;
      }
      if (keyboardHeight){
        keyboardShow(e.target, elementBounds.top, elementBounds.bottom, keyboardViewportHeight, keyboardHeight);
        clearInterval(pollKeyboardHeight);
      }
      count++;

    }, 100);
  }, 32);
}

function keyboardShow(element, elementTop, elementBottom, viewportHeight, keyboardHeight) {
  var details = {
    target: element,
    elementTop: Math.round(elementTop),
    elementBottom: Math.round(elementBottom),
    keyboardHeight: keyboardHeight,
    viewportHeight: viewportHeight
  };

  details.hasPlugin = keyboardHasPlugin();

  details.contentHeight = viewportHeight - keyboardHeight;

  console.log('keyboardShow', keyboardHeight, details.contentHeight);

  // figure out if the element is under the keyboard
  details.isElementUnderKeyboard = (details.elementBottom > details.contentHeight);

  ionic.keyboard.isOpen = true;

  // send event so the scroll view adjusts
  keyboardActiveElement = element;
  ionic.trigger('scrollChildIntoView', details, true);

  ionic.requestAnimationFrame(function(){
    document.body.classList.add(KEYBOARD_OPEN_CSS);
  });

  // any showing part of the document that isn't within the scroll the user
  // could touchmove and cause some ugly changes to the app, so disable
  // any touchmove events while the keyboard is open using e.preventDefault()
  document.addEventListener('touchmove', keyboardPreventDefault, false);

  return details;
}

function keyboardFocusOut(e) {
  clearTimeout(keyboardFocusOutTimer);

  keyboardFocusOutTimer = setTimeout(keyboardHide, 350);
}

function keyboardHide() {
  console.log('keyboardHide');
  ionic.keyboard.isOpen = false;

  ionic.trigger('resetScrollView', {
    target: keyboardActiveElement
  }, true);

  ionic.requestAnimationFrame(function(){
    document.body.classList.remove(KEYBOARD_OPEN_CSS);
  });

  // the keyboard is gone now, remove the touchmove that disables native scroll
  document.removeEventListener('touchmove', keyboardPreventDefault);
  document.removeEventListener('keydown', keyboardOnKeyDown);
}

function keyboardUpdateViewportHeight() {
  if( window.innerHeight > keyboardViewportHeight ) {
    keyboardViewportHeight = window.innerHeight;
  }
}

function keyboardOnKeyDown(e) {
  if( ionic.scroll.isScrolling ) {
    keyboardPreventDefault(e);
  }
}

function keyboardPreventDefault(e) {
  if( e.target.tagName !== 'TEXTAREA' ) {
    e.preventDefault();
  }
}

function keyboardOrientationChange() {
  var updatedViewportHeight = window.innerHeight;

  //too slow, have to wait for updated height
  if (updatedViewportHeight === keyboardViewportHeight){
    var count = 0;
    var pollViewportHeight = setInterval(function(){
      //give up
      if (count > 10){
        clearInterval(pollViewportHeight);
      }

      updatedViewportHeight = window.innerHeight;

      if (updatedViewportHeight !== keyboardViewportHeight){
        if (updatedViewportHeight < keyboardViewportHeight){
          ionic.keyboard.landscape = true;
        }
        else {
          ionic.keyboard.landscape = false;
        }
        keyboardViewportHeight = updatedViewportHeight;
        clearInterval(pollViewportHeight);
      }
      count++;

    }, 50);
  }
  else {
    keyboardViewportHeight = updatedViewportHeight;
  }
}

function keyboardGetHeight() {
  // check if we are already have a keyboard height from the plugin
  if ( ionic.keyboard.height ) {
    return ionic.keyboard.height;
  }

  if ( ionic.Platform.isAndroid() ){
    //should be using the plugin, no way to know how big the keyboard is, so guess
    if ( ionic.Platform.isFullScreen ){
      return 275;
    }
    //otherwise, wait for the screen to resize
    if ( window.innerHeight < keyboardViewportHeight ){
      return keyboardViewportHeight - window.innerHeight;
    }
    else {
      return 0;
    }
  }

  // fallback for when its the webview without the plugin
  // or for just the standard web browser
  if( ionic.Platform.isIOS() ) {
    if ( ionic.keyboard.landscape ){
      return 206;
    }

    if (!ionic.Platform.isWebView()){
      return 216;
    }

    return 260;
  }

  // safe guess
  return 275;
}

function keyboardIsWithinScroll(ele) {
  while(ele) {
    if(ele.classList.contains(SCROLL_CONTAINER_CSS)) {
      return true;
    }
    ele = ele.parentElement;
  }
  return false;
}

function keyboardHasPlugin() {
  return !!(window.cordova && cordova.plugins && cordova.plugins.Keyboard);
}

ionic.Platform.ready(function() {
  keyboardUpdateViewportHeight();

  // Android sometimes reports bad innerHeight on window.load
  // try it again in a lil bit to play it safe
  setTimeout(keyboardUpdateViewportHeight, 999);

  // only initialize the adjustments for the virtual keyboard
  // if a touchstart event happens
  document.addEventListener('touchstart', keyboardInit, false);
});

<commit_msg>fix(keyboard): add link to plugin repo<commit_after>/**
 * @ngdoc page
 * @name keyboard
 * @module ionic
 * @description
 * On both Android and iOS, Ionic will attempt to prevent the keyboard from obscuring inputs and
 * focusable elements when it appears by scrolling them into view.  In order for this to work,
 * any focusable elements must be within a [Scroll View](http://ionicframework.com/docs/api/directive/ionScroll/)
 * or a directive such as [Content](http://ionicframework.com/docs/api/directive/ionContent/) that has a Scroll View.
 *
 * It will also attempt to prevent the native overflow scrolling on focus, which can cause layout issues such as
 * pushing headers up and out of view.
 *
 * The keyboard fixes work best in conjunction with the [Ionic Keyboard Plugin](https://github.com/driftyco/ionic-plugins-keyboard),
 * although it will perform reasonably well without.  However, if you are using Cordova there is no reason not to use the plugin.
 *
 * ### Hide when keyboard shows
 *
 * To hide an element when the keyboard is open, add the class `hide-on-keyboard-open`.
 *
 * ```html
 * <div class="hide-on-keyboard-open">
 *   <div id="google-map"></div>
 * </div>
 * ```
 * ----------
 *
 * ### Plugin Usage
 * Information on using the plugin can be found at [https://github.com/driftyco/ionic-plugins-keyboard](https://github.com/driftyco/ionic-plugins-keyboard).
 *
 * ----------
 *
 * ### Android Notes
 * - If your app is running in fullscreen, i.e. you have `<preference name="Fullscreen" value="true" />` in your `config.xml` file
 * you will need to set `ionic.Platform.isFullScreen = true` manually.
 *
 * - You can configure the behavior of the web view when the keyboard shows by setting
 *   [android:windowSoftInputMode](http://developer.android.com/reference/android/R.attr.html#windowSoftInputMode) to either `adjustPan`, `adjustResize` or `adjustNothing` in your app's activity in `AndroidManifest.xml`. `adjustResize` is the recommended setting for Ionic, but if for some reason you do use `adjustPan` you will need to set `ionic.Platform.isFullScreen = true`.
 *
 *   ```xml
 *   <activity android:windowSoftInputMode="adjustResize">
 *
 *   ```
 *
 * ### iOS Notes
 * - if you are not using the keyboard plugin, switching to inputs below the keyboard using the accessory bar will automatically use the native browser's
 * overflow scrolling and push headers out of view
 *
 */

/*
IONIC KEYBOARD
---------------

*/

var keyboardViewportHeight = window.innerHeight;
var keyboardIsOpen;
var keyboardActiveElement;
var keyboardFocusOutTimer;
var keyboardFocusInTimer;
var keyboardLastShow = 0;

var KEYBOARD_OPEN_CSS = 'keyboard-open';
var SCROLL_CONTAINER_CSS = 'scroll';

ionic.keyboard = {
  isOpen: false,
  height: null,
  landscape: false,
};

function keyboardInit() {
  if( keyboardHasPlugin() ) {
    window.addEventListener('native.showkeyboard', keyboardNativeShow);
    window.addEventListener('native.hidekeyboard', keyboardFocusOut);
  }
  else {
    document.body.addEventListener('focusout', keyboardFocusOut);
  }

  document.body.addEventListener('ionic.focusin', keyboardBrowserFocusIn);
  document.body.addEventListener('focusin', keyboardBrowserFocusIn);

  document.body.addEventListener('orientationchange', keyboardOrientationChange);

  document.removeEventListener('touchstart', keyboardInit);
}

function keyboardNativeShow(e) {
  clearTimeout(keyboardFocusOutTimer);
  ionic.keyboard.height = e.keyboardHeight;
}

function keyboardBrowserFocusIn(e) {
  if( !e.target || !ionic.tap.isTextInput(e.target) || !keyboardIsWithinScroll(e.target) ) return;

  document.addEventListener('keydown', keyboardOnKeyDown, false);

  document.body.scrollTop = 0;
  document.body.querySelector('.scroll-content').scrollTop = 0;

  keyboardActiveElement = e.target;

  keyboardSetShow(e);
}

function keyboardSetShow(e) {
  clearTimeout(keyboardFocusInTimer);
  clearTimeout(keyboardFocusOutTimer);

  keyboardFocusInTimer = setTimeout(function(){
    if ( keyboardLastShow + 350 > Date.now() ) return;
    keyboardLastShow = Date.now();
    var keyboardHeight;
    var elementBounds = keyboardActiveElement.getBoundingClientRect();
    var count = 0;

    var pollKeyboardHeight = setInterval(function(){

      keyboardHeight = keyboardGetHeight();
      if (count > 10){
        clearInterval(pollKeyboardHeight);
        //waited long enough, just guess
        keyboardHeight = 275;
      }
      if (keyboardHeight){
        keyboardShow(e.target, elementBounds.top, elementBounds.bottom, keyboardViewportHeight, keyboardHeight);
        clearInterval(pollKeyboardHeight);
      }
      count++;

    }, 100);
  }, 32);
}

function keyboardShow(element, elementTop, elementBottom, viewportHeight, keyboardHeight) {
  var details = {
    target: element,
    elementTop: Math.round(elementTop),
    elementBottom: Math.round(elementBottom),
    keyboardHeight: keyboardHeight,
    viewportHeight: viewportHeight
  };

  details.hasPlugin = keyboardHasPlugin();

  details.contentHeight = viewportHeight - keyboardHeight;

  console.log('keyboardShow', keyboardHeight, details.contentHeight);

  // figure out if the element is under the keyboard
  details.isElementUnderKeyboard = (details.elementBottom > details.contentHeight);

  ionic.keyboard.isOpen = true;

  // send event so the scroll view adjusts
  keyboardActiveElement = element;
  ionic.trigger('scrollChildIntoView', details, true);

  ionic.requestAnimationFrame(function(){
    document.body.classList.add(KEYBOARD_OPEN_CSS);
  });

  // any showing part of the document that isn't within the scroll the user
  // could touchmove and cause some ugly changes to the app, so disable
  // any touchmove events while the keyboard is open using e.preventDefault()
  document.addEventListener('touchmove', keyboardPreventDefault, false);

  return details;
}

function keyboardFocusOut(e) {
  clearTimeout(keyboardFocusOutTimer);

  keyboardFocusOutTimer = setTimeout(keyboardHide, 350);
}

function keyboardHide() {
  console.log('keyboardHide');
  ionic.keyboard.isOpen = false;

  ionic.trigger('resetScrollView', {
    target: keyboardActiveElement
  }, true);

  ionic.requestAnimationFrame(function(){
    document.body.classList.remove(KEYBOARD_OPEN_CSS);
  });

  // the keyboard is gone now, remove the touchmove that disables native scroll
  document.removeEventListener('touchmove', keyboardPreventDefault);
  document.removeEventListener('keydown', keyboardOnKeyDown);
}

function keyboardUpdateViewportHeight() {
  if( window.innerHeight > keyboardViewportHeight ) {
    keyboardViewportHeight = window.innerHeight;
  }
}

function keyboardOnKeyDown(e) {
  if( ionic.scroll.isScrolling ) {
    keyboardPreventDefault(e);
  }
}

function keyboardPreventDefault(e) {
  if( e.target.tagName !== 'TEXTAREA' ) {
    e.preventDefault();
  }
}

function keyboardOrientationChange() {
  var updatedViewportHeight = window.innerHeight;

  //too slow, have to wait for updated height
  if (updatedViewportHeight === keyboardViewportHeight){
    var count = 0;
    var pollViewportHeight = setInterval(function(){
      //give up
      if (count > 10){
        clearInterval(pollViewportHeight);
      }

      updatedViewportHeight = window.innerHeight;

      if (updatedViewportHeight !== keyboardViewportHeight){
        if (updatedViewportHeight < keyboardViewportHeight){
          ionic.keyboard.landscape = true;
        }
        else {
          ionic.keyboard.landscape = false;
        }
        keyboardViewportHeight = updatedViewportHeight;
        clearInterval(pollViewportHeight);
      }
      count++;

    }, 50);
  }
  else {
    keyboardViewportHeight = updatedViewportHeight;
  }
}

function keyboardGetHeight() {
  // check if we are already have a keyboard height from the plugin
  if ( ionic.keyboard.height ) {
    return ionic.keyboard.height;
  }

  if ( ionic.Platform.isAndroid() ){
    //should be using the plugin, no way to know how big the keyboard is, so guess
    if ( ionic.Platform.isFullScreen ){
      return 275;
    }
    //otherwise, wait for the screen to resize
    if ( window.innerHeight < keyboardViewportHeight ){
      return keyboardViewportHeight - window.innerHeight;
    }
    else {
      return 0;
    }
  }

  // fallback for when its the webview without the plugin
  // or for just the standard web browser
  if( ionic.Platform.isIOS() ) {
    if ( ionic.keyboard.landscape ){
      return 206;
    }

    if (!ionic.Platform.isWebView()){
      return 216;
    }

    return 260;
  }

  // safe guess
  return 275;
}

function keyboardIsWithinScroll(ele) {
  while(ele) {
    if(ele.classList.contains(SCROLL_CONTAINER_CSS)) {
      return true;
    }
    ele = ele.parentElement;
  }
  return false;
}

function keyboardHasPlugin() {
  return !!(window.cordova && cordova.plugins && cordova.plugins.Keyboard);
}

ionic.Platform.ready(function() {
  keyboardUpdateViewportHeight();

  // Android sometimes reports bad innerHeight on window.load
  // try it again in a lil bit to play it safe
  setTimeout(keyboardUpdateViewportHeight, 999);

  // only initialize the adjustments for the virtual keyboard
  // if a touchstart event happens
  document.addEventListener('touchstart', keyboardInit, false);
});

<|endoftext|>


<commit_before>(function () {
    'use strict';

    window.Whisper = window.Whisper || {};

    Whisper.AppView = Backbone.View.extend({
        initialize: function(options) {
          this.inboxView = null;
          this.installView = null;
        },
        events: {
            'click .openInstaller': 'openInstaller',
            'click .openStandalone': 'openStandalone',
            'openInbox': 'openInbox',
        },
        openView: function(view) {
          this.el.innerHTML = "";
          this.el.append(view.el);
          this.delegateEvents();
        },
        openInstaller: function() {
          this.closeInstaller();
          this.installView = new Whisper.InstallView();
          if (Whisper.Registration.everDone()) {
              this.installView.selectStep(3);
              this.installView.hideDots();
          }
          this.openView(this.installView);
        },
        openStandalone: function() {
          if (window.config.environment !== 'production') {
            this.closeInstaller();
            this.installView = new Whisper.StandaloneRegistrationView();
            this.openView(this.installView);
          }
        },
        closeInstaller: function() {
          if (this.installView) {
            this.installView.remove();
            this.installView = null;
          }
        },
        openInbox: function(options) {
          options = options || {};
          _.defaults(options, {initialLoadComplete: false});

          console.log('open inbox');
          this.closeInstaller();

          if (!this.inboxView) {
            return ConversationController.loadPromise().then(function() {
                this.inboxView = new Whisper.InboxView({
                  model: self,
                  window: window,
                  initialLoadComplete: initialLoadComplete
                });
                this.openView(this.inboxView);
            }.bind(this));
          } else {
            if (!$.contains(this.$el, this.inboxView.$el)) {
                this.openView(this.inboxView);
            }
            window.focus(); // FIXME
            return Promise.resolve();
          }
        },
        onEmpty: function() {
          var view = this.inboxView;
          if (view) {
            view.onEmpty();
          }
        },
        onProgress: function(count) {
          var view = this.inboxView;
          if (view) {
            view.onProgress(count);
          }
        },
        openConversation: function(conversation) {
          if (conversation) {
            this.openInbox().then(function() {
              this.inboxView.openConversation(null, conversation);
            }.bind(this));
          }
        },
    });
})();
<commit_msg>Fix scroll reset after clicking on a notification<commit_after>(function () {
    'use strict';

    window.Whisper = window.Whisper || {};

    Whisper.AppView = Backbone.View.extend({
        initialize: function(options) {
          this.inboxView = null;
          this.installView = null;
        },
        events: {
            'click .openInstaller': 'openInstaller',
            'click .openStandalone': 'openStandalone',
            'openInbox': 'openInbox',
        },
        openView: function(view) {
          this.el.innerHTML = "";
          this.el.append(view.el);
          this.delegateEvents();
        },
        openInstaller: function() {
          this.closeInstaller();
          this.installView = new Whisper.InstallView();
          if (Whisper.Registration.everDone()) {
              this.installView.selectStep(3);
              this.installView.hideDots();
          }
          this.openView(this.installView);
        },
        openStandalone: function() {
          if (window.config.environment !== 'production') {
            this.closeInstaller();
            this.installView = new Whisper.StandaloneRegistrationView();
            this.openView(this.installView);
          }
        },
        closeInstaller: function() {
          if (this.installView) {
            this.installView.remove();
            this.installView = null;
          }
        },
        openInbox: function(options) {
          options = options || {};
          _.defaults(options, {initialLoadComplete: false});

          console.log('open inbox');
          this.closeInstaller();

          if (!this.inboxView) {
            return ConversationController.loadPromise().then(function() {
                this.inboxView = new Whisper.InboxView({
                  model: self,
                  window: window,
                  initialLoadComplete: initialLoadComplete
                });
                this.openView(this.inboxView);
            }.bind(this));
          } else {
            if (!$.contains(this.el, this.inboxView.el)) {
                this.openView(this.inboxView);
            }
            window.focus(); // FIXME
            return Promise.resolve();
          }
        },
        onEmpty: function() {
          var view = this.inboxView;
          if (view) {
            view.onEmpty();
          }
        },
        onProgress: function(count) {
          var view = this.inboxView;
          if (view) {
            view.onProgress(count);
          }
        },
        openConversation: function(conversation) {
          if (conversation) {
            this.openInbox().then(function() {
              this.inboxView.openConversation(null, conversation);
            }.bind(this));
          }
        },
    });
})();
<|endoftext|>


<commit_before>/* @flow */

"use strict";

// Perseus module uses React directly and uses $._ directly for
// localization, so we do this as a hack to get it to work
function perseusPrep(katex, KAS, MathJax) {
    $._ = function(x) { return x; };
    window.$_ = function(x) { return x; };
    window.katex = katex;
    window.KAS = KAS;
}

var React = require("react/addons"),
    Util = require("../util"),
    APIClient = require("../apiclient"),
    l10n = require("../l10n"),
    _ = require("underscore"),
    $ = require("jquery");

window.Exercises = {
    cluesEnabled: false
};

var TaskCompleteView = React.createClass({
    render: function() {
        return
TODO: Task complete UI
;
    }
});
/**
 * Represents a single exercise, it will load the exercise dynamically and
 * display it to the user.
 */
var ExerciseViewer = React.createClass({
    propTypes: {
        exercise: React.PropTypes.object.isRequired
    },
    mixins: [Util.BackboneMixin],
    getBackboneModels: function(): Array<any> {
        return [this.props.exercise];
    },
    getInitialState: function() {
        return {
            hintsUsed: 0,
            currentHint: -1
        };
    },
    refreshUserExerciseInfo: function() {
        return APIClient.getTaskInfoByExerciseName(this.props.exercise.getName()).then((info) => {
            this.setState({
                taskId: info.id,
                taskAttemptHistory: info.task_attempt_history
            });
            return APIClient.getUserExercise(this.props.exercise.getName());
        }).then((info) => {
            // TODO: Call this instead when the exercise loads and after submitting
            // Stash out info for the UI
            this.setState({
                level: info.exercise_progress.level,
                mastered: info.exercise_progress.mastered,
                practiced: info.exercise_progress.practiced,
                problemNumber: info.total_done + 1,
                streak: info.streak,
                longestStreak: info.longest_streak
            });
        });

    },
    refreshRandomAssessment: function() {

        // Pick a random problem type:
        var problemTypes = this.exercise.problem_types;
        var randomProblemTypeGroupIndex = Math.floor(Math.random() * problemTypes.length);
        var randomProblemTypeGroup = this.exercise.problem_types[randomProblemTypeGroupIndex];
        this.problemTypeName = randomProblemTypeGroup.name;

        var randomProblemTypeIndex = Math.floor(Math.random() * randomProblemTypeGroup.items.length);

        this.randomAssessmentSHA1 = randomProblemTypeGroup.items[randomProblemTypeIndex].sha1;
        this.randomAssessmentId = randomProblemTypeGroup.items[randomProblemTypeIndex].id;

        this.setState({
            hintsUsed: 0,
            currentHint: -1
        });

        this.refreshUserExerciseInfo().then(() => {
            APIClient.getAssessmentItem(this.randomAssessmentId).done((result) => {
                var assessment = JSON.parse(result.item_data);
                Util.log("Got assessment item: %o: item data: %o", result, assessment);
                this.setState({
                    perseusItemData: assessment
                });
            });
        });
    },
    onClickRequestHint: function() {
        this.refs.itemRenderer.showHint();
        this.setState({
            hintsUsed: this.state.hintsUsed + 1,
            currentHint: this.state.currentHint + 1
        });
    },
    onClickSubmitAnswer: function() {
        var score = this.refs.itemRenderer.scoreInput();
        Util.log("score: %o", score);
        var attemptNumber = 1; // TODO
        var isCorrect = score.correct;
        var secondsTaken = 10; //TODO
        APIClient.reportExerciseProgress(this.props.exercise.getName(), this.state.problemNumber,
            this.randomAssessmentSHA1, this.randomAssessmentId,
            secondsTaken, this.state.hintsUsed, isCorrect,
            attemptNumber, this.problemTypeName, this.state.taskId).done(() => {
                if (isCorrect) {
                    // If we have another correct and we already have 4 correct,
                    // then show task complete view.
                    if (this.state.streak >= 4) {
                        this.setState({
                            taskComplete: true
                        });
                    }
                    this.refreshRandomAssessment();
                } else {
                    // Refresh attempt info so it shows up as wrong
                    this.refreshUserExerciseInfo();
                }
            });
    },
    componentWillMount: function() {
        if (this.props.exercise.isPerseusExercise()) {
            APIClient.getExerciseByName(this.props.exercise.getName()).done((result) => {
                this.exercise = result;
                Util.log("got exercise: %o", result);
                this.refreshRandomAssessment();
            });
        }

        // TODO: Make this load async
        window._ = _;
        window.React = React;
        window.$ = $;
        $._ = (x) => x;
        window.jQuery = $;

        var Khan = require("../../khan-exercises/main");
        var MathJax = require("../../bower_components/MathJax/MathJax.js");
        Khan = window.Khan;
        MathJax = window.MathJax;
        window.KhanUtil = Khan.Util;

        console.log("Khan: %o", Khan);

        var katex = require("../../bower_components/katex/katex"),
            KAS = require("../../bower_components/KAS/kas"),
            Perseus = require("../../bower_components/perseus/perseus-2");
        console.log("katex %o:", katex);
        console.log("KAS %o:", KAS);
        console.log("Perseus %o:", Perseus);

        perseusPrep(katex, KAS, MathJax, Khan.Util);
        Perseus.init({}).then(() => {
            Util.log("Perseus init done %o, %o", Perseus);
            this.ItemRenderer = Perseus.ItemRenderer;
            this.forceUpdate();
        });
    },
    componentDidMount: function() {
    },
    componentWillUnmount: function() {
    },
    render: function(): any {
        var content;
        if (this.state.error) {
            content =
Could not load exercise
;
        } else if (this.props.exercise.isKhanExercisesExercise()) {
            var path = `/khan-exercises/exercises/${this.props.exercise.getFilename()}`;
            content = ;
        } else if (this.state.taskComplete) {
            return <TaskCompleteView/>;
        } else if (this.ItemRenderer && this.state.perseusItemData) {
            var showHintsButton = this.state.perseusItemData.hints.length > this.state.hintsUsed;
            var hint;
            if (this.state.currentHint !== -1 &&
                    this.state.currentHint < this.state.perseusItemData.hints.length) {
            }

            // Always show 5 attempt icons with either pending, correct, hint or wrong
            var attemptIcons = [];
            var taskAttemptHistory = this.state.taskAttemptHistory.slice(-5);
            for (var i = 0; i < 5; i++) {
                if (i >= taskAttemptHistory.length) {
                    attemptIcons.push();
                } else if (taskAttemptHistory[i].seen_hint) {
                    attemptIcons.push();
                } else if (!taskAttemptHistory[i].correct) {
                    attemptIcons.push();
                } else {
                    attemptIcons.push();
                }
            }

            var streakText;
            if (this.state.streak > 5) {
                streakText = l10n.get("correct-streak", {
                    count: this.state.streak
                });
            }
            var longestStreakText;
            if (this.state.longestStreak > 5) {
                longestStreakText = l10n.get("longest-correct-streak", {
                    count: this.state.longestStreak
                });
            }

            content =

                              {attemptIcons}

                                  {streakText}

                                  {longestStreakText}

                          <this.ItemRenderer ref="itemRenderer"
                                             item={this.state.perseusItemData}
                                             problemNum={Math.floor(Math.random() * 50) + 1}
                                             initialHintsVisible={0}
                                             enabledFeatures={{
                                                 highlight: true,
                                                 toolTipFormats: true
                                             }} />

                          <button className="submit-answer-button"
                                  data-l10n-id="submit-answer"
                                  onClick={this.onClickSubmitAnswer}>Submit Answer</button>
                          { !showHintsButton ? null :
                          <button className="submit-answer-button"
                                  data-l10n-id="hint"
                                  onClick={this.onClickRequestHint}>Hint</button>
                          }
                          {hint}

;
        }

        Util.log("render exercise: :%o", this.props.exercise);
        return

            {content}

;
    }
});

module.exports = {
    ExerciseViewer,
};
<commit_msg>Reduce the number of re-renders<commit_after>/* @flow */

"use strict";

// Perseus module uses React directly and uses $._ directly for
// localization, so we do this as a hack to get it to work
function perseusPrep(katex, KAS, MathJax) {
    $._ = function(x) { return x; };
    window.$_ = function(x) { return x; };
    window.katex = katex;
    window.KAS = KAS;
}

var React = require("react/addons"),
    Util = require("../util"),
    APIClient = require("../apiclient"),
    l10n = require("../l10n"),
    _ = require("underscore"),
    $ = require("jquery");

window.Exercises = {
    cluesEnabled: false
};

var TaskCompleteView = React.createClass({
    render: function() {
        return
TODO: Task complete UI
;
    }
});
/**
 * Represents a single exercise, it will load the exercise dynamically and
 * display it to the user.
 */
var ExerciseViewer = React.createClass({
    propTypes: {
        exercise: React.PropTypes.object.isRequired
    },
    mixins: [Util.BackboneMixin],
    getBackboneModels: function(): Array<any> {
        return [this.props.exercise];
    },
    getInitialState: function() {
        return {
            hintsUsed: 0,
            currentHint: -1
        };
    },
    refreshUserExerciseInfo: function() {

        return $.when(APIClient.getTaskInfoByExerciseName(this.props.exercise.getName()),
               APIClient.getUserExercise(this.props.exercise.getName())).then((taskInfo, exerciseInfo) => {

            console.log("getTaskInfoByExerciseName: %o", taskInfo);
            console.log("getUserExercise: %o", exerciseInfo);

            // TODO: Call this instead when the exercise loads and after submitting
            // Stash out info for the UI
            this.setState({
                level: exerciseInfo.exercise_progress.level,
                mastered: exerciseInfo.exercise_progress.mastered,
                practiced: exerciseInfo.exercise_progress.practiced,
                problemNumber: exerciseInfo.total_done + 1,
                streak: exerciseInfo.streak,
                taskId: taskInfo.id,
                taskAttemptHistory: taskInfo.task_attempt_history,
                longestStreak: exerciseInfo.longest_streak
            });
        });

    },
    refreshRandomAssessment: function() {

        // Pick a random problem type:
        var problemTypes = this.exercise.problem_types;
        var randomProblemTypeGroupIndex = Math.floor(Math.random() * problemTypes.length);
        var randomProblemTypeGroup = this.exercise.problem_types[randomProblemTypeGroupIndex];
        this.problemTypeName = randomProblemTypeGroup.name;

        var randomProblemTypeIndex = Math.floor(Math.random() * randomProblemTypeGroup.items.length);

        this.randomAssessmentSHA1 = randomProblemTypeGroup.items[randomProblemTypeIndex].sha1;
        this.randomAssessmentId = randomProblemTypeGroup.items[randomProblemTypeIndex].id;

        this.refreshUserExerciseInfo().then(() => {
            APIClient.getAssessmentItem(this.randomAssessmentId).done((result) => {
                var assessment = JSON.parse(result.item_data);
                Util.log("Got assessment item: %o: item data: %o", result, assessment);
                this.setState({
                    hintsUsed: 0,
                    currentHint: -1,
                    perseusItemData: assessment,
                });
            });
        });
    },
    onClickRequestHint: function() {
        this.refs.itemRenderer.showHint();
        this.setState({
            hintsUsed: this.state.hintsUsed + 1,
            currentHint: this.state.currentHint + 1
        });
    },
    onClickSubmitAnswer: function() {
        var score = this.refs.itemRenderer.scoreInput();
        Util.log("score: %o", score);
        var attemptNumber = 1; // TODO
        var isCorrect = score.correct;
        var secondsTaken = 10; //TODO
        APIClient.reportExerciseProgress(this.props.exercise.getName(), this.state.problemNumber,
            this.randomAssessmentSHA1, this.randomAssessmentId,
            secondsTaken, this.state.hintsUsed, isCorrect,
            attemptNumber, this.problemTypeName, this.state.taskId).done(() => {
                if (isCorrect) {
                    // If we have another correct and we already have 4 correct,
                    // then show task complete view.
                    if (this.state.streak >= 4) {
                        this.setState({
                            taskComplete: true
                        });
                        return;
                    }
                    this.refreshRandomAssessment();
                } else {
                    // Refresh attempt info so it shows up as wrong
                    this.refreshUserExerciseInfo();
                }
            });
    },
    componentWillMount: function() {
        if (this.props.exercise.isPerseusExercise()) {
            APIClient.getExerciseByName(this.props.exercise.getName()).done((result) => {
                this.exercise = result;
                Util.log("got exercise: %o", result);
                this.refreshRandomAssessment();
            });
        }

        // TODO: Make this load async
        window._ = _;
        window.React = React;
        window.$ = $;
        $._ = (x) => x;
        window.jQuery = $;

        var Khan = require("../../khan-exercises/main");
        var MathJax = require("../../bower_components/MathJax/MathJax.js");
        Khan = window.Khan;
        MathJax = window.MathJax;
        window.KhanUtil = Khan.Util;

        console.log("Khan: %o", Khan);

        var katex = require("../../bower_components/katex/katex"),
            KAS = require("../../bower_components/KAS/kas"),
            Perseus = require("../../bower_components/perseus/perseus-2");
        console.log("katex %o:", katex);
        console.log("KAS %o:", KAS);
        console.log("Perseus %o:", Perseus);

        perseusPrep(katex, KAS, MathJax, Khan.Util);
        Perseus.init({}).then(() => {
            Util.log("Perseus init done %o, %o", Perseus);
            this.ItemRenderer = Perseus.ItemRenderer;
            this.forceUpdate();
        });
    },
    componentDidMount: function() {
    },
    componentWillUnmount: function() {
    },
    render: function(): any {
        var content;
        if (this.state.error) {
            content =
Could not load exercise
;
        } else if (this.props.exercise.isKhanExercisesExercise()) {
            var path = `/khan-exercises/exercises/${this.props.exercise.getFilename()}`;
            content = ;
        } else if (this.state.taskComplete) {
            return <TaskCompleteView/>;
        } else if (this.ItemRenderer && this.state.perseusItemData) {
            var showHintsButton = this.state.perseusItemData.hints.length > this.state.hintsUsed;
            var hint;
            if (this.state.currentHint !== -1 &&
                    this.state.currentHint < this.state.perseusItemData.hints.length) {
            }

            // Always show 5 attempt icons with either pending, correct, hint or wrong
            var attemptIcons = [];
            var taskAttemptHistory = this.state.taskAttemptHistory.slice(-5);
            for (var i = 0; i < 5; i++) {
                if (i >= taskAttemptHistory.length) {
                    attemptIcons.push();
                } else if (taskAttemptHistory[i].seen_hint) {
                    attemptIcons.push();
                } else if (!taskAttemptHistory[i].correct) {
                    attemptIcons.push();
                } else {
                    attemptIcons.push();
                }
            }

            var streakText;
            if (this.state.streak > 5) {
                streakText = l10n.get("correct-streak", {
                    count: this.state.streak
                });
            }
            var longestStreakText;
            if (this.state.longestStreak > 5) {
                longestStreakText = l10n.get("longest-correct-streak", {
                    count: this.state.longestStreak
                });
            }

            content =

                              {attemptIcons}

                                  {streakText}

                                  {longestStreakText}

                          <this.ItemRenderer ref="itemRenderer"
                                             item={this.state.perseusItemData}
                                             problemNum={Math.floor(Math.random() * 50) + 1}
                                             initialHintsVisible={0}
                                             enabledFeatures={{
                                                 highlight: true,
                                                 toolTipFormats: true
                                             }} />

                          <button className="submit-answer-button"
                                  data-l10n-id="submit-answer"
                                  onClick={this.onClickSubmitAnswer}>Submit Answer</button>
                          { !showHintsButton ? null :
                          <button className="submit-answer-button"
                                  data-l10n-id="hint"
                                  onClick={this.onClickRequestHint}>Hint</button>
                          }
                          {hint}

;
        }

        Util.log("render exercise: :%o", this.props.exercise);
        return

            {content}

;
    }
});

module.exports = {
    ExerciseViewer,
};
<|endoftext|>