<commit_before>/**
 * Really simple example of a chat class that allows client side Javascript to interact
 * with an ipcortex keevio chat system
 */

var SimpleChat = (
/**
 * Creates a new SimpleChat singleton and initialises it with the user info
 * that it will use to authenticate against the PBX. Provide two callbacks:
 * when created:
 *   status: for API initialisation success/failure
 *   room: for all room create/destroy/message events
 *
 * @constructor
 *
 * @param {String} username A valid PBX username for a user who owns a phone
 * @param {String} password Password
 * @param {SimpleChat~statusCallback} status to call on error or successful API initialisation
 * @param {SimpleChat~roomCallback} room to call when a chat room event occurs
 * 		See roomUpdate() for details.
 */
	function(username, password, status, room) {
		var tmp = {};
		var rooms = {};
		var online = {};

		var lookup = {
			contact:	{},
			room:		{}
		};

		var active = {
			xmppid:		null,
			callback:	null
		};

		var CB = {
			room:		room,
			status:		status
		};

/**
 * Internal method to handle authentication-OK and the resultant
 * starting of API
 *
 * @private
 */
		function authCB(ok) {
			if ( ok )
				IPCortex.PBX.startPoll(go, error);
			else
				CB.status(false, -1, "Login failed");
		}

/**
 * Internal method to handle API startup FAILURE  - Used by authCB
 *
 * @private
 */
		function error(n, m) {
			CB.status(false, n, m);
			console.error('We got an error number: ' + n + ' Text: ' + m);
		}

/**
 * Internal method to handle API startup SUCCESS  - Used by authCB
 *
 * @private
 */
		function go() {
			console.log('SimpleChat.go()');
			IPCortex.PBX.enableChat(roomCB);
			CB.status(true, 0, "API Initialised");
		}

/**
 * Internal method to handle simplification of callback data provided by room API.
 * The 'room' callback is called with a simple object containing the following
 * attributes whenever a room changes state, or a new message arrives:
 *
 *	id:		A unique room ID number.
 *	name:		The name of the room, this can change over time.
 *	state:		The state of the room, typically 'invited', 'inviting', 'open', 'closed' or 'dead'
 *			NOTE: After a call with 'dead' the room ceases to exist and
 *				should be cleaned up and not used again.
 *	members:	An array containing a list of joined members in the room.
 *	invited:	An array containing a list of invited members who have not since closed the room.
 *	msg:		null = no new messages, or {cid: sender_id, time: timestamp_of_message, msg: message_text}
 *
 * @private
 */
		function roomUpdate(filter, hid, room) {
			var data = {
				id:		room.get('roomID'),
				name:		room.get('name'),
				state:		room.get('state'),
				members:	room.get('joined').sort(),
				invited:	room.get('linked').sort(),
				msg:		null
			};
			var tmp = [];
			var msgs = room.get('msgs') || [];
			for ( var i = 0; i < msgs.length; i++ ) {
				if ( msgs[i].cN != 'SYSTEM' )
					tmp.push({
						cid:	msgs[i].cID,
						time:	msgs[i].time,
						msg:	msgs[i].msg
					});
			}
			if ( tmp.length ) {
				for ( var i = 0; i < tmp.length; i++ ) {
					data.msg = tmp[i];
					CB.room(data);
				}
			} else
				CB.room(data);
		}

/**
 * Internal method to handle new-room callbacks from the API, invite missing users if needed
 * and hook the room for further updates from the API.
 *
 * @private
 */
		function roomCB(room) {
			lookup.room[room.get('roomID')] = room;
			roomUpdate(null, null, room);
			room.hook(roomUpdate);
		}

/**
 * Internal method to handle updates to contact online/offline states.
 * CB.address callback is supplied when 'getOnline' is called.
 *
 * @private
 */
		function addressUpdate(filter, hid, address) {
			if ( address.get('online') && ! online[address.get('cid')] ) {
				online[address.get('cid')] = true;
				CB.address({
					cid:	address.get('cid'),
					name:	address.get('name'),
					online:	true
				});
			} else if ( ! address.get('online') && online[address.get('cid')] ) {
				delete online[address.get('cid')];
				CB.address({
					cid:	address.get('cid'),
					name:	address.get('name'),
					online:	false
				});
			}
		}

/**
 * Internal method which understands how to handle the API address callback.
 *
 * @private
 */
		function addressCB(address, deleted) {
			for ( var i = 0; i < address.length; i++ ) {
				lookup.contact[address[i].get('cid')] = address[i];
				address[i].hook(addressUpdate);
			}
		}

/**
 * Used by the API initialisation process
 */
		onAPILoadReady = (
			function() {
				IPCortex.PBX.Auth.login(username, password, null, authCB);
			}
		);

		return {
/**
 * Request a list of online contacts. Results are returned one contact at a time
 * via the supplied callback. Call back is called with an object having the
 * properties:
 *
 *	cid:	contact's ID
 *	name:	contact's Name
 *	online:	true/false
 *
 * If a contact changes state, the callback will be called again with the updated
 * online status.
 *
 * @param {SimpleChat~contactCallback} cb Callback to call on address entry change.
 */
			getOnline:
				function(cb) {
					if ( typeof(cb) != 'function' )
						return false;
					for ( var cid in online ) {
						if ( online.hasOwnProperty(cid) ) {
							cb(online);
							return;
						}
					}
					IPCortex.PBX.getAddressbook(addressCB);
					CB.address = cb;
				},
/**
 * Open a new room.
 *
 * @param {Number} cids[] An array of contact IDs to include in the room.
 */
			openRoom:
				function(cids) {
					cids = [].concat(cids);
					function cb(a, b) {
/* TODO: Handle error response */
					}
/* TODO: support multiple outstanding rooms! */
					IPCortex.PBX.chatInvite(cids, cb)
				},
/**
 * Rename an existing room.
 *
 * @param {Number} roomID The ID of the room to rename
 * @param {String} name The new name of the room
 */
			renameRoom:
				function(roomID, name) {
					if ( ! lookup.room[roomID] )
						return false;
					lookup.room[roomID].modify({name: name, gomulti: true});
				},
/**
 * Get owner ID of room (null if none or unknown)
 *
 * @param {Number} roomID The ID of the room to rename
 */
			getOwnerId:
				function(roomID) {
					if ( ! lookup.room[roomID] )
						return false;
					lookup.room[roomID].get('owner');
				},
/**
 * Leave a room. Other members will remain in the room.
 *
 * @param {Number} roomID The ID of the room to rename
 */
			leaveRoom:
				function(roomID) {
					if ( ! lookup.room[roomID] )
						return false;
					lookup.room[roomID].leave();
				},
/**
 * Post a message into a room.
 *
 * @param {Number} roomID The ID of the room to rename
 * @param {String} msg The message to post
 */
			postRoom:
				function(roomID, msg) {
					if ( ! lookup.room[roomID] )
						return false;
					lookup.room[roomID].post(msg);
				}
		};
	}
);
<commit_msg>Fix bug in getOwnerId (found by Dessi)<commit_after>/**
 * Really simple example of a chat class that allows client side Javascript to interact
 * with an ipcortex keevio chat system
 */

var SimpleChat = (
/**
 * Creates a new SimpleChat singleton and initialises it with the user info
 * that it will use to authenticate against the PBX. Provide two callbacks:
 * when created:
 *   status: for API initialisation success/failure
 *   room: for all room create/destroy/message events
 *
 * @constructor
 *
 * @param {String} username A valid PBX username for a user who owns a phone
 * @param {String} password Password
 * @param {SimpleChat~statusCallback} status to call on error or successful API initialisation
 * @param {SimpleChat~roomCallback} room to call when a chat room event occurs
 * 		See roomUpdate() for details.
 */
	function(username, password, status, room) {
		var tmp = {};
		var rooms = {};
		var online = {};

		var lookup = {
			contact:	{},
			room:		{}
		};

		var active = {
			xmppid:		null,
			callback:	null
		};

		var CB = {
			room:		room,
			status:		status
		};

/**
 * Internal method to handle authentication-OK and the resultant
 * starting of API
 *
 * @private
 */
		function authCB(ok) {
			if ( ok )
				IPCortex.PBX.startPoll(go, error);
			else
				CB.status(false, -1, "Login failed");
		}

/**
 * Internal method to handle API startup FAILURE  - Used by authCB
 *
 * @private
 */
		function error(n, m) {
			CB.status(false, n, m);
			console.error('We got an error number: ' + n + ' Text: ' + m);
		}

/**
 * Internal method to handle API startup SUCCESS  - Used by authCB
 *
 * @private
 */
		function go() {
			console.log('SimpleChat.go()');
			IPCortex.PBX.enableChat(roomCB);
			CB.status(true, 0, "API Initialised");
		}

/**
 * Internal method to handle simplification of callback data provided by room API.
 * The 'room' callback is called with a simple object containing the following
 * attributes whenever a room changes state, or a new message arrives:
 *
 *	id:		A unique room ID number.
 *	name:		The name of the room, this can change over time.
 *	state:		The state of the room, typically 'invited', 'inviting', 'open', 'closed' or 'dead'
 *			NOTE: After a call with 'dead' the room ceases to exist and
 *				should be cleaned up and not used again.
 *	members:	An array containing a list of joined members in the room.
 *	invited:	An array containing a list of invited members who have not since closed the room.
 *	msg:		null = no new messages, or {cid: sender_id, time: timestamp_of_message, msg: message_text}
 *
 * @private
 */
		function roomUpdate(filter, hid, room) {
			var data = {
				id:		room.get('roomID'),
				name:		room.get('name'),
				state:		room.get('state'),
				members:	room.get('joined').sort(),
				invited:	room.get('linked').sort(),
				msg:		null
			};
			var tmp = [];
			var msgs = room.get('msgs') || [];
			for ( var i = 0; i < msgs.length; i++ ) {
				if ( msgs[i].cN != 'SYSTEM' )
					tmp.push({
						cid:	msgs[i].cID,
						time:	msgs[i].time,
						msg:	msgs[i].msg
					});
			}
			if ( tmp.length ) {
				for ( var i = 0; i < tmp.length; i++ ) {
					data.msg = tmp[i];
					CB.room(data);
				}
			} else
				CB.room(data);
		}

/**
 * Internal method to handle new-room callbacks from the API, invite missing users if needed
 * and hook the room for further updates from the API.
 *
 * @private
 */
		function roomCB(room) {
			lookup.room[room.get('roomID')] = room;
			roomUpdate(null, null, room);
			room.hook(roomUpdate);
		}

/**
 * Internal method to handle updates to contact online/offline states.
 * CB.address callback is supplied when 'getOnline' is called.
 *
 * @private
 */
		function addressUpdate(filter, hid, address) {
			if ( address.get('online') && ! online[address.get('cid')] ) {
				online[address.get('cid')] = true;
				CB.address({
					cid:	address.get('cid'),
					name:	address.get('name'),
					online:	true
				});
			} else if ( ! address.get('online') && online[address.get('cid')] ) {
				delete online[address.get('cid')];
				CB.address({
					cid:	address.get('cid'),
					name:	address.get('name'),
					online:	false
				});
			}
		}

/**
 * Internal method which understands how to handle the API address callback.
 *
 * @private
 */
		function addressCB(address, deleted) {
			for ( var i = 0; i < address.length; i++ ) {
				lookup.contact[address[i].get('cid')] = address[i];
				address[i].hook(addressUpdate);
			}
		}

/**
 * Used by the API initialisation process
 */
		onAPILoadReady = (
			function() {
				IPCortex.PBX.Auth.login(username, password, null, authCB);
			}
		);

		return {
/**
 * Request a list of online contacts. Results are returned one contact at a time
 * via the supplied callback. Call back is called with an object having the
 * properties:
 *
 *	cid:	contact's ID
 *	name:	contact's Name
 *	online:	true/false
 *
 * If a contact changes state, the callback will be called again with the updated
 * online status.
 *
 * @param {SimpleChat~contactCallback} cb Callback to call on address entry change.
 */
			getOnline:
				function(cb) {
					if ( typeof(cb) != 'function' )
						return false;
					for ( var cid in online ) {
						if ( online.hasOwnProperty(cid) ) {
							cb(online);
							return;
						}
					}
					IPCortex.PBX.getAddressbook(addressCB);
					CB.address = cb;
				},
/**
 * Open a new room.
 *
 * @param {Number} cids[] An array of contact IDs to include in the room.
 */
			openRoom:
				function(cids) {
					cids = [].concat(cids);
					function cb(a, b) {
/* TODO: Handle error response */
					}
/* TODO: support multiple outstanding rooms! */
					IPCortex.PBX.chatInvite(cids, cb)
				},
/**
 * Rename an existing room.
 *
 * @param {Number} roomID The ID of the room to rename
 * @param {String} name The new name of the room
 */
			renameRoom:
				function(roomID, name) {
					if ( ! lookup.room[roomID] )
						return false;
					lookup.room[roomID].modify({name: name, gomulti: true});
				},
/**
 * Get owner ID of room (null if none or unknown)
 *
 * @param {Number} roomID The ID of the room to rename
 */
			getOwnerId:
				function(roomID) {
					if ( ! lookup.room[roomID] )
						return false;
					return lookup.room[roomID].get('owner');
				},
/**
 * Leave a room. Other members will remain in the room.
 *
 * @param {Number} roomID The ID of the room to rename
 */
			leaveRoom:
				function(roomID) {
					if ( ! lookup.room[roomID] )
						return false;
					lookup.room[roomID].leave();
				},
/**
 * Post a message into a room.
 *
 * @param {Number} roomID The ID of the room to rename
 * @param {String} msg The message to post
 */
			postRoom:
				function(roomID, msg) {
					if ( ! lookup.room[roomID] )
						return false;
					lookup.room[roomID].post(msg);
				}
		};
	}
);
<|endoftext|>


<commit_before>/*
   Copyright 2018 cmanlh

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
/**
 * It's not standard base64 algorithm converter, just for converting text to String which can be included in JSON String
 */
(function ($) {
    const CHAR_SET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    const CHAR_MAP = {
        "A": 0,
        "B": 1,
        "C": 2,
        "D": 3,
        "E": 4,
        "F": 5,
        "G": 6,
        "H": 7,
        "I": 8,
        "J": 9,
        "K": 10,
        "L": 11,
        "M": 12,
        "N": 13,
        "O": 14,
        "P": 15,
        "Q": 16,
        "R": 17,
        "S": 18,
        "T": 19,
        "U": 20,
        "V": 21,
        "W": 22,
        "X": 23,
        "Y": 24,
        "Z": 25,
        "a": 26,
        "b": 27,
        "c": 28,
        "d": 29,
        "e": 30,
        "f": 31,
        "g": 32,
        "h": 33,
        "i": 34,
        "j": 35,
        "k": 36,
        "l": 37,
        "m": 38,
        "n": 39,
        "o": 40,
        "p": 41,
        "q": 42,
        "r": 43,
        "s": 44,
        "t": 45,
        "u": 46,
        "v": 47,
        "w": 48,
        "x": 49,
        "y": 50,
        "z": 51,
        "0": 52,
        "1": 53,
        "2": 54,
        "3": 55,
        "4": 56,
        "5": 57,
        "6": 58,
        "7": 59,
        "8": 60,
        "9": 61,
        "+": 62,
        "/": 63
    };

    $.charUtil = {
        encodeArrayBuffer: function (source) {
            var target = '';

            new Uint32Array(source).forEach(val => {
                target = target.concat('=')
                    .concat(this.charToU64(val));
            });

            return target;
        },
        decodeArrayBuffer: function (source) {
            var target = '';

            var charArray = source.split('=');
            var size = charArray.length;
            var buffer = new Uint32Array(size - 1);
            for (var i = 1; i < size; i++) {
                buffer[i - 1] = this.u64ToChar(charArray[i]).charCodeAt(0);
            }

            return buffer.buffer;
        },
        encodeString: function (source) {
            var target = '';

            for (var i in source) {
                target = target.concat('=')
                    .concat(this.charToU64(source.charCodeAt(i)));
            }

            return target;
        },
        decodeString: function (source) {
            var target = '';

            var charArray = source.split('=');
            var size = charArray.length;

            for (var i = 1; i < size; i++) {
                target = target.concat(this.u64ToChar(charArray[i]));
            }

            return target;
        },
        charToU64: function (val) {
            var buf = '',
                mask = 63;
            do {
                buf = buf.concat(CHAR_SET[val & mask]);
                val >>>= 6;
            } while (val != 0);

            return buf;
        },
        u64ToChar: function (u64) {
            var len = 0,
                val = 0;
            for (var i in u64) {
                val += CHAR_MAP[u64[i]] << (len++ * 6);
            }

            return String.fromCharCode(val);
        }
    };
}(jQuery));<commit_msg>browser using utf-16, not work with uint32,add special convertor for uint32<commit_after>/*
   Copyright 2018 cmanlh

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
/**
 * It's not standard base64 algorithm converter, just for converting text to String which can be included in JSON String
 */
(function ($) {
    const CHAR_SET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    const CHAR_MAP = {
        "A": 0,
        "B": 1,
        "C": 2,
        "D": 3,
        "E": 4,
        "F": 5,
        "G": 6,
        "H": 7,
        "I": 8,
        "J": 9,
        "K": 10,
        "L": 11,
        "M": 12,
        "N": 13,
        "O": 14,
        "P": 15,
        "Q": 16,
        "R": 17,
        "S": 18,
        "T": 19,
        "U": 20,
        "V": 21,
        "W": 22,
        "X": 23,
        "Y": 24,
        "Z": 25,
        "a": 26,
        "b": 27,
        "c": 28,
        "d": 29,
        "e": 30,
        "f": 31,
        "g": 32,
        "h": 33,
        "i": 34,
        "j": 35,
        "k": 36,
        "l": 37,
        "m": 38,
        "n": 39,
        "o": 40,
        "p": 41,
        "q": 42,
        "r": 43,
        "s": 44,
        "t": 45,
        "u": 46,
        "v": 47,
        "w": 48,
        "x": 49,
        "y": 50,
        "z": 51,
        "0": 52,
        "1": 53,
        "2": 54,
        "3": 55,
        "4": 56,
        "5": 57,
        "6": 58,
        "7": 59,
        "8": 60,
        "9": 61,
        "+": 62,
        "/": 63
    };

    $.charUtil = {
        encodeArrayBuffer: function (source) {
            var target = '';

            new Uint32Array(source).forEach(val => {
                target = target.concat('=')
                    .concat(this.uintToU64(val));
            });

            return target;
        },
        decodeArrayBuffer: function (source) {
            var target = '';

            var charArray = source.split('=');
            var size = charArray.length;
            var buffer = new Uint32Array(size - 1);
            for (var i = 1; i < size; i++) {
                buffer[i - 1] = this.u64ToUint(charArray[i]);
            }

            return buffer.buffer;
        },
        encodeString: function (source) {
            var target = '';

            for (var i in source) {
                target = target.concat('=')
                    .concat(this.uintToU64(source.charCodeAt(i)));
            }

            return target;
        },
        decodeString: function (source) {
            var target = '';

            var charArray = source.split('=');
            var size = charArray.length;

            for (var i = 1; i < size; i++) {
                target = target.concat(this.u64ToChar(charArray[i]));
            }

            return target;
        },
        uintToU64: function (val) {
            var buf = '',
                mask = 63;
            do {
                buf = buf.concat(CHAR_SET[val & mask]);
                val >>>= 6;
            } while (val != 0);

            return buf;
        },
        u64ToChar: function (u64) {
            var len = 0,
                val = 0;
            for (var i in u64) {
                val |= CHAR_MAP[u64[i]] << (len++ * 6);
            }

            return String.fromCharCode(val);
        },
        u64ToUint: function (u64) {
            var len = 0,
                val = 0;
            for (var i in u64) {
                val |= CHAR_MAP[u64[i]] << (len++ * 6);
            }

            return val;
        }
    };
}(jQuery));<|endoftext|>


<commit_before>/**
 * Native embed library:
 *
 * Enables embedPlayer support for native iOS/Android webView playback system
 */

(function (mw, $) {
	"use strict";
	//make the player transparent to see the native iOS/Android player
	if (mw.getConfig("EmbedPlayer.ForceNativeComponent")) {
		$('body,.videoHolder').css('background-color', 'transparent');
	}

	mw.EmbedPlayerNativeComponent = {
		//Instance Name
		instanceOf: 'NativeComponent',

		// Flag to only load the video ( not play it )
		onlyLoadFlag: false,

		//Callback fired once video is "loaded"
		onLoadedCallback: null,

		// The previous "currentTime" to sniff seek actions
		// NOTE the bug where onSeeked does not seem fire consistently may no longer be applicable
		prevCurrentTime: -1,

		// Store the progress event ( updated during monitor )
		progressEventData: null,

		// If the media loaded event has been fired
		mediaLoadedFlag: null,

		// A flag to keep the video tag offscreen.
		keepPlayerOffScreenFlag: null,

		// A flag to designate the first play event, as to not propagate the native event in this case
		ignoreNextNativeEvent: null,

		// A local var to store the current seek target time:
		currentSeekTargetTime: null,

		// Disable switch source callback
		disableSwitchSourceCallback: false,

		// All the native events per:
		// http://www.w3.org/TR/html5/video.html#mediaevents
		nativeEvents: [
			'stop',
			'play',
			'pause',
			'canplay',
			'seeking',
			'seeked',
			'ended',
			'error',
			'stalled',
			'loadedmetadata',
			'timeupdate',
			'progress',
			'enterfullscreen',
			'exitfullscreen',
			'durationchange',
			'chromecastDeviceConnected',
			'chromecastDeviceDisConnected'
		],

		nativeActions: [
			'share',
			'openHomePage'
		],
		// Native player supported feature set
		supports: {
			'playHead': true,
			'pause': true,
			'fullscreen': true,
			'SourceSelector': false,
			'timeDisplay': true,
			'volumeControl': false,
			'overlays': true
		},

		setup: function (readyCallback) {
			var _this = this;
			mw.log("NativeComponent:: setup");
			// remove any existing pid ( if present )
			$('#' + this.pid).remove();

			var divElement = document.createElement("div");
			divElement.setAttribute('id', 'proxy');
			document.body.appendChild(divElement);

			this.proxyElement = divElement;
			try {
				if (NativeBridge.videoPlayer) {
					NativeBridge.videoPlayer.registePlayer(this.getPlayerElement());
					NativeBridge.videoPlayer.registerEmbedPlayer(this);
				}
			}
			catch (e) {
				alert(e);
			}

			this.applyMediaElementBindings();

			this.bindHelper("SourceChange", function () {
				_this.getPlayerElement().attr('src', this.getSrc());
			});
			this.bindHelper("layoutBuildDone ended", function () {
				_this.getPlayerElement().notifyLayoutReady();
			});
			this.bindHelper("showChromecastDeviceList", function () {
				mw.log("EmbedPlayerNativeComponent:: showChromecastDeviceList::");
				_this.getPlayerElement().showChromecastDeviceList();
			});

			this.resolveSrcURL(this.getSrc()).then(
				function (resolvedSrc) {
					mw.log("EmbedPlayerNativeComponent::resolveSrcURL get succeeded");
					_this.getPlayerElement().attr('src', resolvedSrc);
					readyCallback();
				},
				function () {
					mw.log("EmbedPlayerNativeComponent::resolveSrcURL get failed");
					_this.getPlayerElement().attr('src', _this.getSrc());
					readyCallback();
				}
			);
		},

		embedPlayerHTML: function () {
		},

		playerSwitchSource: function (source, switchCallback, doneCallback) {
			mw.log("NativeComponent:: playerSwitchSource");
			var _this = this;
			var vid = this.getPlayerElement();
			var switchBindPostfix = '.playerSwitchSource';

			// remove old binding:
			$(vid).unbind(switchBindPostfix);

			// add a loading indicator:
			_this.addPlayerSpinner();

			// empty out any existing sources:
			$(vid).empty();

			if (this.getSrc() != source.getSrc()) {
				vid.attr('src', source.getSrc());
			} else {
				vid.attr('src', this.getSrc());
			}

			this.isPauseLoading = false;
			_this.hideSpinner();
			if ($.isFunction(switchCallback)) {
				switchCallback(vid);
			}

			if ($.isFunction(switchCallback)) {
				setTimeout(function () {
					vid.play();
				}, 100);
			}

			// Add the end binding if we have a post event:
			if ($.isFunction(doneCallback)) {
				$(vid).bind('ended' + switchBindPostfix, function (event) {
					if (_this.disableSwitchSourceCallback) {
						return;
					}
					// remove end binding:
					$(vid).unbind(switchBindPostfix);
					// issue the doneCallback
					doneCallback();

					// Support loop for older iOS
					// Temporarily disabled pending more testing or refactor into a better place.
					//if ( _this.loop ) {
					//	vid.play();
					//}
					return false;
				});
			}
		},

		/**
		 * Apply media element bindings
		 */
		applyMediaElementBindings: function () {
			var _this = this;
			mw.log("EmbedPlayerNative::MediaElementBindings");

			$.each(_this.nativeEvents, function (inx, eventName) {
				$(_this.getPlayerElement()).unbind(eventName).bind(eventName, function () {
					// make sure we propagating events, and the current instance is in the correct closure.
					if (_this._propagateEvents && _this.instanceOf == 'NativeComponent') {
						var argArray = $.makeArray(arguments);
						// Check if there is local handler:

						if (_this[ '_on' + eventName ]) {
							_this[ '_on' + eventName ].apply(_this, argArray);
						} else {
							// No local handler directly propagate the event to the abstract object:
							$(_this).trigger(eventName, argArray);
						}
					}
				});
			});
		},

		/**
		 * Get the embed player time
		 */
		getPlayerElementTime: function () {
			var _this = this;
			// Make sure we have .vid obj

			if (!this.getPlayerElement()) {
				mw.log('EmbedPlayerNative::getPlayerElementTime: ' + this.id + ' not in dom ( stop monitor)');
				this.stop();
				return false;
			}
			var ct = this.getPlayerElement().attr('currentTime');
			// Return 0 or a positive number:
			if (!ct || isNaN(ct) || ct < 0 || !isFinite(ct)) {
				return 0;
			}
			// Return the playerElement currentTime
			return  ct;
		},

		/**
		 * Get /update the playerElement value
		 */
		getPlayerElement: function () {
			return this.proxyElement;
		},

		/**
		 * Stop the player ( end all listeners )
		 */
		stop: function(){
			var _this = this;
			if( this.playerElement && this.playerElement.currentTime){
				this.playerElement.pause();
			}
			this.parent_stop();
		},

		/**
		 * Play back the video stream
		 * calls parent_play to update the interface
		 */

		play: function () {
			mw.log("EmbedPlayerNativeComponent:: play::");

			this.removePoster();

			if (this.parent_play()) {
				if (this.getPlayerElement()) { // update player
					this.getPlayerElement().play();
				}
				this.monitor();
			}
		},

		/**
		 * Pause the video playback
		 * calls parent_pause to update the interface
		 */
		pause: function () {
			mw.log("EmbedPlayerNativeComponent:: pause::");
			this.parent_pause(); // update interface
			if (this.getPlayerElement()) { // update player
				this.getPlayerElement().pause();
			}
		},

		seek: function (percentage) {
			mw.log("EmbedPlayerNativeComponent:: seek::");
			var seekTime = percentage * this.getDuration();
			this.getPlayerElement().attr('currentTime', seekTime);
			this.parent_seek(percentage);
		},

		doNativeAction: function (actionParams) {
			mw.log("EmbedPlayerNativeComponent:: doNativeAction::");
			this.getPlayerElement().attr('nativeAction', actionParams);
			this.getPlayerElement().doNativeAction();
		},

		nativeActionType: function (actionName) {
			return $.inArray(actionName, this.nativeActions);
		},

		isNativeApp: function () {
			return mw.getConfig("EmbedPlayer.ForceNativeComponent");
		},

		/**
		 * returns true if device can auto play
		 */
		canAutoPlay: function () {
			return true;
		},

		/**
		 * Handle the native play event
		 */
		_onplay: function () {
			mw.log("EmbedPlayerNativeComponent:: OnPlay::");

			$(this).trigger("playing");

			if (this.paused && this.parent_play()) {
				this.monitor();
			}
		},

		/**
		 * Handle the native paused event
		 */
		/**
		 * Handle the native paused event
		 */
		_onpause: function () {
			mw.log("EmbedPlayerNativeComponent:: OnPause::");
			this.parent_pause();
		},

		/**
		 * Local method for seeking event
		 * fired when "seeking"
		 */
		_onseeking: function () {
			mw.log("EmbedPlayerNative::onSeeking ");
			if (!this.seeking) {
				this.seeking = true;
				// Run the onSeeking interface update
				this.layoutBuilder.onSeek();

				if (this._propagateEvents) {
					// Trigger the html5 "seeking" trigger
					mw.log("EmbedPlayerNative::seeking:trigger:: ");
					this.triggerHelper('seeking');
				}
			}
		},

		/**
		 * Local method for seeked event
		 * fired when done seeking
		 */
		_onseeked: function () {
			mw.log("EmbedPlayerNativeComponent::onSeeked ");
			if (this.seeking) {
				this.seeking = false;

				if (this._propagateEvents) {
					mw.log("EmbedPlayerNativeComponent:: trigger: seeked");
					this.triggerHelper('seeked');
				}

				this.hideSpinner();
				this.updatePlayheadStatus();
				this.monitor();
			}
		},

		/**
		 * Local method for metadata ready
		 * fired when metadata becomes available
		 *
		 * Used to update the media duration to
		 * accurately reflect the src duration
		 */
		_onloadedmetadata: function () {
			//Fire "onLoaded" flags if set
			if (typeof this.onLoadedCallback == 'function') {
				this.onLoadedCallback();
			}

			// Trigger "media loaded"
			if (!this.mediaLoadedFlag) {
				$(this).trigger('mediaLoaded');
				this.mediaLoadedFlag = true;
			}
		},

		/**
		 * Local method for end of media event
		 */
		_onended: function (event) {
			if (this.getPlayerElement()) {
				mw.log('EmbedPlayer:native: onended:');
				if (this._propagateEvents) {
					this.onClipDone();
				}
			}
		},

		/**
		 * Local onClip done function for native player.
		 */
		onClipDone: function () {
			this.parent_onClipDone();
		},

		/**
		 * playback error
		 */
		_onerror: function (event, data) {
			this.triggerHelper('embedPlayerError', [ data ]);
		},

		/**
		 * buffer progress
		 * @param event
		 * @param progress
		 * @private
		 */
		_onprogress: function (event, progress) {
			if (typeof progress !== 'undefined') {
				this.updateBufferStatus(progress);
			}
		},

		/*
		 * Write the Embed html to the target
		 */
		getVideoElementPosition: function () {
			var videoElementDiv = parent.document.getElementById(this.id);
			var videoElementRect = videoElementDiv.getBoundingClientRect();

			return videoElementRect;
		},

		showNativePlayer: function () {
			this.getPlayerElement().showNativePlayer();
		},

		hideNativePlayer: function () {
			this.getPlayerElement().hideNativePlayer();
		},

		useNativePlayerControls: function () {
			return false;
		},

		/**
		 * Passes a fullscreen request to the layoutBuilder interface
		 */
		toggleFullscreen: function () {
			this.getPlayerElement().toggleFullscreen();
		},

		doneFSBtnPressed: function () {
			this.getPlayerElement().doneFSBtnPressed();
		},

		addNativeAirPlayButton: function () {
			this.getPlayerElement().addNativeAirPlayButton();
		},

		showNativeAirPlayButton: function (airPlayBtnPosition) {
			var x = airPlayBtnPosition.left;
			var y = airPlayBtnPosition.top;
			var w = airPlayBtnPosition.right - airPlayBtnPosition.left;
			var h = airPlayBtnPosition.bottom - airPlayBtnPosition.top;

			this.getPlayerElement().showNativeAirPlayButton([x, y, w, h]);
		},

		hideNativeAirPlayButton: function () {
			this.getPlayerElement().hideNativeAirPlayButton();
		},

		isVideoSiblingEnabled: function () {
			return false;
		}
	};
})(mediaWiki, jQuery);

<commit_msg>#FEC-2399 #comment fixed auto playback<commit_after>/**
 * Native embed library:
 *
 * Enables embedPlayer support for native iOS/Android webView playback system
 */

(function (mw, $) {
	"use strict";
	//make the player transparent to see the native iOS/Android player
	if (mw.getConfig("EmbedPlayer.ForceNativeComponent")) {
		$('body,.videoHolder').css('background-color', 'transparent');
	}

	mw.EmbedPlayerNativeComponent = {
		//Instance Name
		instanceOf: 'NativeComponent',

		// Flag to only load the video ( not play it )
		onlyLoadFlag: false,

		//Callback fired once video is "loaded"
		onLoadedCallback: null,

		// The previous "currentTime" to sniff seek actions
		// NOTE the bug where onSeeked does not seem fire consistently may no longer be applicable
		prevCurrentTime: -1,

		// Store the progress event ( updated during monitor )
		progressEventData: null,

		// If the media loaded event has been fired
		mediaLoadedFlag: null,

		// A flag to keep the video tag offscreen.
		keepPlayerOffScreenFlag: null,

		// A flag to designate the first play event, as to not propagate the native event in this case
		ignoreNextNativeEvent: null,

		// A local var to store the current seek target time:
		currentSeekTargetTime: null,

		// Disable switch source callback
		disableSwitchSourceCallback: false,

		// All the native events per:
		// http://www.w3.org/TR/html5/video.html#mediaevents
		nativeEvents: [
			'stop',
			'play',
			'pause',
			'canplay',
			'seeking',
			'seeked',
			'ended',
			'error',
			'stalled',
			'loadedmetadata',
			'timeupdate',
			'progress',
			'enterfullscreen',
			'exitfullscreen',
			'durationchange',
			'chromecastDeviceConnected',
			'chromecastDeviceDisConnected'
		],

		nativeActions: [
			'share',
			'openHomePage'
		],
		// Native player supported feature set
		supports: {
			'playHead': true,
			'pause': true,
			'fullscreen': true,
			'SourceSelector': false,
			'timeDisplay': true,
			'volumeControl': false,
			'overlays': true
		},

		setup: function (readyCallback) {
			var _this = this;
			mw.log("NativeComponent:: setup");
			// remove any existing pid ( if present )
			$('#' + this.pid).remove();

			var divElement = document.createElement("div");
			divElement.setAttribute('id', 'proxy');
			document.body.appendChild(divElement);

			this.proxyElement = divElement;
			try {
				if (NativeBridge.videoPlayer) {
					NativeBridge.videoPlayer.registePlayer(this.getPlayerElement());
					NativeBridge.videoPlayer.registerEmbedPlayer(this);
				}
			}
			catch (e) {
				alert(e);
			}

			this.applyMediaElementBindings();

			this.bindHelper("SourceChange", function () {
				_this.getPlayerElement().attr('src', this.getSrc());
			});
			this.bindHelper("layoutBuildDone ended", function () {
				_this.getPlayerElement().notifyLayoutReady();
			});
			this.bindHelper("showChromecastDeviceList", function () {
				mw.log("EmbedPlayerNativeComponent:: showChromecastDeviceList::");
				_this.getPlayerElement().showChromecastDeviceList();
			});

			this.resolveSrcURL(this.getSrc()).then(
				function (resolvedSrc) {
					mw.log("EmbedPlayerNativeComponent::resolveSrcURL get succeeded");
					_this.getPlayerElement().attr('src', resolvedSrc);
					readyCallback();
				},
				function () {
					mw.log("EmbedPlayerNativeComponent::resolveSrcURL get failed");
					_this.getPlayerElement().attr('src', _this.getSrc());
					readyCallback();
				}
			);
		},

		embedPlayerHTML: function () {
		},

		playerSwitchSource: function (source, switchCallback, doneCallback) {
			mw.log("NativeComponent:: playerSwitchSource");
			var _this = this;
			var vid = this.getPlayerElement();
			var switchBindPostfix = '.playerSwitchSource';

			// remove old binding:
			$(vid).unbind(switchBindPostfix);

			// add a loading indicator:
			_this.addPlayerSpinner();

			// empty out any existing sources:
			$(vid).empty();

			if (this.getSrc() != source.getSrc()) {
				vid.attr('src', source.getSrc());
			} else {
				vid.attr('src', this.getSrc());
			}

			this.isPauseLoading = false;
			_this.hideSpinner();
			if ($.isFunction(switchCallback)) {
				switchCallback(vid);

				if (!(!this.adsOnReplay && this.adTimeline.displayedSlotCount > 0)){
					setTimeout(function () {
						vid.play();
					}, 100);
				}
			}

			// Add the end binding if we have a post event:
			if ($.isFunction(doneCallback)) {
				$(vid).bind('ended' + switchBindPostfix, function (event) {
					if (_this.disableSwitchSourceCallback) {
						return;
					}
					// remove end binding:
					$(vid).unbind(switchBindPostfix);
					// issue the doneCallback
					doneCallback();

					// Support loop for older iOS
					// Temporarily disabled pending more testing or refactor into a better place.
					//if ( _this.loop ) {
					//	vid.play();
					//}
					return false;
				});
			}
		},

		/**
		 * Apply media element bindings
		 */
		applyMediaElementBindings: function () {
			var _this = this;
			mw.log("EmbedPlayerNative::MediaElementBindings");

			$.each(_this.nativeEvents, function (inx, eventName) {
				$(_this.getPlayerElement()).unbind(eventName).bind(eventName, function () {
					// make sure we propagating events, and the current instance is in the correct closure.
					if (_this._propagateEvents && _this.instanceOf == 'NativeComponent') {
						var argArray = $.makeArray(arguments);
						// Check if there is local handler:

						if (_this[ '_on' + eventName ]) {
							_this[ '_on' + eventName ].apply(_this, argArray);
						} else {
							// No local handler directly propagate the event to the abstract object:
							$(_this).trigger(eventName, argArray);
						}
					}
				});
			});
		},

		/**
		 * Get the embed player time
		 */
		getPlayerElementTime: function () {
			var _this = this;
			// Make sure we have .vid obj

			if (!this.getPlayerElement()) {
				mw.log('EmbedPlayerNative::getPlayerElementTime: ' + this.id + ' not in dom ( stop monitor)');
				this.stop();
				return false;
			}
			var ct = this.getPlayerElement().attr('currentTime');
			// Return 0 or a positive number:
			if (!ct || isNaN(ct) || ct < 0 || !isFinite(ct)) {
				return 0;
			}
			// Return the playerElement currentTime
			return  ct;
		},

		/**
		 * Get /update the playerElement value
		 */
		getPlayerElement: function () {
			return this.proxyElement;
		},

		/**
		 * Stop the player ( end all listeners )
		 */
		stop: function(){
			var _this = this;
			if( this.playerElement && this.playerElement.currentTime){
				this.playerElement.pause();
			}
			this.parent_stop();
		},

		/**
		 * Play back the video stream
		 * calls parent_play to update the interface
		 */

		play: function () {
			mw.log("EmbedPlayerNativeComponent:: play::");

			this.removePoster();

			if (this.parent_play()) {
				if (this.getPlayerElement()) { // update player
					this.getPlayerElement().play();
				}
				this.monitor();
			}
		},

		/**
		 * Pause the video playback
		 * calls parent_pause to update the interface
		 */
		pause: function () {
			mw.log("EmbedPlayerNativeComponent:: pause::");
			this.parent_pause(); // update interface
			if (this.getPlayerElement()) { // update player
				this.getPlayerElement().pause();
			}
		},

		seek: function (percentage) {
			mw.log("EmbedPlayerNativeComponent:: seek::");
			var seekTime = percentage * this.getDuration();
			this.getPlayerElement().attr('currentTime', seekTime);
			this.parent_seek(percentage);
		},

		doNativeAction: function (actionParams) {
			mw.log("EmbedPlayerNativeComponent:: doNativeAction::");
			this.getPlayerElement().attr('nativeAction', actionParams);
			this.getPlayerElement().doNativeAction();
		},

		nativeActionType: function (actionName) {
			return $.inArray(actionName, this.nativeActions);
		},

		isNativeApp: function () {
			return mw.getConfig("EmbedPlayer.ForceNativeComponent");
		},

		/**
		 * returns true if device can auto play
		 */
		canAutoPlay: function () {
			return true;
		},

		/**
		 * Handle the native play event
		 */
		_onplay: function () {
			mw.log("EmbedPlayerNativeComponent:: OnPlay::");

			$(this).trigger("playing");

			if (this.paused && this.parent_play()) {
				this.monitor();
			}
		},

		/**
		 * Handle the native paused event
		 */
		/**
		 * Handle the native paused event
		 */
		_onpause: function () {
			mw.log("EmbedPlayerNativeComponent:: OnPause::");
			this.parent_pause();
		},

		/**
		 * Local method for seeking event
		 * fired when "seeking"
		 */
		_onseeking: function () {
			mw.log("EmbedPlayerNative::onSeeking ");
			if (!this.seeking) {
				this.seeking = true;
				// Run the onSeeking interface update
				this.layoutBuilder.onSeek();

				if (this._propagateEvents) {
					// Trigger the html5 "seeking" trigger
					mw.log("EmbedPlayerNative::seeking:trigger:: ");
					this.triggerHelper('seeking');
				}
			}
		},

		/**
		 * Local method for seeked event
		 * fired when done seeking
		 */
		_onseeked: function () {
			mw.log("EmbedPlayerNativeComponent::onSeeked ");
			if (this.seeking) {
				this.seeking = false;

				if (this._propagateEvents) {
					mw.log("EmbedPlayerNativeComponent:: trigger: seeked");
					this.triggerHelper('seeked');
				}

				this.hideSpinner();
				this.updatePlayheadStatus();
				this.monitor();
			}
		},

		/**
		 * Local method for metadata ready
		 * fired when metadata becomes available
		 *
		 * Used to update the media duration to
		 * accurately reflect the src duration
		 */
		_onloadedmetadata: function () {
			//Fire "onLoaded" flags if set
			if (typeof this.onLoadedCallback == 'function') {
				this.onLoadedCallback();
			}

			// Trigger "media loaded"
			if (!this.mediaLoadedFlag) {
				$(this).trigger('mediaLoaded');
				this.mediaLoadedFlag = true;
			}
		},

		/**
		 * Local method for end of media event
		 */
		_onended: function (event) {
			if (this.getPlayerElement()) {
				mw.log('EmbedPlayer:native: onended:');
				if (this._propagateEvents) {
					this.onClipDone();
				}
			}
		},

		/**
		 * Local onClip done function for native player.
		 */
		onClipDone: function () {
			mw.log('EmbedPlayer:native: oneClipDone:');
			this.parent_onClipDone();
		},

		/**
		 * playback error
		 */
		_onerror: function (event, data) {
			this.triggerHelper('embedPlayerError', [ data ]);
		},

		/**
		 * buffer progress
		 * @param event
		 * @param progress
		 * @private
		 */
		_onprogress: function (event, progress) {
			if (typeof progress !== 'undefined') {
				this.updateBufferStatus(progress);
			}
		},

		/*
		 * Write the Embed html to the target
		 */
		getVideoElementPosition: function () {
			var videoElementDiv = parent.document.getElementById(this.id);
			var videoElementRect = videoElementDiv.getBoundingClientRect();

			return videoElementRect;
		},

		showNativePlayer: function () {
			this.getPlayerElement().showNativePlayer();
		},

		hideNativePlayer: function () {
			this.getPlayerElement().hideNativePlayer();
		},

		useNativePlayerControls: function () {
			return false;
		},

		/**
		 * Passes a fullscreen request to the layoutBuilder interface
		 */
		toggleFullscreen: function () {
			this.getPlayerElement().toggleFullscreen();
		},

		doneFSBtnPressed: function () {
			this.getPlayerElement().doneFSBtnPressed();
		},

		addNativeAirPlayButton: function () {
			this.getPlayerElement().addNativeAirPlayButton();
		},

		showNativeAirPlayButton: function (airPlayBtnPosition) {
			var x = airPlayBtnPosition.left;
			var y = airPlayBtnPosition.top;
			var w = airPlayBtnPosition.right - airPlayBtnPosition.left;
			var h = airPlayBtnPosition.bottom - airPlayBtnPosition.top;

			this.getPlayerElement().showNativeAirPlayButton([x, y, w, h]);
		},

		hideNativeAirPlayButton: function () {
			this.getPlayerElement().hideNativeAirPlayButton();
		},

		isVideoSiblingEnabled: function () {
			return false;
		}
	};
})(mediaWiki, jQuery);

<|endoftext|>


<commit_before>$(function() {

	/*
	Creates a new asset
	*/

	//var id=$('#meta-asset-id').html();
	var type = $('#meta-asset-type').val();

	var TAG_API_URL = '/publisher/api/tag/';
	var tagType = $('#meta-asset-type').val() + 's';

	var tagUrl = TAG_API_URL + tagType;
	var THEME = 'facebook';
	var TAG_CONTAINER = '#tag-container';
	var CHARS_REM = 'chars-rem';
	var DESC_MAX_CHARS = 995;

	$('#overview_description').after('');

	//Obtain all of the tags for the given asset type
	$.ajax({
		url : tagUrl,
		type : 'GET',
		success : function(response) {
			var tags = JSON.parse(response);
			$(TAG_CONTAINER).tokenInput(tags, {
				theme : THEME,
				allowFreeTagging : true
			});

		},
		error : function() {
			console.log('unable to fetch tag cloud for ' + type);
		}
	});

	$('#overview_name').on('blur', function() {
		var $this = $(this), flag = $('.icon-check-appname'), btnCreate = $('#btn-create-asset');
		var assetName = $this.val();

		if (!flag.length) {
			$this.after('');
			flag = $('.icon-check-appname');
		}

		//check if the asset name available as user types in
		$.ajax({
			url : '/publisher/api/validations/assets/' + type + '/overview_name/' + assetName,
			type : 'GET',
			success : function(response) {

				var result = JSON.parse(response);

				//Check if the asset was added
				if (result.ok) {
					flag.removeClass().addClass('icon-ok icon-check-appname').show();
					btnCreate.removeAttr('disabled');
                    $(".alert-error").hide();
				} else {
					flag.removeClass().addClass('icon-ban-circle icon-check-appname').show();
					btnCreate.attr('disabled', 'disabled');
				}

			},
			error : function(response) {
                flag.removeClass().addClass('icon-ok icon-check-appname').hide();
				showAlert('Unable to auto check Asset name availability', 'error');
			}
		});

	});

	$('#btn-create-asset').on('click', function(e) {
		e.preventDefault();

		var fields = $('#form-asset-create :input');
		var data = {};
		var formData = new FormData();
		fields.each(function() {
			if (this.type != 'button') {
				//console.log(this.value);
				data[this.id] = this.value;
				formData = fillForm(this, formData);
			}
		});

		//Append the tags to the form data
		formData.append('tags', obtainTags());

		$.ajax({
			url : '/publisher/asset/' + type,
			type : 'POST',
			data : formData,
			cache : false,
			contentType : false,
			processData : false,
			success : function(response) {

				var result = JSON.parse(response);

				//Check if the asset was added
				if (result.ok) {
					showAlert('Asset added successfully.', 'success');
					window.location = '/publisher/assets/' + type + '/';
				} else {
					var msg = processErrorReport(result.report);
					showAlert(msg, 'error');
				}

			},
			error : function(response) {
				showAlert('Failed to add asset.', 'error');
			}
		});

		//$.post('/publisher/asset/'+type, data);

	});

	$('#overview_description').keyup(function() {
		var self = $(this), length = self.val().length, left = DESC_MAX_CHARS - length, temp;

		if (length > DESC_MAX_CHARS) {
			temp = self.val();
			$(this).val(temp.substring(0, DESC_MAX_CHARS));
			console.log("Max chars reached");
			return;
		}
		$('.' + CHARS_REM).text('Characters left: ' + left);
	});

	/*
	 The function is used to build a report message indicating the errors in the form
	 @report: The report to be processed
	 @return: An html string containing the validation issues
	 */
	function processErrorReport(report) {
		var msg = '';
		for (var index in report) {

			for (var item in report[index]) {
				msg += report[index][item] + "
";
			}
		}

		return msg;
	}

	/*
	 The function is used to add a given field to a FormData element
	 @field: The field to be added to the formData
	 @formData: The FormDara object used to store the field
	 @return: A FormData object with the added field
	 */
	function fillForm(field, formData) {

		var fieldType = field.type;

		if (fieldType == 'file') {
			console.log('added ' + field.id + ' file.');
			formData.append(field.id, field.files[0]);
		} else {
			formData.append(field.id, field.value);
		}

		return formData;
	}

	/*
	 The function is used to obtain tags selected by the user
	 @returns: An array containing the tags selected by the user
	 */
	function obtainTags() {

		var tagArray = [];

		try {
			var tags = $(TAG_CONTAINER).tokenInput('get');

			for (var index in tags) {
				tagArray.push(tags[index].name);
			}

			return tagArray;
		} catch(e) {
			return tagArray;
		}

	}

	$('.selectpicker').selectpicker();
});
<commit_msg>sort indentation issue<commit_after>$(function() {

	/*
	Creates a new asset
	 */

	//var id=$('#meta-asset-id').html();
	var type = $('#meta-asset-type').val();

	var TAG_API_URL = '/publisher/api/tag/';
	var tagType = $('#meta-asset-type').val() + 's';

	var tagUrl = TAG_API_URL + tagType;
	var THEME = 'facebook';
	var TAG_CONTAINER = '#tag-container';
	var CHARS_REM = 'chars-rem';
	var DESC_MAX_CHARS = 995;

	$('#overview_description').after('');

	//Obtain all of the tags for the given asset type
	$.ajax({
		url : tagUrl,
		type : 'GET',
		success : function(response) {
			var tags = JSON.parse(response);
			$(TAG_CONTAINER).tokenInput(tags, {
				theme : THEME,
				allowFreeTagging : true
			});

		},
		error : function() {
			console.log('unable to fetch tag cloud for ' + type);
		}
	});

	$('#overview_name').on('blur', function() {
		var $this = $(this), flag = $('.icon-check-appname'), btnCreate = $('#btn-create-asset');
		var assetName = $this.val();

		if (!flag.length) {
			$this.after('');
			flag = $('.icon-check-appname');
		}

		//check if the asset name available as user types in
		$.ajax({
			url : '/publisher/api/validations/assets/' + type + '/overview_name/' + assetName,
			type : 'GET',
			success : function(response) {

				var result = JSON.parse(response);

				//Check if the asset was added
				if (result.ok) {
					flag.removeClass().addClass('icon-ok icon-check-appname').show();
					btnCreate.removeAttr('disabled');
					$(".alert-error").hide();
				} else {
					flag.removeClass().addClass('icon-ban-circle icon-check-appname').show();
					btnCreate.attr('disabled', 'disabled');
				}

			},
			error : function(response) {
				flag.removeClass().addClass('icon-ok icon-check-appname').hide();
				showAlert('Unable to auto check Asset name availability', 'error');
			}
		});

	});

	$('#btn-create-asset').on('click', function(e) {
		e.preventDefault();

		var fields = $('#form-asset-create :input');
		var data = {};
		var formData = new FormData();
		fields.each(function() {
			if (this.type != 'button') {
				//console.log(this.value);
				data[this.id] = this.value;
				formData = fillForm(this, formData);
			}
		});

		//Append the tags to the form data
		formData.append('tags', obtainTags());

		$.ajax({
			url : '/publisher/asset/' + type,
			type : 'POST',
			data : formData,
			cache : false,
			contentType : false,
			processData : false,
			success : function(response) {

				var result = JSON.parse(response);

				//Check if the asset was added
				if (result.ok) {
					showAlert('Asset added successfully.', 'success');
					window.location = '/publisher/assets/' + type + '/';
				} else {
					var msg = processErrorReport(result.report);
					showAlert(msg, 'error');
				}

			},
			error : function(response) {
				showAlert('Failed to add asset.', 'error');
			}
		});

		//$.post('/publisher/asset/'+type, data);

	});

	$('#overview_description').keyup(function() {
		var self = $(this), length = self.val().length, left = DESC_MAX_CHARS - length, temp;

		if (length > DESC_MAX_CHARS) {
			temp = self.val();
			$(this).val(temp.substring(0, DESC_MAX_CHARS));
			console.log("Max chars reached");
			return;
		}
		$('.' + CHARS_REM).text('Characters left: ' + left);
	});

	/*
	 The function is used to build a report message indicating the errors in the form
	 @report: The report to be processed
	 @return: An html string containing the validation issues
	 */
	function processErrorReport(report) {
		var msg = '';
		for (var index in report) {

			for (var item in report[index]) {
				msg += report[index][item] + "
";
			}
		}

		return msg;
	}

	/*
	 The function is used to add a given field to a FormData element
	 @field: The field to be added to the formData
	 @formData: The FormDara object used to store the field
	 @return: A FormData object with the added field
	 */
	function fillForm(field, formData) {

		var fieldType = field.type;

		if (fieldType == 'file') {
			console.log('added ' + field.id + ' file.');
			formData.append(field.id, field.files[0]);
		} else {
			formData.append(field.id, field.value);
		}

		return formData;
	}

	/*
	 The function is used to obtain tags selected by the user
	 @returns: An array containing the tags selected by the user
	 */
	function obtainTags() {

		var tagArray = [];

		try {
			var tags = $(TAG_CONTAINER).tokenInput('get');

			for (var index in tags) {
				tagArray.push(tags[index].name);
			}

			return tagArray;
		} catch(e) {
			return tagArray;
		}

	}

	$('.selectpicker').selectpicker();
});
<|endoftext|>


<commit_before>const Utils = require('./Utils');
const testIDs = require('../playground/src/testIDs');
const Android = require('./AndroidUtils');

const { elementByLabel, elementById, sleep } = Utils;

describe('screen stack', () => {
  beforeEach(async () => {
    await device.relaunchApp();
  });

  it('push and pop screen', async () => {
    await elementById(testIDs.PUSH_BUTTON).tap();
    await expect(elementById(testIDs.PUSHED_SCREEN_HEADER)).toBeVisible();
    await elementById(testIDs.POP_BUTTON).tap();
    await expect(elementById(testIDs.WELCOME_SCREEN_HEADER)).toBeVisible();
  });

  it(':android: push and pop screen without animation', async () => {
    await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
    await expect(elementById(testIDs.OPTIONS_SCREEN_HEADER)).toBeVisible();
    Android.pressBack();
    await expect(elementById(testIDs.WELCOME_SCREEN_HEADER)).toBeVisible();
  });

  it('pop screen deep in the stack', async () => {
    await elementById(testIDs.PUSH_BUTTON).tap();
    await expect(elementByLabel('Stack Position: 1')).toBeVisible();
    await elementById(testIDs.PUSH_BUTTON).tap();
    await expect(elementByLabel('Stack Position: 2')).toBeVisible();
    await elementById(testIDs.POP_PREVIOUS_BUTTON).tap();
    await expect(elementByLabel('Stack Position: 2')).toBeVisible();
    await elementById(testIDs.POP_BUTTON).tap();
    await expect(elementById(testIDs.WELCOME_SCREEN_HEADER)).toBeVisible();
  });

  it('pop to specific id', async () => {
    await elementById(testIDs.PUSH_BUTTON).tap();
    await elementById(testIDs.PUSH_BUTTON).tap();
    await elementById(testIDs.PUSH_BUTTON).tap();
    await expect(elementByLabel('Stack Position: 3')).toBeVisible();
    await elementById(testIDs.POP_STACK_POSITION_ONE_BUTTON).tap();
    await expect(elementByLabel('Stack Position: 1')).toBeVisible();
  });

  it('pop to root', async () => {
    await elementById(testIDs.PUSH_BUTTON).tap();
    await elementById(testIDs.PUSH_BUTTON).tap();
    await elementById(testIDs.PUSH_BUTTON).tap();
    await elementById(testIDs.POP_TO_ROOT).tap();
    await expect(elementById(testIDs.WELCOME_SCREEN_HEADER)).toBeVisible();
  });

  it('switch to tab', async () => {
    await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
    await expect(elementByLabel('This is tab 1')).toBeVisible();
    await elementById(testIDs.SWITCH_SECOND_TAB_BUTTON).tap();
    await expect(elementByLabel('This is tab 1')).toBeNotVisible();
    await expect(elementByLabel('This is tab 2')).toBeVisible();
  });

  it('switch to tab by cotnainerId', async () => {
    await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
    await expect(elementByLabel('This is tab 1')).toBeVisible();
    await elementById(testIDs.SWITCH_SECOND_TAB_BUTTON).tap();
    await expect(elementByLabel('This is tab 2')).toBeVisible();
    await elementById(testIDs.SWITCH_FIRST_TAB_BUTTON).tap();
    await expect(elementByLabel('This is tab 1')).toBeVisible();
  });

  it('push stack with multiple children', async () => {
    await elementById(testIDs.SHOW_MODAL_BUTTON).tap();
    await elementById(testIDs.MODAL_WITH_STACK_BUTTON).tap();
    await expect(elementByLabel('Screen 2')).toBeVisible();
    await elementById(testIDs.POP_BUTTON).tap();
    await expect(elementByLabel('Screen 1')).toBeVisible();
  });

  it('push external component with options', async () => {
    await elementById(testIDs.PUSH_EXTERNAL_COMPONENT_BUTTON).tap();
    await expect(elementByLabel('This is an external component')).toBeVisible();
    await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
  });
});
<commit_msg>setStackRoot e2e<commit_after>const Utils = require('./Utils');
const testIDs = require('../playground/src/testIDs');
const Android = require('./AndroidUtils');

const { elementByLabel, elementById, sleep } = Utils;

describe('screen stack', () => {
  beforeEach(async () => {
    await device.relaunchApp();
  });

  it('push and pop screen', async () => {
    await elementById(testIDs.PUSH_BUTTON).tap();
    await expect(elementById(testIDs.PUSHED_SCREEN_HEADER)).toBeVisible();
    await elementById(testIDs.POP_BUTTON).tap();
    await expect(elementById(testIDs.WELCOME_SCREEN_HEADER)).toBeVisible();
  });

  it(':android: push and pop screen without animation', async () => {
    await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
    await expect(elementById(testIDs.OPTIONS_SCREEN_HEADER)).toBeVisible();
    Android.pressBack();
    await expect(elementById(testIDs.WELCOME_SCREEN_HEADER)).toBeVisible();
  });

  it('pop screen deep in the stack', async () => {
    await elementById(testIDs.PUSH_BUTTON).tap();
    await expect(elementByLabel('Stack Position: 1')).toBeVisible();
    await elementById(testIDs.PUSH_BUTTON).tap();
    await expect(elementByLabel('Stack Position: 2')).toBeVisible();
    await elementById(testIDs.POP_PREVIOUS_BUTTON).tap();
    await expect(elementByLabel('Stack Position: 2')).toBeVisible();
    await elementById(testIDs.POP_BUTTON).tap();
    await expect(elementById(testIDs.WELCOME_SCREEN_HEADER)).toBeVisible();
  });

  it('pop to specific id', async () => {
    await elementById(testIDs.PUSH_BUTTON).tap();
    await elementById(testIDs.PUSH_BUTTON).tap();
    await elementById(testIDs.PUSH_BUTTON).tap();
    await expect(elementByLabel('Stack Position: 3')).toBeVisible();
    await elementById(testIDs.POP_STACK_POSITION_ONE_BUTTON).tap();
    await expect(elementByLabel('Stack Position: 1')).toBeVisible();
  });

  it('pop to root', async () => {
    await elementById(testIDs.PUSH_BUTTON).tap();
    await elementById(testIDs.PUSH_BUTTON).tap();
    await elementById(testIDs.PUSH_BUTTON).tap();
    await elementById(testIDs.POP_TO_ROOT).tap();
    await expect(elementById(testIDs.WELCOME_SCREEN_HEADER)).toBeVisible();
  });

  it('switch to tab', async () => {
    await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
    await expect(elementByLabel('This is tab 1')).toBeVisible();
    await elementById(testIDs.SWITCH_SECOND_TAB_BUTTON).tap();
    await expect(elementByLabel('This is tab 1')).toBeNotVisible();
    await expect(elementByLabel('This is tab 2')).toBeVisible();
  });

  it('switch to tab by cotnainerId', async () => {
    await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
    await expect(elementByLabel('This is tab 1')).toBeVisible();
    await elementById(testIDs.SWITCH_SECOND_TAB_BUTTON).tap();
    await expect(elementByLabel('This is tab 2')).toBeVisible();
    await elementById(testIDs.SWITCH_FIRST_TAB_BUTTON).tap();
    await expect(elementByLabel('This is tab 1')).toBeVisible();
  });

  it('push stack with multiple children', async () => {
    await elementById(testIDs.SHOW_MODAL_BUTTON).tap();
    await elementById(testIDs.MODAL_WITH_STACK_BUTTON).tap();
    await expect(elementByLabel('Screen 2')).toBeVisible();
    await elementById(testIDs.POP_BUTTON).tap();
    await expect(elementByLabel('Screen 1')).toBeVisible();
  });

  it('push external component with options', async () => {
    await elementById(testIDs.PUSH_EXTERNAL_COMPONENT_BUTTON).tap();
    await expect(elementByLabel('This is an external component')).toBeVisible();
    await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
  });

  it(':ios: set stack root component should be first in stack', async () => {
    await elementById(testIDs.PUSH_BUTTON).tap();
    await expect(elementByLabel('Stack Position: 1')).toBeVisible();
    await elementById(testIDs.SET_STACK_ROOT_BUTTON).tap();
    await expect(elementByLabel('Stack Position: 2')).toBeVisible();
    await elementById(testIDs.POP_BUTTON).tap();
    await expect(elementByLabel('Stack Position: 2')).toBeVisible();
  });

});
<|endoftext|>


<commit_before>// Logger variable. Set to true to turn on logging.
var LOG = true;
// Initialization status variable. Helps elimiate duplicate buttons.
var pinitInitInitialized = false;

(function() {
  try {
    // In case of insite navigation, document.ready will work.
    $(document).ready(function(){
      LOG && console.debug("DOM is ready. Time to start up Pin It.");
      visioplanetPinItInit();
    });
    $(window).load(function() {
      LOG && console.debug("Window load is complete. Time to start up Pin It.");
      visioplanetPinItInit();
    });
  } catch(e) {
    console.error('Visioplanet Pinit Initialization Exception: ' + e);
    windowLoadMethod();
  }
})();

function windowLoadMethod() {
  try {
    // If this is a direct post load, document.ready may not work and we will have to use window.load
    $(window).load(function() {
      visioplanetPinItInit();
    });
  } catch(e) {
    console.error('Visioplanet Pinit Initialization Exception: ' + e);
  }
}

function visioplanetPinItInit(){
  setTimeout (
    function() {
      LOG && console.debug("document status: " + document.readyState);
      if (document.readyState !== 'complete') {
        LOG && console.debug("Document's not ready. Waiting...");
        setTimeout(visioplanetPinItInit,1000);
      } else {
        // The following line will make sure that PinIt button initialization runs only once.
        // So that multiple PinIt buttons don't show up.
        if(pinitInitInitialized == true) {
          LOG && console.debug("PinIt initialization already started. Exiting visioplanetPinItInit() to avoid duplicates.");
          return;
        }

        // We're gonna wait for sometime and then do our work (URL remains the old one otherwise).
	    LOG && console.debug("Let's begin PinIt Initialization...");
        // We're gonna put the button on top of the image at the top right corner of it.
        // For that, we'd have to check the width of the image.

        pinitInitInitialized = true;
        var imgURL = "";
        var img = new Image();

        $("img").each(
          function(){
            img.src = $(this).attr('src');
            var theWidth;
            var theHeight;
            $(img).load(
              function(){
                theWidth = $(this).width();
                theHeight = $(this).height();
            });

            LOG && console.debug($(this).attr("src") + ": " + theWidth + "x" + theHeight);
            if((theWidth >= 400) || (theHeight >= 400)) {
              imgURL = $(this).attr("src");
              LOG && console.debug("Got the image URL: " + imgURL);
              return false;
              LOG && console.debug("This line shouldn't come up.");
            }
        });

        var htmlStr = "";

        LOG && console.debug("The tag's as follows:\n\n" + htmlStr);

        $(htmlStr).insertAfter("span.share-facebook.delay");
        // We're placing this button right after the +1 button.
        $("a.visioplanetPinItButton").css("z-index", 1000);
      }
    }
  , 2000);
}
<commit_msg>Update VisioplanetPinItByImgSizeInShareSection.js<commit_after>// Logger variable. Set to true to turn on logging.
var LOG = true;
// Initialization status variable. Helps elimiate duplicate buttons.
var pinitInitInitialized = false;

(function() {
  try {
    // In case of insite navigation, document.ready will work.
    $(document).ready(function(){
      LOG && console.debug("DOM is ready. Time to start up Pin It.");
      visioplanetPinItInit();
    });
    $(window).load(function() {
      LOG && console.debug("Window load is complete. Time to start up Pin It.");
      visioplanetPinItInit();
    });
  } catch(e) {
    console.error('Visioplanet Pinit Initialization Exception: ' + e);
    windowLoadMethod();
  }
})();

function windowLoadMethod() {
  try {
    // If this is a direct post load, document.ready may not work and we will have to use window.load
    $(window).load(function() {
      visioplanetPinItInit();
    });
  } catch(e) {
    console.error('Visioplanet Pinit Initialization Exception: ' + e);
  }
}

function visioplanetPinItInit(){
  setTimeout (
    function() {
      LOG && console.debug("document status: " + document.readyState);
      if (document.readyState !== 'complete') {
        LOG && console.debug("Document's not ready. Waiting...");
        setTimeout(visioplanetPinItInit,1000);
      } else {
        // The following line will make sure that PinIt button initialization runs only once.
        // So that multiple PinIt buttons don't show up.
        if(pinitInitInitialized == true) {
          LOG && console.debug("PinIt initialization already started. Exiting visioplanetPinItInit() to avoid duplicates.");
          return;
        }

        // We're gonna wait for sometime and then do our work (URL remains the old one otherwise).
	    LOG && console.debug("Let's begin PinIt Initialization...");
        // We're gonna put the button on top of the image at the top right corner of it.
        // For that, we'd have to check the width of the image.

        pinitInitInitialized = true;
        var imgURL = "";
        var img = new Image();

        $("img").each(
          function(){
            img.src = $(this).attr('src');
            var theWidth;
            var theHeight;
            $(img).load(
              function(){
                theWidth = this.width;
                theHeight = this.height;
            });

            LOG && console.debug($(this).attr("src") + ": " + theWidth + "x" + theHeight);
            if((theWidth >= 400) || (theHeight >= 400)) {
              imgURL = $(this).attr("src");
              LOG && console.debug("Got the image URL: " + imgURL);
              return false;
              LOG && console.debug("This line shouldn't come up.");
            }
        });

        var htmlStr = "";

        LOG && console.debug("The tag's as follows:\n\n" + htmlStr);

        $(htmlStr).insertAfter("span.share-facebook.delay");
        // We're placing this button right after the +1 button.
        $("a.visioplanetPinItButton").css("z-index", 1000);
      }
    }
  , 2000);
}
<|endoftext|>


<commit_before>// this class is not really related to a sampleToc but can be used for any TOC

import API from 'src/util/api';
import Versioning from 'src/util/versioning';

let defaultOptions = {
  group: 'all',
  varName: 'sampleToc',
  viewName: 'sample_toc',
  sort: (a, b) => {
    if (a.value.modificationDate > b.value.modificationDate) {
      return -1;
    } else if (a.value.modificationDate < b.value.modificationDate) {
      return 1;
    } else {
      return 0;
    }
  }
};

class SampleToc {
  /**
   * Create an object managing the Toc
   * @param {object} [options={}]
   * @param {object} [roc=undefined]
   * @param {string} [options.group='mine'] Group to retrieve products. mine, all of a specific group name
   * @param {string} [options.varName='sampleToc']
   * @param {string} [options.viewName='sample_toc']
   * @param {function} [options.sort] Callback, by default sort by reverse date
   * @param {function} [options.filter] Callback to filter the result
   */
  constructor(roc, options = {}) {
    this.roc = roc;
    this.options = Object.assign({}, defaultOptions, options);
  }

  setFilter(filter) {
    this.options.filter = filter;
    this.refresh();
  }

  /**
   * Retrieve the sample_toc and put the result in `sampleToc` variable
   *
   */
  refresh(options = {}) {
    console.log('Refresh sampleToc');
    let { group, sort, filter, viewName } = Object.assign(
      {},
      this.options,
      options
    );
    let mine = 0;
    let groups = '';
    group = String(group);
    if (group === 'mine') {
      mine = 1;
    } else if (group !== 'all') {
      groups = group;
    }

    return this.roc
      .query(viewName, {
        groups,
        mine,
        sort,
        filter,
        varName: this.options.varName
      })
      .then((entries) => {
        if (this.options.callback) {
          entries.forEach(this.options.callback);
        }
        return entries;
      });
  }

  /**
   * Retrieve the allowed groups for the logged in user and create 'groupForm'
   * variable and 'groupFormSchema' (for onde module). It will keep in a cookie
   * the last selected group. Calling this method should reload automatically
   * @param {object} [options={}]
   * @param {string} [varName='groupForm'] contains the name of the variable containing the form value
   * @param {string} [schemaVarName='groupFormSchema'] contains the name of the variable containing the form schema
   * @param {string} [cookieName='eln-default-sample-group''] cookie name containing the last selected group
   * @param {string} [filter] filter applied on first refresh
   * @param {string} [autoRefresh=true] refresh least after initialization
   * @return {string} the form to select group}
   */
  async initializeGroupForm(options = {}) {
    const {
      schemaVarName = 'groupFormSchema',
      varName = 'groupForm',
      cookieName = 'eln-default-sample-group',
      filter,
      autoRefresh = true
    } = options;

    let groups = (await this.roc.getGroupMembership()).map((g) => g.name);
    var possibleGroups = ['all', 'mine'].concat(groups);
    var defaultGroup = localStorage.getItem(cookieName);
    if (possibleGroups.indexOf(defaultGroup) === -1) {
      defaultGroup = 'all';
    }
    var schema = {
      type: 'object',
      properties: {
        group: {
          type: 'string',
          enum: possibleGroups,
          default: defaultGroup,
          required: true
        }
      }
    };
    API.createData(schemaVarName, schema);

    let groupForm = await API.createData(varName, { group: defaultGroup });

    this.options.group = groupForm.group;
    if (autoRefresh) {
      await this.refresh(filter);
    }
    let mainData = Versioning.getData();
    mainData.onChange((evt) => {
      if (evt.jpath[0] === varName) {
        localStorage.setItem(cookieName, groupForm.group);
        this.options.group = String(groupForm.group);
      }
    });

    return groupForm;
  }
}

module.exports = SampleToc;
<commit_msg>fix(SampleToc): setFilter returns the data<commit_after>// this class is not really related to a sampleToc but can be used for any TOC

import API from 'src/util/api';
import Versioning from 'src/util/versioning';

let defaultOptions = {
  group: 'all',
  varName: 'sampleToc',
  viewName: 'sample_toc',
  sort: (a, b) => {
    if (a.value.modificationDate > b.value.modificationDate) {
      return -1;
    } else if (a.value.modificationDate < b.value.modificationDate) {
      return 1;
    } else {
      return 0;
    }
  }
};

class SampleToc {
  /**
   * Create an object managing the Toc
   * @param {object} [options={}]
   * @param {object} [roc=undefined]
   * @param {string} [options.group='mine'] Group to retrieve products. mine, all of a specific group name
   * @param {string} [options.varName='sampleToc']
   * @param {string} [options.viewName='sample_toc']
   * @param {function} [options.sort] Callback, by default sort by reverse date
   * @param {function} [options.filter] Callback to filter the result
   */
  constructor(roc, options = {}) {
    this.roc = roc;
    this.options = Object.assign({}, defaultOptions, options);
  }

  setFilter(filter) {
    this.options.filter = filter;
    return this.refresh();
  }

  /**
   * Retrieve the sample_toc and put the result in `sampleToc` variable
   *
   */
  refresh(options = {}) {
    console.log('Refresh sampleToc');
    let { group, sort, filter, viewName } = Object.assign(
      {},
      this.options,
      options
    );
    let mine = 0;
    let groups = '';
    group = String(group);
    if (group === 'mine') {
      mine = 1;
    } else if (group !== 'all') {
      groups = group;
    }

    return this.roc
      .query(viewName, {
        groups,
        mine,
        sort,
        filter,
        varName: this.options.varName
      })
      .then((entries) => {
        if (this.options.callback) {
          entries.forEach(this.options.callback);
        }
        return entries;
      });
  }

  /**
   * Retrieve the allowed groups for the logged in user and create 'groupForm'
   * variable and 'groupFormSchema' (for onde module). It will keep in a cookie
   * the last selected group. Calling this method should reload automatically
   * @param {object} [options={}]
   * @param {string} [varName='groupForm'] contains the name of the variable containing the form value
   * @param {string} [schemaVarName='groupFormSchema'] contains the name of the variable containing the form schema
   * @param {string} [cookieName='eln-default-sample-group''] cookie name containing the last selected group
   * @param {string} [filter] filter applied on first refresh
   * @param {string} [autoRefresh=true] refresh least after initialization
   * @return {string} the form to select group}
   */
  async initializeGroupForm(options = {}) {
    const {
      schemaVarName = 'groupFormSchema',
      varName = 'groupForm',
      cookieName = 'eln-default-sample-group',
      filter,
      autoRefresh = true
    } = options;

    let groups = (await this.roc.getGroupMembership()).map((g) => g.name);
    var possibleGroups = ['all', 'mine'].concat(groups);
    var defaultGroup = localStorage.getItem(cookieName);
    if (possibleGroups.indexOf(defaultGroup) === -1) {
      defaultGroup = 'all';
    }
    var schema = {
      type: 'object',
      properties: {
        group: {
          type: 'string',
          enum: possibleGroups,
          default: defaultGroup,
          required: true
        }
      }
    };
    API.createData(schemaVarName, schema);

    let groupForm = await API.createData(varName, { group: defaultGroup });

    this.options.group = groupForm.group;
    if (autoRefresh) {
      await this.refresh(filter);
    }
    let mainData = Versioning.getData();
    mainData.onChange((evt) => {
      if (evt.jpath[0] === varName) {
        localStorage.setItem(cookieName, groupForm.group);
        this.options.group = String(groupForm.group);
      }
    });

    return groupForm;
  }
}

module.exports = SampleToc;
<|endoftext|>


<commit_before>import React from "react";
import PropTypes from "prop-types";
import styled from "styled-components";
import FeatherIcon from "../genericComponents/FeatherIcon";
import Button from "../genericComponents/Button";

import style from "../genericComponents/style.scss";
import LogoFI from "../genericComponents/LogoFI";
import { useGlobalContext } from "@agir/front/genericComponents/GobalContext";

const TopBarBar = styled.div`
  position: fixed;
  top: 0;
  left: 0;

  z-index: 10;

  width: 100%;
  padding: 0.75rem 2rem;

  background-color: #fff;
  box-shadow: 0px 0px 3px rgba(0, 35, 44, 0.1),
    0px 3px 2px rgba(0, 35, 44, 0.05);
`;

const TopBarContainer = styled.div`
  display: flex;
  justify-content: space-between;

  max-width: 1400px;
  margin: 0 auto;

  .large-only {
    @media only screen and (max-width: ${style.collapse}px) {
      display: none;
    }
  }

  .small-only {
    @media only screen and (min-width: ${style.collpase + 1}px) {
      display: none;
    }
  }

  .grow {
    flex-grow: 1;
  }

  .justify {
    justify-content: center;
  }
`;

const HorizontalFlex = styled.div`
  display: flex;
  align-items: center;

  * + * {
    margin-left: 1.25em;
  }
`;

const MenuLink = styled.a`
  display: flex;
  align-items: center;
  color: ${style.black1000};
  font-weight: 500;
  height: 3rem;

  * + * {
    margin-left: 0.5em;
  }

  :hover {
    text-decoration: none;
    color: ${style.black1000};
  }

  :hover > * {
    text-decoration: underline;
  }
`;

const SearchBar = styled.div`
  position: relative;
  max-width: 450px;
`;

const SearchBarIndicator = styled.div`
  position: absolute;
  left: 1rem;
  top: 0.75rem;
`;

const SearchBarButton = styled.button.attrs(() => ({ type: "submit" }))`
  position: absolute;
  right: 1em;
  top: 0.75rem;

  padding: 0;
  width: 1.5rem;

  border: 0;
  background: none;

  svg {
    display: none;
  }

  input:focus + & svg {
    display: block;
  }
`;

const SearchBarInput = styled.input.attrs(() => ({ type: "text", name: "q" }))`
  width: 100%;
  height: 3rem;
  margin: 0;
  padding: 0 3.5rem;

  border-radius: 3px;
  background-color: ${style.black50};
  color: ${style.black1000};
  border: 1px solid ${style.black50};

  &::placeholder {
    color: ${style.black500};
    font-weight: 500;
    opacity: 1;
  }
  &:focus {
    background: #fff;
    border: 1px solid ${style.black500};
  }
`;

const ConnectionInfo = ({ user, routes }) =>
  user === null ? (
    <>
      <MenuLink href={routes.logIn}>
        <FeatherIcon name="user" />
        Connexion
      </MenuLink>
      <Button color="secondary" href={routes.signIn} className="large-only">
        Créer mon compte
      </Button>
    </>
  ) : (
    <MenuLink
      href={
        user.isInsoumise
          ? routes.personalInformation
          : routes.contactConfiguration
      }
    >
      <FeatherIcon name="user" />
      {user.displayName}
    </MenuLink>
  );

ConnectionInfo.propTypes = {
  user: PropTypes.shape({
    displayName: PropTypes.string,
    isInsoumise: PropTypes.bool,
  }),
  routes: PropTypes.objectOf(PropTypes.string),
};

export const PureTopBar = ({ user, routes }) => {
  const inputRef = React.useRef();

  return (
    <TopBarBar>
      <TopBarContainer>
        <MenuLink href={routes.search} className="small-only">
          <FeatherIcon name="search" />
        </MenuLink>

        <HorizontalFlex className="grow justify">
          <MenuLink href={routes.dashboard}>
            <LogoFI height="3rem" />
          </MenuLink>
          <form className="large-only grow" method="get" action={routes.search}>
            <SearchBar>
              <SearchBarIndicator>
                <FeatherIcon
                  name="search"
                  color={style.black500}
                  alignOnText={false}
                />
              </SearchBarIndicator>
              <SearchBarInput
                ref={inputRef}
                placeholder="Rechercher un groupe ou un événement"
              />
              <SearchBarButton
                onClick={(e) => {
                  if (inputRef.current.value.trim() === "") {
                    inputRef.current.focus();
                    e.preventDefault();
                  }
                }}
              >
                <FeatherIcon
                  name="arrow-right"
                  color={style.black500}
                  alignOnText={false}
                />
              </SearchBarButton>
            </SearchBar>
          </form>
        </HorizontalFlex>
        <HorizontalFlex>
          <MenuLink href={routes.help} className="large-only">
            <FeatherIcon name="help-circle" />
            Aide
          </MenuLink>
          <ConnectionInfo user={user} routes={routes} />
        </HorizontalFlex>
      </TopBarContainer>
    </TopBarBar>
  );
};
PureTopBar.propTypes = {
  user: PropTypes.shape({
    displayName: PropTypes.string,
    isInsoumise: PropTypes.bool,
  }),
  routes: PropTypes.objectOf(PropTypes.string),
};
PureTopBar.defaultProps = {
  user: null,
  routes: {
    dashboard: "#dashboard",
    search: "#search",
    help: "#help",
    personalInformation: "#personalInformation",
    contactConfiguration: "#contactConfiguration",
    signIn: "#signIn",
    logIn: "#logIn",
  },
};

const TopBar = () => {
  const config = useGlobalContext();
  return <PureTopBar {...config} />;
};

export default TopBar;
<commit_msg>Fix: problème de media queries<commit_after>import React from "react";
import PropTypes from "prop-types";
import styled from "styled-components";
import FeatherIcon from "../genericComponents/FeatherIcon";
import Button from "../genericComponents/Button";

import style from "../genericComponents/style.scss";
import LogoFI from "../genericComponents/LogoFI";
import { useGlobalContext } from "@agir/front/genericComponents/GobalContext";

const TopBarBar = styled.div`
  position: fixed;
  top: 0;
  left: 0;

  z-index: 10;

  width: 100%;
  padding: 0.75rem 2rem;

  background-color: #fff;
  box-shadow: 0px 0px 3px rgba(0, 35, 44, 0.1),
    0px 3px 2px rgba(0, 35, 44, 0.05);
`;

const TopBarContainer = styled.div`
  display: flex;
  justify-content: space-between;

  max-width: 1400px;
  margin: 0 auto;

  & .large-only {
    @media only screen and (max-width: ${style.collapse}px) {
      display: none;
    }
  }

  & .small-only {
    @media only screen and (min-width: ${+style.collapse + 1}px) {
      display: none;
    }
  }

  .grow {
    flex-grow: 1;
  }

  .justify {
    justify-content: center;
  }
`;

const HorizontalFlex = styled.div`
  display: flex;
  align-items: center;

  * + * {
    margin-left: 1.25em;
  }
`;

const MenuLink = styled.a`
  display: flex;
  align-items: center;
  color: ${style.black1000};
  font-weight: 500;
  height: 3rem;

  * + * {
    margin-left: 0.5em;
  }

  :hover {
    text-decoration: none;
    color: ${style.black1000};
  }

  :hover > * {
    text-decoration: underline;
  }
`;

const SearchBar = styled.div`
  position: relative;
  max-width: 450px;
`;

const SearchBarIndicator = styled.div`
  position: absolute;
  left: 1rem;
  top: 0.75rem;
`;

const SearchBarButton = styled.button.attrs(() => ({ type: "submit" }))`
  position: absolute;
  right: 1em;
  top: 0.75rem;

  padding: 0;
  width: 1.5rem;

  border: 0;
  background: none;

  svg {
    display: none;
  }

  input:focus + & svg {
    display: block;
  }
`;

const SearchBarInput = styled.input.attrs(() => ({ type: "text", name: "q" }))`
  width: 100%;
  height: 3rem;
  margin: 0;
  padding: 0 3.5rem;

  border-radius: 3px;
  background-color: ${style.black50};
  color: ${style.black1000};
  border: 1px solid ${style.black50};

  &::placeholder {
    color: ${style.black500};
    font-weight: 500;
    opacity: 1;
  }
  &:focus {
    background: #fff;
    border: 1px solid ${style.black500};
  }
`;

const ConnectionInfo = ({ user, routes }) =>
  user === null ? (
    <>
      <MenuLink href={routes.logIn}>
        <FeatherIcon name="user" />
        Connexion
      </MenuLink>
      <Button color="secondary" href={routes.signIn} className="large-only">
        Créer mon compte
      </Button>
    </>
  ) : (
    <MenuLink
      href={
        user.isInsoumise
          ? routes.personalInformation
          : routes.contactConfiguration
      }
    >
      <FeatherIcon name="user" />
      {user.displayName}
    </MenuLink>
  );

ConnectionInfo.propTypes = {
  user: PropTypes.shape({
    displayName: PropTypes.string,
    isInsoumise: PropTypes.bool,
  }),
  routes: PropTypes.objectOf(PropTypes.string),
};

export const PureTopBar = ({ user, routes }) => {
  const inputRef = React.useRef();

  return (
    <TopBarBar>
      <TopBarContainer>
        <MenuLink href={routes.search} className="small-only">
          <FeatherIcon name="search" />
        </MenuLink>

        <HorizontalFlex className="grow justify">
          <MenuLink href={routes.dashboard}>
            <LogoFI height="3rem" />
          </MenuLink>
          <form className="large-only grow" method="get" action={routes.search}>
            <SearchBar>
              <SearchBarIndicator>
                <FeatherIcon
                  name="search"
                  color={style.black500}
                  alignOnText={false}
                />
              </SearchBarIndicator>
              <SearchBarInput
                ref={inputRef}
                placeholder="Rechercher un groupe ou un événement"
              />
              <SearchBarButton
                onClick={(e) => {
                  if (inputRef.current.value.trim() === "") {
                    inputRef.current.focus();
                    e.preventDefault();
                  }
                }}
              >
                <FeatherIcon
                  name="arrow-right"
                  color={style.black500}
                  alignOnText={false}
                />
              </SearchBarButton>
            </SearchBar>
          </form>
        </HorizontalFlex>
        <HorizontalFlex>
          <MenuLink href={routes.help} className="large-only">
            <FeatherIcon name="help-circle" />
            Aide
          </MenuLink>
          <ConnectionInfo user={user} routes={routes} />
        </HorizontalFlex>
      </TopBarContainer>
    </TopBarBar>
  );
};
PureTopBar.propTypes = {
  user: PropTypes.shape({
    displayName: PropTypes.string,
    isInsoumise: PropTypes.bool,
  }),
  routes: PropTypes.objectOf(PropTypes.string),
};
PureTopBar.defaultProps = {
  user: null,
  routes: {
    dashboard: "#dashboard",
    search: "#search",
    help: "#help",
    personalInformation: "#personalInformation",
    contactConfiguration: "#contactConfiguration",
    signIn: "#signIn",
    logIn: "#logIn",
  },
};

const TopBar = () => {
  const config = useGlobalContext();
  return <PureTopBar {...config} />;
};

export default TopBar;
<|endoftext|>