Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
ct_browser = function(){
var ua= navigator.userAgent, tem,
M= ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
if(/trident/i.test(M[1])){
tem= /\brv[ :]+(\d+)/g.exec(ua) || [];
return 'IE '+(tem[1] || '');
}
if(M[1]=== 'Chrome'){
tem= ua.match(/\b(OPR|Edge)\/(\d+)/);
if(tem!= null) return tem.slice(1).join(' ').replace('OPR', 'Opera');
}
M= M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
if((tem= ua.match(/version\/(\d+)/i))!= null) M.splice(1, 1, tem[1]);
return M
};
if (location.protocol === 'https:' && ct_browser()[0] == "Chrome" && ct_browser()[1] > 45) {
ct_webrtc = true;
}
else if (location.protocol === 'https:' && ct_browser()[0] == "Firefox" && ct_browser()[1] > 39) {
ct_webrtc = true;
}
else {
ct_webrtc = false
}
//
// CameraTag
//
if (typeof(CameraTag) == "undefined") {
CameraTag = function(){};
CameraTag = new function() {
var self = this;
self.version = "7";
var appServer = "www.cameratag.com";
self.cameras = {};
self.players = {};
self.uploader = {};
var callbacks = {};
var settingUp = false;
var ct_jwplayer;
var jwplayerInjected = false;
var allow_play_count = true;
if (typeof(CT_verbose_mode) == "undefined") {
CT_verbose_mode = false;
}
// i18n
if (typeof(CT_i18n) == "undefined") {
CT_i18n = []
}
CT_i18n[0] = CT_i18n[0] || "To record this video using your mobile phone please visit <<url>> in your mobile browser"
CT_i18n[1] = CT_i18n[1] || "Your mobile device does not support video uploading"
CT_i18n[2] = CT_i18n[2] || "Please install Flash Player 11 or higher: https://get.adobe.com/flashplayer"
CT_i18n[3] = CT_i18n[3] || "Unable to embed video recorder. Please make sure you have Flash Player 11 or higher installed"
CT_i18n[4] = CT_i18n[4] || "choose a method below to submit your video"
CT_i18n[5] = CT_i18n[5] || "record from webcam"
CT_i18n[6] = CT_i18n[6] || "upload a file"
CT_i18n[7] = CT_i18n[7] || "record from phone"
CT_i18n[8] = CT_i18n[8] || "wave to the camera"
CT_i18n[9] = CT_i18n[9] || "recording in"
CT_i18n[10] = CT_i18n[10] || "uploading..."
CT_i18n[11] = CT_i18n[11] || "click to stop recording"
CT_i18n[12] = CT_i18n[12] || "click to skip review"
CT_i18n[13] = CT_i18n[13] || "Accept"
CT_i18n[14] = CT_i18n[14] || "Re-record"
CT_i18n[15] = CT_i18n[15] || "Review Recording"
CT_i18n[16] = CT_i18n[16] || "please wait while we push pixels"
CT_i18n[17] = CT_i18n[17] || "published"
CT_i18n[18] = CT_i18n[18] || "Enter your <b>mobile phone number</b> below and we will text you a link for mobile recording"
CT_i18n[19] = CT_i18n[19] || "Send Mobile Link"
CT_i18n[20] = CT_i18n[20] || "cancel"
CT_i18n[21] = CT_i18n[21] || "Check your phone for mobile recording instructions"
CT_i18n[22] = CT_i18n[22] || "or point your mobile browser to"
CT_i18n[23] = CT_i18n[23] || "drop file to upload"
CT_i18n[24] = CT_i18n[24] || "sending your message"
CT_i18n[25] = CT_i18n[25] || "please enter your phone number!"
CT_i18n[26] = CT_i18n[26] || "that does not appear to be a valid phone number"
CT_i18n[27] = CT_i18n[27] || "Unable to send SMS."
CT_i18n[28] = CT_i18n[28] || "No Camera Detected"
CT_i18n[29] = CT_i18n[29] || "No Microphone Detected"
CT_i18n[30] = CT_i18n[30] || "Camera Access Denied"
CT_i18n[31] = CT_i18n[31] || "Lost connection to server"
CT_i18n[32] = CT_i18n[32] || "Playback failed"
CT_i18n[33] = CT_i18n[33] || "Unable To Connect"
CT_i18n[34] = CT_i18n[34] || "Unable to publish your recording."
CT_i18n[35] = CT_i18n[35] || "Unable to submit form data."
CT_i18n[36] = CT_i18n[36] || "uploading your video"
CT_i18n[37] = CT_i18n[37] || "buffering video playback"
CT_i18n[38] = CT_i18n[38] || "publishing"
CT_i18n[39] = CT_i18n[39] || "connecting..."
CT_i18n[40] = CT_i18n[40] || "negotiating firewall..."
CT_i18n[41] = CT_i18n[41] || "Oh No! It looks like your browser paused the recorder"
CT_i18n[42] = CT_i18n[42] || "That does not appear to be a valid video file. Proceed anyway?"
CT_i18n[43] = CT_i18n[43] || "We were unable to activate your camera and/or microphone."
self.setup = function() {
// prevent double setup
if (settingUp) {
raise("setup() called while CameraTag already setting up. Try again later");
return;
}
settingUp = true;
// create instances for each camera tag in the page
instantiateCameras();
// create instances for each video tag in the page
instantiatePlayers();
settingUp = false;
}
var jwplayerReady = function() {
// jwplayer is ready
if ( typeof(jwplayer) == "function" ) {
ct_jwplayer = jwplayer;
return true;
}
// embed the script if we havent already
else if (!jwplayerInjected) {
var jwplayer_script = document.createElement('script');
jwplayer_script.src = "//"+appServer+"/api/v"+self.version+"/js/jwplayer.js";
// var jwplayer_css = document.createElement('link');
// jwplayer_css.href = "//"+appServer+"/"+self.version+"/jwplayer.css";
// jwplayer_css.rel = "stylesheet";
// jwplayer_css.type = "text/css";
document.body.insertBefore( jwplayer_script, document.body.firstChild );
// document.body.insertBefore( jwplayer_css, document.body.firstChild );
jwplayerInjected = true;
return false
}
else {
return false;
}
};
var new_video = function(camera_uuid, callback) {
$.ajax({
url: "//"+appServer+"/api/v"+CameraTag.version+"/cameras/"+camera_uuid+"/new_video.json",
type:"get",
success: function(response) {
if (response.camera != undefined) {
var new_video = {};
new_video.uuid = generateUUID();
// setup video formats
new_video.formats = {};
if (response.camera.formats) {
$(response.camera.formats).each(function(index, format){
new_video.formats[format.name] = {};
})
}
// build response
callback({
success: true,
camera: response.camera,
video: new_video,
videoServer: response.videoServer
});
}
else {
callback({ success: false, message: response.error });
}
},
error: function(jqXHR, textStatus, errorThrown) {
//sendStat("authorization_error", {code: jqXHR.status});
callback({ success: false, message: "error initializing recorder" });
}
});
}
var generateUUID = function(){
var d = new Date().getTime();
var uuid = 'v-xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=='x' ? r : (r&0x3|0x8)).toString(16);
});
return uuid;
};
var instantiateCameras = function() {
$("camera").each(function(index, camera_el) {
new CameraTagRecorder(camera_el);
});
};
var instantiatePlayers = function() {
$("video").each(function(index, video_el){
if ($(video_el).attr("data-uuid") || $(video_el).attr("data-video-id")) {
new CameraTagPlayer(video_el);
}
});
};
// EVENT OBSERVATION
self.observe = function(camera_dom_id,event_name,callback,priority) {
priority = priority || false;
if ( !callbacks[camera_dom_id] )
callbacks[camera_dom_id] = {};
if ( !callbacks[camera_dom_id][event_name] )
callbacks[camera_dom_id][event_name] = [];
if (priority) {
callbacks[camera_dom_id][event_name].splice(0,0,callback);
}
else {
callbacks[camera_dom_id][event_name].push(callback);
}
};
self.fire = function(camera_dom_id,event_name,data) {
if ( !callbacks[camera_dom_id] )
callbacks[camera_dom_id] = {};
if ( !callbacks[camera_dom_id][event_name] )
callbacks[camera_dom_id][event_name] = [];
setTimeout(function(){
fire_handlers(camera_dom_id,event_name,data);
}, 1);
};
var fire_handlers = function(camera_dom_id,event_name,data) {
for( i = 0; i < callbacks[camera_dom_id][event_name].length; i++ ) {
try {
callbacks[camera_dom_id][event_name][i](data);
}
catch(err) {}
}
}
self.stopObserving = function(camera_dom_id,event_name,callback) {
if ( !callbacks[camera_dom_id] )
callbacks[camera_dom_id] = {};
if ( !callbacks[camera_dom_id][event_name] )
callbacks[camera_dom_id][event_name] = [];
for( i = 0; i < callbacks[camera_dom_id][event_name].length; i++ ) {
if( callbacks[camera_dom_id][event_name][i] == callback )
callbacks[camera_dom_id][event_name].splice(i,1);
}
};
var publish_on_server = function(camera_uuid, video_uuid, type, videoServer, signature, signature_expiration, video_data_object, video_name, video_description) {
$.ajax({
url: "//"+appServer+"/api/v"+CameraTag.version+"/cameras/"+camera_uuid+"/videos/"+video_uuid+"/publish.json",
type:"post",
data: {
publish_type: type,
server: videoServer,
referer: window.location.toString(),
version: self.version,
signature: signature,
signature_expiration: signature_expiration,
video_data: JSON.stringify(video_data_object),
video_name: video_name,
video_description: video_description
},
success: function(response) {
if (response["uuid"]) {
self.fire(video_uuid, "published", response);
}
else {
self.fire(video_uuid, "publishFailed", {success: false, video_uuid: video_uuid, message: response});
}
},
error: function() {
self.fire(video_uuid, "publishFailed", {success: false, video_uuid: video_uuid, message: "unkown error"});
}
})
};
self.prototype = self; // legacy support
var CameraTagRecorder = function(camera_el) {
// main data objects
var cachebuster = parseInt( Math.random() * 100000000 );
var self = this;
var this_recorder = this;
var camera_uuid = $(camera_el).attr("data-uuid") || $(camera_el).attr("data-app-id") || $(camera_el).attr("id");
var signature = $(camera_el).attr("data-signature");
var signature_expiration = $(camera_el).attr("data-signature-expiration");
var video_name = $(camera_el).attr("data-video-name");
var video_description = $(camera_el).attr("data-video-description");
var video_data = $(camera_el).attr("data-video-data");
var permitted_extensions = ["mov", "mp4", "webm", "flv", "mv4", "mov", "avi", "wmv", "mpeg", "mpg"];
try {
var video_data_object = JSON.parse(video_data);
}
catch (e) {
if (video_data != undefined) {
//console.logwarn("Could not parse video-data JSON from <camera> attributes.");
}
}
var camera = {};
var video = {};
var dom_id = $(camera_el).attr("id") || cachebuster;
var uploader;
var txt_message;
var input_name;
var css_url;
var record_timer;
var paused_timer;
var record_timer_count = 0;
var record_timer_prompt = 0;
var existing_uuid = $(camera_el).attr("data-video-uuid");
var processed_timer;
var published_timer;
// mobile vars
var mobile_browser;
var mobile_upload_supported;
var mobile_enabled;
// overwritable params
var sources;
var fps;
var className;
var videoServer; // gets set by server in auth callback
var height;
var width;
var hResolution;
var vResolution;
var maxLength;
var skipPreview;
var skipFontAwesome;
var videoBitRate;
var skipAutoDetect;
var simpleSecurity;
var preRollLength;
var recordOnConnect;
var publishOnUpload;
var uploadOnSelect;
var flipRecordPreview;
var poll_processed;
var font_size;
var default_sms_country;
// DOM references
var container;
var recorder;
var start_screen;
var paused_screen;
var playback_screen;
var recording_screen;
var camera_detection_screen;
var countdown_screen;
var countdown_status;
var upload_screen;
var upload_status;
var accept_screen;
var wait_screen;
var wait_message;
var completed_screen;
var error_screen;
var error_message;
var sms_screen;
var sms_input;
var thumb_bg;
var check_phone_screen;
var alternative_prompt;
// Control functions
self.connect = function(){};
self.play = function(){};
self.record = function(){};
self.stopRecording = function(){};
self.stopPlayback = function(){};
self.showFlashSettings = function(){};
// state management
var state;
var current_screen;
var paused = false;
var connected = false;
var readyToRecord = false;
var countdown_counter = 0;
var uploading = false;
var error_messages = [];
var readyToPublish = false;
var initialized = false;
var publishType = "webcam";
// keep a reference to this instance in the Class prototype
CameraTag.cameras[dom_id] = self;
var setup = function() {
css_url = $(camera_el).attr("data-cssurl") || camera.cssURL || "//d2k4cphdw09pf3.cloudfront.net/"+CameraTag.version+"/cameratag.css";
if (css_url !== "false") {
$.get(css_url, function(response){
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (style.styleSheet){
style.styleSheet.cssText = response;
} else {
style.appendChild(document.createTextNode(response));
}
head.appendChild(style);
init();
});
} else {
init();
}
};
var init = function() {
// get permission for a new video
new_video(camera_uuid, function(server_response){
if (server_response.success) {
camera = server_response.camera;
video = server_response.video;
if (existing_uuid) {
video.uuid = existing_uuid
}
videoServer = server_response.videoServer;
// observe new video for its published state
CameraTag.observe(video.uuid, "published", function(published_video) {
if (video.uuid == published_video.uuid) {
state = "published";
populate_hidden_inputs();
self.loadInterface(completed_screen);
if (connected) {
self.disconnect();
}
//sendStat("publish_success");
CameraTag.fire(dom_id, "published", video);
if (poll_processed) {
pollForProcessed();
}
}
}, true);
// failed publish
CameraTag.observe(video.uuid, "publishFailed", function(error) {
if (video.uuid == error.video_uuid) {
throw_error(CT_i18n[34] + error.message.error);
}
}, true);
}
else {
error_messages.push(server_response.message);
//sendStat("authorization_error", {message: server_response.message, code: 200});
}
// initialize the interface
setup_interface();
if (error_messages.length > 0) {
throw_error(error_messages[0]);
return;
}
self.loadInterface(start_screen, true);
// font-size
font_size = parseInt($(container).height() / 14);
if (font_size < 12) {
font_size = 12;
}
$(container).css({fontSize: font_size+"px"});
});
}
var setup_interface = function() {
// setup prarms with preference to passed in as data attributes on the camera tag
input_name = $(camera_el).attr("name") || dom_id;
inline_styles = $(camera_el).attr("style");
className = $(camera_el).attr("class") || camera.className || "";
fps = camera.formats && camera.formats[0].fps || 24;
if (camera.formats && ct_webrtc) {
width = camera.formats[0].height < 360 ? camera.formats[0].width : camera.formats[0].width / 2;
height = camera.formats[0].height < 360 ? camera.formats[0].height : camera.formats[0].height / 2;
hResolution = camera.formats[0].width > 640 ? 896 : camera.formats[0].width;
vResolution = camera.formats[0].width > 640 ? 504 : camera.formats[0].height;
}
else if (camera.formats) {
width = camera.formats[0].height < 360 ? camera.formats[0].width : camera.formats[0].width / 2;
height = camera.formats[0].height < 360 ? camera.formats[0].height : camera.formats[0].height / 2;
hResolution = camera.formats[0].width;
vResolution = camera.formats[0].height;
} else {
// this is used to define an area to display an error
width = 300;
height = 200;
hResolution = 300;
vResolution = 200;
}
sources = $(camera_el).attr("data-sources") || "record,upload,sms";
sources = sources.replace(" ", "").split(",");
maxLength = $(camera_el).attr("data-maxlength") || $(camera_el).attr("data-max-length") || camera.maxLength || 30;
videoBitRate = $(camera_el).attr("data-videobitrate");
if (camera.formats) {
videoBitRate = videoBitRate || camera.formats[0].videoBitRate;
}
mobile_browser = isMobile();
mobile_upload_supported = mobileUploadSupported();
mobile_enabled = mobile_browser && mobile_upload_supported && camera.allowMobileUploads;
CT_i18n[0] = $(camera_el).attr("data-txt-message") || CT_i18n[0];
autoPreview = $(camera_el).attr("data-autopreview") == "true"
skipPreview = !autoPreview;
publishOnUpload = $(camera_el).attr("data-publish-on-upload") != "false";
skipFontAwesome = $(camera_el).attr("data-skip-font-awesome") == "true";
uploadOnSelect = $(camera_el).attr("data-upload-on-select") != "false";
recordOnConnect = $(camera_el).attr("data-record-on-connect") != "false";
skipAutoDetect = $(camera_el).attr("data-skip-auto-detect") != "false" || $(camera_el).attr("data-detect-camera") == "true";
simpleSecurity = $(camera_el).attr("data-simple-security") == "true";
flipRecordPreview = $(camera_el).attr("data-mirror-recording") != "false";
poll_processed = $(camera_el).attr("data-poll-for-processed") == "true" || camera.poll_for_processed;
ct_webrtc = ct_webrtc && $(camera_el).attr("data-webrtc") != "false";
default_sms_country = $(camera_el).attr("default-sms-country");
if ($(camera_el).attr("data-pre-roll-length")) {
preRollLength = parseInt($(camera_el).attr("data-pre-roll-length"))
}
else {
preRollLength = 5;
}
// build the control elements
createUploader();
buildInterface();
setupEventObservers();
// check for non-compatible mobile device
if (mobile_browser && !mobile_enabled) {
error_messages.push(CT_i18n[1]);
//sendStat("unsupported_device_error");
}
// show error messages if there are any
if (error_messages.length > 0) {
throw_error(error_messages.join("\n"));
return;
}
// initialize if we're mobile or webrtc (otherwise let the swf do it)
if (mobile_browser || ct_webrtc) {
CameraTag.fire(dom_id, "initialized");
}
};
var embed_recording_stack = function() {
if (ct_webrtc && typeof(navigator.getUserMedia) == "function") {
recorder = new WebRTCRecorder();
}
else if (!mobile_enabled) {
embedSWF();
}
else {
// mobile html5 upload
}
}
var embedSWF = function() {
var flashvars = {
videoServer: videoServer,
videoUUID: video.uuid,
cameraUUID: camera.uuid,
domID: dom_id,
maxLength: maxLength,
hResolution: hResolution,
vResolution: vResolution,
fps: fps,
videoBitRate: videoBitRate,
skipAutoDetect: skipAutoDetect,
flipRecordPreview: flipRecordPreview,
simpleSecurity: simpleSecurity,
verboseMode: CT_verbose_mode
};
var params = {
allowfullscreen: 'true',
allowscriptaccess: 'always',
wmode: "transparent"
};
var attributes = {
id: dom_id+"_swf",
name: dom_id+"_swf"
};
swfobject.embedSWF("//"+appServer+"/"+CameraTag.version+"/camera.swf?"+cachebuster, dom_id+"_recorder_placeholder", "100%", "100%", '11.1.0', 'https://'+appServer+'/'+CameraTag.version+'/expressInstall.swf', flashvars, params, attributes, checkSWF);
if (swfobject.getFlashPlayerVersion().major < 11) {
error_messages.push(CT_i18n[2]);
}
};
var checkSWF = function(e){
recorder = $("#"+dom_id+"_swf")[0];
if (recorder == undefined && e.success == true) {
var flash_ver = swfobject.getFlashPlayerVersion().major + "." + swfobject.getFlashPlayerVersion().minor;
//sendStat("no_flash_error", {flash_version: flash_ver, source: "post embed check"});
error_messages.push(CT_i18n[2]);
}
}
self.remove = function() {
clearTimeout(processed_timer);
clearTimeout(published_timer);
clearInterval(record_timer);
countdown_counter = 0;
record_timer_count = 0;
uploading = false;
error_messages = [];
readyToPublish = false;
if (connected) {
self.disconnect();
}
self.uploader.destroy();
container.remove();
delete CameraTag.cameras[dom_id];
delete callbacks[dom_id];
}
self.getState = function() {
return state;
};
var createUploader = function() {
uploader = new Evaporate({
signerUrl: '//'+appServer+'/api/v'+CameraTag.version+'/videos/upload_signature',
aws_key: 'AKIAJCHWZMZ35EB62V2A',
bucket: 'assets.cameratag.com',
aws_url: 'https://d2az0z6s4nrieh.cloudfront.net',
cloudfront: true,
logging: false
});
self.uplaoder = uploader;
// dont allow upload source if uploader not supported
if (!uploader.supported) {
var upload_source_index = sources.indexOf("upload");
if (upload_source_index != -1) {
sources.splice(upload_source_index,1);
}
}
}
var buildInterface = function() {
if (!skipFontAwesome) {
var font_awesome = $('<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" media="all" rel="stylesheet" type="text/css" />');
$("head").append(font_awesome);
}
// container and swf
container = $('<div id="'+dom_id+'" class="camera_tag"></div>');
container.css({width: width+"px", height: height+"px"})
container.attr("style", inline_styles);
container.addClass(className);
$(camera_el).replaceWith(container);
if (!mobile_browser) {
// create swf placeholder in container then embed the camera swf
container.append("<div id='"+dom_id+"_recorder_placeholder'></div>")
// create reocorder object (HTML5 or swf)
embed_recording_stack();
// communication to and from swf
setupExternalInterface();
}
// start screen
start_screen = $("#"+dom_id+"-start-screen").addClass("cameratag_screen");
if (start_screen.length == 0) {
start_screen = $('<div id="'+dom_id+'_start_screen" class="cameratag_screen cameratag_start"></div>');
var selection_prompt = $('<a class="cameratag_select_prompt">'+CT_i18n[4]+'</a>');
start_screen.append(selection_prompt);
if (sources.indexOf("record") != -1 && !mobile_enabled) {
var record_btn = $('<a class="cameratag_primary_link cameratag_record_link cameratag_record"><span class="cameratag_action_icon">&#61501;</span><br><span class="cameratag_prompt_label">'+CT_i18n[5]+'</span></a>');
start_screen.append(record_btn);
}
if (sources.indexOf("upload") != -1 && !mobile_enabled) {
var upload_btn = $('<a id="'+dom_id+'_upload_link" class="cameratag_primary_link cameratag_upload_link cameratag_upload"><span class="cameratag_action_icon">&#61678;</span><br><span class="cameratag_prompt_label">'+CT_i18n[6]+'</span></a>');
start_screen.append(upload_btn);
}
if (sources.indexOf("sms") != -1 || mobile_enabled) {
var sms_btn = $('<a class="cameratag_primary_link cameratag_sms_link"><span class="cameratag_action_icon">&#61707;</span><br><span class="cameratag_prompt_label">'+CT_i18n[7]+'</span></a>');
start_screen.append(sms_btn);
}
if (!mobile_enabled && !ct_webrtc) {
var settings_btn = $('<img class="cameratag_settings_btn" src="//cameratag.com/assets/gear.png">');
start_screen.append(settings_btn);
}
}
// add to DOM
container.append(start_screen);
// check position
$(start_screen).css("position", "absolute");
// error screen
error_screen = $("#"+dom_id+"-error-screen").addClass("cameratag_screen");
if (error_screen.length == 0) {
error_screen = $('<div class="cameratag_screen cameratag_error"></div>');
error_message = $('<div class="cameratag_error_message"></div>');
error_screen.append(error_message);
var settings_btn = $('<img class="cameratag_settings_btn" src="//cameratag.com/assets/gear.png">');
error_screen.append(settings_btn);
}
else {
error_message = $('.cameratag_error_message');
}
// add to DOM
container.append(error_screen);
// camera detection
camera_detection_screen = $("#"+dom_id+"-camera-detection-screen").addClass("cameratag_screen");
if (camera_detection_screen.length == 0) {
// legacy support for old typo
camera_detection_screen = $("#"+dom_id+"camera-detection-screen").addClass("cameratag_screen");
}
if (camera_detection_screen.length == 0) {
camera_detection_screen = $('<div class="cameratag_screen cameratag_detect"></div>');
var camera_detection_prompt = $('<div class="cameratag_prompt">'+CT_i18n[8]+'</div>');
camera_detection_screen.append(camera_detection_prompt);
}
// add to DOM
container.append(camera_detection_screen);
// countdown
countdown_screen = $("#"+dom_id+"-countdown-screen").addClass("cameratag_screen");
countdown_status = countdown_screen.find(".cameratag_countdown_status");
if (countdown_screen.length == 0) {
countdown_screen = $('<div class="cameratag_screen cameratag_count"></div>');
var countdown_prompt = $('<div class="cameratag_prompt">'+CT_i18n[9]+' </div>');
countdown_status = $('<div class="cameratag_countdown_status"></div>');
countdown_screen.append(countdown_status);
countdown_screen.append(countdown_prompt);
}
// add to DOM
container.append(countdown_screen);
// paused screen
paused_screen = $("#"+dom_id+"-pause-screen")
if (paused_screen.length == 0) {
paused_screen = $('<div class="cameratag_paused"></div>');
var paused_msg = $('<div class="cameratag_paused_message">'+CT_i18n[41]+'</div>')
}
// add to DOM
paused_screen.append(paused_msg);
container.append(paused_screen);
// upload
upload_screen = $("#"+dom_id+"-upload-screen").addClass("cameratag_screen");
upload_status = upload_screen.find(".cameratag_upload_status");
if (upload_screen.length == 0) {
upload_screen = $('<div class="cameratag_screen cameratag_upload"></div>');
var upload_prompt = $('<div class="cameratag_prompt">'+CT_i18n[10]+'</div>');
upload_status = $('<div class="cameratag_upload_status"></div>');
upload_screen.append(upload_status);
upload_screen.append(upload_prompt);
}
// add to DOM
container.append(upload_screen);
// record controls
recording_screen = $("#"+dom_id+"-recording-screen").addClass("cameratag_screen");
record_timer_prompt = recording_screen.find(".cameratag_record_timer_prompt");
if (recording_screen.length == 0) {
recording_screen = $('<div class="cameratag_screen cameratag_recording cameratag_stop_recording"></div>');
var stop_prompt = $('<div class="cameratag_prompt">'+CT_i18n[11]+'</div>');
record_timer_prompt = $('<span class="cameratag_record_timer_prompt">('+maxLength+')</span>');
var recording_indicator = $('<img src="//'+appServer+'/assets/recording.gif"/>');
stop_prompt.append(record_timer_prompt);
recording_screen.append(stop_prompt);
recording_screen.append(recording_indicator);
}
// add to DOM
container.append(recording_screen);
// play controls
playback_screen = $("#"+dom_id+"-playback-screen").addClass("cameratag_screen");
if (playback_screen.length == 0) {
playback_screen = $('<div class="cameratag_screen cameratag_playback cameratag_stop_playback"></div>');
var skip_prompt = $('<div class="cameratag_prompt">'+CT_i18n[12]+'</div>');
playback_screen.append(skip_prompt);
}
// add to DOM
container.append(playback_screen);
// accept controls
accept_screen = $("#"+dom_id+"-accept-screen").addClass("cameratag_screen");
if (accept_screen.length == 0) {
accept_screen = $('<div class="cameratag_screen cameratag_accept"></div>');
var accept_btn = $('<a class="cameratag_accept_btn cameratag_publish"><span class="button_label">&#10003; '+CT_i18n[13]+'</span></a>');
var rerecord_btn = $('<a class="cameratag_rerecord_btn cameratag_record"><span class="button_label">&#9851; '+CT_i18n[14]+'</span></a>');
var play_btn = $('<a class="cameratag_play_btn cameratag_play"><span class="button_label">&#8629; '+CT_i18n[15]+'</span></a>');
accept_screen.append(accept_btn);
accept_screen.append(rerecord_btn);
accept_screen.append(play_btn);
}
// add to DOM
container.append(accept_screen);
// wait screen
wait_screen = $("#"+dom_id+"-wait-screen").addClass("cameratag_screen");
wait_message = wait_screen.find(".cameratag_wait_message");
if (wait_screen.length == 0) {
wait_screen = $('<div class="cameratag_screen cameratag_wait"></div>');
var spinner = $('<div class="cameratag_spinner"><img src="//'+appServer+'/assets/loading.gif"/><br/><span class="cameratag_wait_message">'+CT_i18n[16]+'</span></div>');
wait_screen.append(spinner);
wait_message = wait_screen.find(".cameratag_wait_message");
}
// add to DOM
container.append(wait_screen);
// completed screen
completed_screen = $("#"+dom_id+"-completed-screen").addClass("cameratag_screen");
if (completed_screen.length == 0) {
completed_screen = $('<div class="cameratag_screen cameratag_completed"></div>');
thumb_bg = $('<div class="cameratag_thumb_bg"></div>');
var check_mrk = $('<div class="cameratag_checkmark"><span class="check">&#10004;</span> '+CT_i18n[17]+'</div>');
completed_screen.append(thumb_bg);
completed_screen.append(check_mrk);
}
// add to DOM
container.append(completed_screen);
// sms screen
sms_screen = $("#"+dom_id+"-sms-screen").addClass("cameratag_screen");
sms_input = sms_screen.find(".cameratag_sms_input");
if (sms_screen.length == 0) {
sms_screen = $('<div class="cameratag_screen cameratag_sms"></div>');
var sms_input_prompt = $('<div class="cameratag_sms_prompt">'+CT_i18n[18]+'<br/></div>');
sms_input = $('<input id="'+dom_id+'_sms_input" class="cameratag_sms_input" type="text"/>');
var sms_submit = $('<br/><a href="javascript:" class="cameratag_send_sms">'+CT_i18n[19]+'</a>&nbsp;&nbsp;<a href="javascript:" class="cameratag_goto_start">'+CT_i18n[20]+'</a>');
sms_input_prompt.append(sms_input);
sms_input_prompt.append(sms_submit);
sms_screen.append(sms_input_prompt);
$(sms_input).intlTelInput({
initialCountry: default_sms_country
});
}
// add to DOM
container.append(sms_screen);
// check phone screen
check_phone_screen = $("#"+dom_id+"-check-phone-screen").addClass("cameratag_screen");
if (check_phone_screen.length == 0) {
check_phone_screen = $('<div class="cameratag_screen cameratag_check_phone"><div class="cameratag_check_phone_prompt">'+CT_i18n[21]+'</div><div class="cameratag_check_phone_url">'+CT_i18n[22]+' http://'+appServer+'/api/v'+CameraTag.version+'/cameras/'+camera.uuid+'/videos/'+video.uuid+'/mobile_record</div></div>');
}
// add to DOM
container.append(check_phone_screen);
// load up the start screen
self.loadInterface(start_screen, true);
// hidden inputs
container.append("<input id='"+input_name+"_video_uuid' type='hidden' name='"+input_name+"[video_uuid]' value=''>");
$(camera.formats).each(function(index, format){
container.append("<input id='"+input_name+"_"+format.name+"_video' type='hidden' name='"+input_name+"["+format.name+"][video]' value=''>");
container.append("<input id='"+input_name+"_"+format.name+"_mp4' type='hidden' name='"+input_name+"["+format.name+"][mp4]' value=''>");
container.append("<input id='"+input_name+"_"+format.name+"_webm' type='hidden' name='"+input_name+"["+format.name+"][webm]' value=''>");
container.append("<input id='"+input_name+"_"+format.name+"_thumb' type='hidden' name='"+input_name+"["+format.name+"][thumb]' value=''>");
container.append("<input id='"+input_name+"_"+format.name+"_small_thumb' type='hidden' name='"+input_name+"["+format.name+"][small_thumb]' value=''>");
});
//
// SETUP ACTION CLASS OBSERVERS
//
// UPLOADING
if (mobile_enabled) {
create_uploader(start_screen);
}
else if (start_screen.find(".cameratag_upload").length > 0) {
create_uploader( start_screen.find(".cameratag_upload")[0] );
}
// CLICKING
if (!mobile_browser) {
container.find(".cameratag_record").click(function(){self.record()});
container.find(".cameratag_stop_recording").click(function(){self.stopRecording()});
container.find(".cameratag_stop_playback").click(function(){self.stopPlayback()});
container.find(".cameratag_play").click(function(){self.play()});
container.find(".cameratag_publish").click(function(){self.publish()});
container.find(".cameratag_goto_start").click(function(){self.loadInterface(start_screen, true);});
container.find(".cameratag_send_sms").click(function(){self.send_sms();});
container.find(".cameratag_sms_link").click(function(){self.loadInterface(sms_screen);});
container.find(".cameratag_settings_btn").click(function(e){
e.stopPropagation();
self.showFlashSettings();
});
}
};
var isMobile = function() {
if( navigator.userAgent.match(/Android/i)
|| navigator.userAgent.match(/webOS/i)
|| navigator.userAgent.match(/iPhone/i)
|| navigator.userAgent.match(/iPad/i)
|| navigator.userAgent.match(/iPod/i)
|| navigator.userAgent.match(/BlackBerry/i)
|| navigator.userAgent.match(/Windows Phone/i)
) {
return true;
}
else {
return false;
}
}
var mobileUploadSupported = function () {
// Handle devices which falsely report support
if (navigator.userAgent.match(/(Android (1.0|1.1|1.5|1.6|2.0|2.1))|(Windows Phone (OS 7|8.0))|(XBLWP)|(ZuneWP)|(w(eb)?OSBrowser)|(webOS)|(Kindle\/(1.0|2.0|2.5|3.0))/)) {
return false;
}
// Create test element
var el = document.createElement("input");
el.type = "file";
return !el.disabled;
}
var countdown = function(length, callback) {
if (paused) {
container.find(".cameratag_screen").hide();
setTimeout(function(){
countdown(length, callback);
}, 1000);
}
else {
self.loadInterface(countdown_screen);
if (countdown_counter >= length) {
countdown_counter = 0;
countdown_screen.hide();
callback();
CameraTag.fire(dom_id, "countdownFinished");
}
else {
countdown_status.html(length - countdown_counter);
countdown_counter += 1;
setTimeout(function(){
countdown(length, callback);
}, 1000);
}
}
};
self.loadInterface = function(state_container, alternatives, message) {
current_screen = state_container;
container.find(".cameratag_screen").hide();
if (state_container != "none") {
state_container.css('display','block');
}
};
var recordTimerTick = function() {
record_timer_count += 1;
var time_left = maxLength - record_timer_count;
record_timer_prompt.html( "(" + time_left + ")" );
if (time_left == 0) {
CameraTag.fire(dom_id, "recordingTimeOut");
clearInterval(record_timer);
self.stopRecording();
}
}
var upload_and_publish = function(camera_uuid, video_uuid, type, videoServer, signature, signature_expiration, video_data_object, video_name, video_description) {
if (type == "webrtc") {
// this will auto call publish_on_server on complete
uploadRTC();
}
else {
publish_on_server(camera_uuid, video_uuid, type, videoServer, signature, signature_expiration, video_data_object, video_name, video_description)
}
};
var throw_error = function(message) {
error_message.html(message);
self.loadInterface(error_screen, true);
};
self.publish = function() {
if (!readyToPublish) {
//sendStat("premature_publish");
throw("Camera not ready to publish. Please wait for the 'readyToPublish' event.");
return;
}
CameraTag.fire(dom_id, "publishing");
wait(CT_i18n[38]);
upload_and_publish(camera.uuid, video.uuid, publishType, videoServer, signature, signature_expiration, video_data_object, video_name, video_description);
}
var wait = function(message) {
message = message || "please wait";
wait_message.html(message);
self.loadInterface(wait_screen);
}
var populate_hidden_inputs = function() {
$("#"+input_name+"_video_uuid").val(video.uuid);
$(camera.formats).each(function(index, format){
// videos
var mp4_url = "//"+appServer+"/videos/"+video.uuid+"/"+format.name+"/mp4.mp4";
if (camera.create_webm) {
var webm_url = "//"+appServer+"/videos/"+video.uuid+"/"+format.name+"/webm.webm";
}
else {
var webm_url = "";
}
$("#"+input_name+"_"+format.name+"_video").val(mp4_url);
video.formats[format.name]["video_url"] = mp4_url;
$("#"+input_name+"_"+format.name+"_mp4").val(mp4_url);
video.formats[format.name]["mp4_url"] = mp4_url;
$("#"+input_name+"_"+format.name+"_webm").val(webm_url);
video.formats[format.name]["webm_url"] = webm_url;
// thumbnails
var thumb_url = "//"+appServer+"/videos/"+video.uuid+"/"+format.name+"/thumb.png";
$("#"+input_name+"_"+format.name+"_thumb").val(thumb_url);
video.formats[format.name]["thumb_url"] = thumb_url;
var small_thumb_url = "//"+appServer+"/videos/"+video.uuid+"/"+format.name+"/small_thumb.png";
$("#"+input_name+"_"+format.name+"_small_thumb").val(small_thumb_url);
video.formats[format.name]["small_thumb_url"] = small_thumb_url;
});
};
self.setVideoData = function(js_object) {
self.addVideoData(js_object);
};
self.addVideoData = function(js_object) {
if (typeof(js_object) != "object") {
throw("addVideoData only accepts Javascript Objects");
}
video_data_object = js_object;
var json_string = JSON.stringify(js_object);
$.ajax({
url: "//"+appServer+"/api/v"+CameraTag.version+"/cameras/"+camera.uuid+"/videos/"+video.uuid+"/form_data.json",
data:{form_data: json_string},
type:"post",
success: function(response) {
return true
},
error: function() {
throw_error(CT_i18n[35]);
//sendStat("video_data_error");
return false;
}
})
}
self.reset = function() {
// start with a clean slate
clearTimeout(processed_timer);
clearTimeout(published_timer);
clearInterval(record_timer);
countdown_counter = 0;
record_timer_count = 0;
uploading = false;
error_messages = [];
readyToPublish = false;
publishType = "webcam";
if (connected) {
self.disconnect();
}
// create new video
video_name = null;
video_description = null;
video_data_object = null;
video = {};
video.uuid = generateUUID();
video.formats = {};
if (camera.formats) {
$(camera.formats).each(function(index, format){
video.formats[format.name] = {};
})
}
// observe publishing
CameraTag.observe(video.uuid, "published", function(published_video) {
if (video.uuid == published_video.uuid) {
state = "published";
populate_hidden_inputs();
self.loadInterface(completed_screen);
if (connected) {
self.disconnect();
}
CameraTag.fire(dom_id, "published", video);
if (poll_processed) {
pollForProcessed();
}
}
}, true);
// failed publish
CameraTag.observe(video.uuid, "publishFailed", function(error) {
if (video.uuid == error.video_uuid) {
throw_error(CT_i18n[34] + error.message.error);
}
}, true);
if (!mobile_enabled) {
recorder.setUUID(video.uuid);
recorder.showNothing();
}
container.find("input").val("");
self.loadInterface(start_screen, true);
self.showRecorder();
CameraTag.fire(dom_id, "cameraReset");
}
self.setLength = function(new_length) {
maxLength = new_length;
record_timer_prompt.html( "(" + new_length + ")" );
}
// publicly accessable methods
self.send_sms = function() {
var country_code = $(sms_input).intlTelInput("getSelectedCountryData").dialCode;
var local_number = $(sms_input).val();
var complete_number = "+"+country_code+local_number;
if (local_number == "") {
alert(CT_i18n[25])
return;
}
wait(CT_i18n[24]);
// make sure video data has been added before we ditch
if (video_data_object) {
self.addVideoData(video_data_object);
}
$.ajax({
url: "//"+appServer+"/api/v"+CameraTag.version+"/cameras/"+camera.uuid+"/videos/"+video.uuid+"/sms",
data:{number: complete_number, message: CT_i18n[0]},
type:"post",
success: function(response) {
if (response.success) {
self.loadInterface(check_phone_screen);
CameraTag.fire(dom_id, "smsSent");
}
else {
self.loadInterface(sms_screen);
alert(CT_i18n[26]);
}
},
error: function() {
throw_error(CT_i18n[27]);
//sendStat("sms_error", {number: number, message: txt_message});
return false;
}
})
}
self.getVideo = function() {
return video;
};
self.restart_upload = function() {
self.uploader.restart_upload();
}
self.destroy = function() {
state = "disconnecting";
if (recorder && connected) {
recorder.disconnect();
}
delete CameraTag.cameras[dom_id];
container.remove();
}
var create_uploader = function(browse_element) {
var upload_source_index = sources.indexOf("upload");
if (upload_source_index == -1) {
var upload_input = $('<input id="'+dom_id+'_upload_file" style="position:absolute;" type="file" accept="video/mp4,video/m4v,video/x-flv,video/flv,video/wmv,video/mpg,video/quicktime,video/webm,video/x-ms-wmv,video/ogg,video/avi,video/mov,video/x-m4v,video/*" capture>')
} else {
var upload_input = $('<input id="'+dom_id+'_upload_file" style="position:absolute;" type="file" accept="video/mp4,video/m4v,video/x-flv,video/flv,video/wmv,video/mpg,video/quicktime,video/webm,video/x-ms-wmv,video/ogg,video/avi,video/mov,video/x-m4v,video/*">')
}
$(start_screen).append(upload_input);
$(upload_input).css({
left: $(browse_element).offset().left - $(browse_element).offsetParent().offset().left,
top: $(browse_element).offset().top - $(browse_element).offsetParent().offset().top,
width: $(browse_element).width(),
height: $(browse_element).height(),
opacity: 0
})
$(browse_element).css({
zIndex: 1
})
$(browse_element).click(function(e){
if (e.target != upload_input[0]) {
e.stopPropagation();
$(upload_input).click();
}
})
$(upload_input).change(function(evt){
files = evt.target.files;
// get file extension
var ext = files[0].name.split(".")
ext = ext[ext.length-1];
ext = ext.toLowerCase();
// dont permit invalid filetypes
if (permitted_extensions.indexOf(ext) == -1) {
if ( !confirm(CT_i18n[42]) ) {
return;
}
}
// state = "uploading";
// uploading = true; // andorid doesn't seem to get this set through the uploadStarted event?
CameraTag.fire(dom_id, "uploadStarted");
// upload_screen.show();
// // start_screen.hide();
// start_screen.css("left", "-10000px");
// start_screen.css("right", "10000px");
var isAndroid = navigator.userAgent.toLowerCase().indexOf("android") > -1; //&& ua.indexOf("mobile");
if (isAndroid) {
upload_status.html("...");
}
else {
upload_status.html("0%");
}
uploader.add({
name: 'recordings/' + video.uuid + '.flv',
file: files[0],
notSignedHeadersAtInitiate: {
'Cache-Control': 'max-age=3600'
},
xAmzHeadersAtInitiate : {
'x-amz-acl': 'public-read'
},
signParams: {},
complete: function(){
readyToPublish = true;
publishType = "upload";
CameraTag.fire(dom_id, "readyToPublish");
//sendStat("upload_success", {});
start_screen.css("left", "0px");
start_screen.css("right", "0px");
if (publishOnUpload) {
wait(CT_i18n[38]);
upload_and_publish(camera.uuid, video.uuid, "upload", videoServer, signature, signature_expiration, video_data_object, video_name, video_description); // publish without s3
}
else {
self.loadInterface(completed_screen);
}
},
progress: function(progress){
CameraTag.fire(dom_id, "UploadProgress", progress);
CameraTag.fire(dom_id, "uploadProgress", progress);
}
});
$(evt.target).val('');
});
}
var pollForProcessed = function() {
if (poll_processed) {
$.ajax({
url: "//"+appServer+"/api/v"+CameraTag.version+"/cameras/"+camera.uuid+"/videos/"+video.uuid+".json",
type:"get",
data: {
referer: window.location.toString()
},
success: function(response) {
if (response.formats && response.formats[0] && response.formats[0].state == "COMPLETED") {
CameraTag.fire(dom_id, "processed", response);
}
else if (response.formats && response.formats[0] && response.formats[0].state == "ERROR") {
CameraTag.fire(dom_id, "processingFailed", response);
}
else {
processed_timer = setTimeout(pollForProcessed, 2000);
}
},
error: function() {
//sendStat("processed_poll_error");
processed_timer = setTimeout(pollForProcessed, 2000);
}
})
}
}
var pollForPublished = function() {
$.ajax({
url: "//"+appServer+"/api/v"+CameraTag.version+"/cameras/"+camera.uuid+"/videos/"+video.uuid+".json",
type:"get",
data: {
referer: window.location.toString()
},
success: function(response) {
if (response.state == "published" || response.state == "processed") {
CameraTag.fire(video.uuid, "published", response);
clearInterval(published_timer);
}
else {
published_timer = setTimeout(function(){pollForPublished()}, 4000);
}
},
error: function() {
//sendStat("published_poll_error");
published_timer = setTimeout(function(){pollForPublished()}, 4000);
}
})
}
var uploadRTC = function() {
self.loadInterface(upload_screen);
recorder.uploadRTC();
}
// these methods require the swf to be in existance and are created after it's available
var setupExternalInterface = function() {
// communication to swf
self.play = function() {
if (connected) {
recorder.startPlayback();
}
};
self.showFlashSettings = function() {
self.loadInterface("none");
recorder.showFlashSettings();
}
self.record = function() { // actually calls countdown which will call record_without_countdown in callback
if (connected) {
CameraTag.fire(dom_id, "countdownStarted");
recorder.showRecorder();
countdown(preRollLength, self.record_without_countdown);
}
else {
self.loadInterface("none");
self.connect();
}
};
self.showRecorder = function() {
recorder.showRecorder();
}
self.showPlayer = function() {
recorder.showPlayer();
}
self.record_without_countdown = function() {
if (!readyToRecord) {
//sendStat("premature_record");
throw("Camera not ready to record. Please observe 'readyToRecord' event before recording");
return;
}
state = "recording";
recorder.showRecorder();
recorder.startRecording()
};
self.stopPlayback = function() {
if (connected) {
recorder.stopPlayback();
}
};
self.stopRecording = function() {
clearInterval(record_timer);
if (connected) {
recorder.stopRecording();
}
};
self.connect = function() {
recorder.connect();
};
self.disconnect = function() {
state = "disconnecting";
recorder.disconnect();
};
}
var setupEventObservers = function() {
// communication from swf
CameraTag.observe(dom_id, "initialized", function() {
initialized = true;
state = "initialized";
}, true);
CameraTag.observe(dom_id, "paused", function() {
paused_timer = setTimeout(function(){
paused = true;
container.find(".cameratag_screen").hide();
paused_screen.show();
}, 1500);
}, true);
CameraTag.observe(dom_id, "unpaused", function() {
clearTimeout(paused_timer);
if (paused) {
paused = false;
paused_screen.hide();
self.loadInterface(current_screen);
}
}, true);
CameraTag.observe(dom_id, "connecting", function() {
wait(CT_i18n[39])
}, true);
CameraTag.observe(dom_id, "connecting2", function() {
wait(CT_i18n[40])
}, true);
CameraTag.observe(dom_id, "securityDialogOpen", function() {
self.loadInterface("none");
}, true);
CameraTag.observe(dom_id, "securityDialogClosed", function() {
self.loadInterface(camera_detection_screen);
}, true);
CameraTag.observe(dom_id, "settingsDialogClosed", function() {
self.loadInterface(start_screen, true);
}, true);
CameraTag.observe(dom_id, "detectingCamera", function() {
self.loadInterface(camera_detection_screen);
}, true);
CameraTag.observe(dom_id, "noCamera", function() {
throw_error(CT_i18n[28]);
}, true);
CameraTag.observe(dom_id, "noMic", function() {
throw_error(CT_i18n[29]);
}, true);
CameraTag.observe(dom_id, "hardwareError", function() {
throw_error(CT_i18n[43]);
}, true);
CameraTag.observe(dom_id, "readyToRecord", function() {
readyToRecord = true;
if (recordOnConnect) {
self.record(); //starts countdown
} else {
self.loadInterface("none");
}
}, true);
CameraTag.observe(dom_id, "cameraDenied", function() {
throw_error(CT_i18n[30]);
}, true);
CameraTag.observe(dom_id, "serverConnected", function() {
connected = true;
}, true);
CameraTag.observe(dom_id, "serverDisconnected", function() {
connected = false;
readyToRecord = false;
if (state != "disconnecting" && state != "published" && state != "readyToPublish"){
self.stopRecording();
recorder.showNothing();
throw_error(CT_i18n[31]);
setTimeout(function(){
self.loadInterface(start_screen);
}, 2000);
//sendStat("pre_record_disconnect_error");
}
}, true);
CameraTag.observe(dom_id, "playbackFailed", function() {
throw_error(CT_i18n[32]);
//sendStat("playback_error");
}, true);
CameraTag.observe(dom_id, "serverError", function() {
throw_error(CT_i18n[33]);
//sendStat("no_server_error");
}, true);
CameraTag.observe(dom_id, "waitingForCameraActivity", function() {
}, true);
CameraTag.observe(dom_id, "countdownStarted", function() {
self.loadInterface(countdown_screen);
}, true);
CameraTag.observe(dom_id, "countdownFinished", function() {
}, true);
CameraTag.observe(dom_id, "recordingStarted", function() {
record_timer_count = 0;
record_timer = setInterval(function(){ recordTimerTick() }, 1000);
self.loadInterface(recording_screen);
}, true);
CameraTag.observe(dom_id, "recordingStopped", function() {
clearInterval(record_timer);
if (skipPreview) {
recorder.showPlayer();
self.loadInterface(accept_screen);
}
else {
self.play();
}
}, true);
CameraTag.observe(dom_id, "bufferingDown", function() {
wait(CT_i18n[37]);
}, true);
CameraTag.observe(dom_id, "recordingTimeOut", function() {
}, true);
CameraTag.observe(dom_id, "playbackStarted", function() {
self.loadInterface(playback_screen);
}, true);
CameraTag.observe(dom_id, "playbackStopped", function() {
self.loadInterface(accept_screen);
}, true);
CameraTag.observe(dom_id, "publishing", function() {
state = "publishing";
}, true);
CameraTag.observe(dom_id, "uploadStarted", function() {
state = "uploading";
uploading = true;
self.loadInterface(upload_screen);
}, true);
CameraTag.observe(dom_id, "uploadProgress", function(progress) {
upload_status.html((progress * 100).toFixed(1) + "%");
}, true);
CameraTag.observe(dom_id, "uploadAborted", function() {
uploading = false;
}, true);
CameraTag.observe(dom_id, "readyToPublish", function() {
readyToPublish = true;
state = "readyToPublish";
}, true);
CameraTag.observe(dom_id, "smsSent", function() {
pollForPublished();
}, true);
CameraTag.observe(dom_id, "published", function() {
}, true);
CameraTag.observe(dom_id, "publishFailed", function(data) {
throw_error(CT_i18n[34]);
//sendStat("publish_error", data);
}, true);
CameraTag.observe(dom_id, "processed", function() {
state = "processed";
$(container).find(".cameratag_thumb_bg").css({backgroundImage: "url(//"+appServer+"/videos/"+video.uuid+"/"+camera.formats[0].name+"/thumb.png)"});
}, true);
}
var WebRTCRecorder = function(){
var self = this;
var recordVideo, recordAudio;
var audioPreview = new Audio();
var videoPreview = $('<video id="'+dom_id+'-video-preview"></video>');
container.append(videoPreview);
var videoPreview = videoPreview[0];
videoPreview.style.width = $(container).width()+"px";
videoPreview.style.height = $(container).height()+"px";
if (flipRecordPreview) {
videoPreview.style.transform = "scaleX(-1)";
}
var wrtc_stream;
self.setUUID = function(new_uuid) {
// this will always use the video.uuid so do nothing
};
self.showNothing = function() {
videoPreview.src = null;
};
self.showRecorder = function() {
videoPreview.src = window.URL.createObjectURL(wrtc_stream);
videoPreview.play();
};
self.showPlayer = function() {
videoPreview.style.width = $(container).width()+"px";
videoPreview.style.height = $(container).height()+"px";
videoPreview.src = recordVideo.toURL();
};
self.stopPlayback = function() {
videoPreview.pause();
audioPreview.src = null;
CameraTag.fire(dom_id, "playbackStopped");
};
self.startPlayback = function() {
audioPreview.src = recordAudio.toURL();
audioPreview.controls = true;
audioPreview.autoplay = true;
audioPreview.style.visibility = "hidden";
audioPreview.onloadedmetadata = function() {
videoPreview.src = recordVideo.toURL();
videoPreview.play();
CameraTag.fire(dom_id, "playbackStarted");
};
$(videoPreview).bind("ended", function() {
CameraTag.fire(dom_id, "playbackStopped", {});
});
videoPreview.parentNode.appendChild(audioPreview);
if(audioPreview.paused) audioPreview.play();
};
self.connect = function() {
publishType = "webrtc";
var aspect_ratio = hResolution / vResolution;
navigator.getUserMedia({
audio: true,
video: {
width: hResolution,
height: vResolution,
aspectRatio: aspect_ratio,
optional: [
{minAspectRatio: aspect_ratio},
{maxAspectRatio: aspect_ratio},
{minWidth: hResolution},
{minHeight: vResolution},
{maxWidth: hResolution},
{maxHeight: vResolution}
]
}
}, function(stream) {
wrtc_stream = stream;
videoPreview.src = window.URL.createObjectURL(wrtc_stream);
videoPreview.play();
videoPreview.muted = true;
recordAudio = RecordRTC(wrtc_stream, {
type: 'audio',
numberOfAudioChannels: 2,
recorderType: StereoAudioRecorder,
bufferSize: 4096
// sampleRate: 44100
});
// todo: Firefox supports MediaRecorder API
// however it will record both audio/video tracks into single WebM
// Need to construct a MediaStream that is having no audio tracks
recordVideo = RecordRTC(wrtc_stream, {
type: "video",
video: {
width: hResolution,
height: vResolution
},
canvas: {
width: hResolution,
height: vResolution
},
quality: 10,
videoBitsPerSecond: videoBitRate,
frameInterval: 41
});
CameraTag.fire(dom_id, "serverConnected");
CameraTag.fire(dom_id, "readyToRecord");
}, function(error) {
CameraTag.fire(dom_id, "cameraDenied");
});
};
self.disconnect = function() {
wrtc_stream.stop();
};
self.startRecording = function() {
recordVideo.initRecorder(function() {
recordAudio.initRecorder(function() {
recordVideo.startRecording();
recordAudio.startRecording();
});
});
CameraTag.fire(dom_id, "recordingStarted");
};
self.stopRecording = function() {
wait("please wait");
recordAudio.stopRecording(function() {
recordVideo.stopRecording(function() {
CameraTag.fire(dom_id, "recordingStopped");
CameraTag.fire(dom_id, "readyToPublish");
});
});
//wrtc_stream.stop();
};
self.uploadRTC = function() {
// state = "uploading";
// uploading = true; // andorid doesn't seem to get this set through the uploadStarted event?
CameraTag.fire(dom_id, "uploadStarted");
// start_screen.css("left", "-10000px");
// start_screen.css("right", "10000px");
uploader.add({
name: 'recordings/' + video.uuid + '.wav',
file: recordAudio.blob,
notSignedHeadersAtInitiate: {
'Cache-Control': 'max-age=3600'
},
xAmzHeadersAtInitiate : {
'x-amz-acl': 'public-read'
},
signParams: {},
complete: function(){
uploader.add({
name: 'recordings/' + video.uuid + '.webm',
file: recordVideo.blob,
notSignedHeadersAtInitiate: {
'Cache-Control': 'max-age=3600'
},
xAmzHeadersAtInitiate : {
'x-amz-acl': 'public-read'
},
signParams: {},
complete: function(){
readyToPublish = true;
publishType = "webrtc";
//sendStat("upload_success", {});
start_screen.css("left", "0px");
start_screen.css("right", "0px");
wait(CT_i18n[38]);
// call p_o_s directly so we dont upload again
publish_on_server(camera.uuid, video.uuid, "webrtc", videoServer, signature, signature_expiration, video_data_object, video_name, video_description); // publish without s3
},
progress: function(progress){
progress = (progress * .5) + .5;
CameraTag.fire(dom_id, "UploadProgress", progress);
// legacy capitalization support
CameraTag.fire(dom_id, "uploadProgress", progress);
}
});
},
progress: function(progress){
progress = progress * .5;
CameraTag.fire(dom_id, "UploadProgress", progress);
// legacy capitalization support
CameraTag.fire(dom_id, "uploadProgress", progress);
}
});
// var filename = parseInt(Math.random() * 10000000);
// var formData = new FormData();
// formData.append('filename', filename);
// formData.append('audio-blob', recordAudio.blob);
// formData.append('video-blob', recordVideo.blob);
// var request = new XMLHttpRequest();
// request.onreadystatechange = function() {
// if (request.readyState == 4 && request.status == 200) {
// alert("success!");
// }
// };
// request.onprogress = function(e) {
// debugger;
// if (e.lengthComputable) {
// var percentComplete = e.loaded / e.total;
// console.log(percentComplete);
// } else {
// // Unable to compute progress information since the total size is unknown
// }
// };
// request.open("POST", "/v7/videos/webrtc");
// request.send(formData);
}
}
// end of WebRTCRecorder
// setup for CameraTagRecorder
setup();
}
//
// Player
//
CameraTagPlayer = function(video_el) {
var cachebuster = parseInt( Math.random() * 100000000 );
var video_el = $(video_el);
var new_video_tag;
var jwplayerInjected = false;
var uuids;
var self = this;
var dom_id = video_el.attr("id") || cachebuster;
var signature = $(video_el).attr("data-signature");
var signature_expiration = $(video_el).attr("data-signature-expiration");
var height;
var width;
var player_id;
var jw_player_instance;
var user_options = {};
var playlist;
var flv_failover;
var setup = function(){
// make sure videojs is ready
if (!jwplayerReady()) {
setTimeout(setup, 30);
return;
};
// get uuids array
if (video_el.attr("data-uuid") != "" && video_el.attr("data-uuid")[0] == "[") {
uuids = eval(video_el.attr("data-uuid"));
} else if (video_el.attr("data-uuid") != "") {
uuids = [ video_el.attr("data-uuid") ]
} else {
alert("no video uuids found")
}
// parse any options that were passed in
if (video_el.attr("data-options")) {
user_options = JSON.parse( video_el.attr("data-options") );
}
else {
user_options = {}
}
// build playlist
build_playlist_array(uuids, user_options.image);
}
var build_playlist_array = function(uuids, preview_url) {
playlist = []
$(uuids).each(function(index, uuid){
$.ajax({
url: "//"+appServer+"/api/v"+CameraTag.version+"/videos/"+uuid+".json?signature="+signature+"&signature_expiration="+signature_expiration,
type:"get",
data: {
referer: window.location.toString()
},
success: function(video) {
var format = find_format_by_name( video, video_el.attr("data-format") ) || video.formats[0];
var source;
if (format) {
// determine which source to use based on availability
if (format.state == "COMPLETED") {
source = format.mp4_url;
flv_failover = false;
}
else if (format.flv_url) {
source = format.flv_url;
flv_failover = true;
}
else {
// no source available
flv_failover = false;
return;
}
playlist.push({
image: (preview_url || format.thumbnail_url),
sources: [
{ file: source }
],
height: format.height,
width: format.width,
uuid: video.uuid
})
if (playlist.length == uuids.length) {
init_jwplayer();
}
}
else {
playlist.push({
image: "https://cameratag.com/videos/v-4f03e790-f640-0131-cc78-12313914f10b/720p/thumb.png",
sources: [
{ file: "https://cameratag.com/videos/v-4f03e790-f640-0131-cc78-12313914f10b/720p/mp4.mp4" }
],
uuid: video.uuid
})
if (playlist.length == uuids.length) {
init_jwplayer();
}
}
},
error: function() {
playlist.push({
image: "https://cameratag.com/videos/v-4f03e790-f640-0131-cc78-12313914f10b/720p/small_thumb.png",
sources: [
{ file: "https://cameratag.com/videos/v-4f03e790-f640-0131-cc78-12313914f10b/720p/mp4.mp4" }
],
uuid: "v-4f03e790-f640-0131-cc78-12313914f10b"
})
if (playlist.length == uuids.length) {
init_jwplayer();
}
}
});
});
}
var find_format_by_name = function(video, name) {
var result = $.grep(video.formats, function(e){ return e.name == name; });
return result[0];
}
var init_jwplayer = function() {
player_id = video_el.attr("id");
jwplayer.key="ziVL9s0pxpESKa4mUW9KADbRrkb59LC2qGEI9Q==";
var default_options = {
skin: "glow",
abouttext: "powered by CameraTag",
aboutlink: "http://www.cameratag.com",
playlist: playlist,
primary: "flash"
}
if (flv_failover) {
default_options["stretching"] = "exactfit";
}
if (playlist.length > 1) {
default_options.listbar = {
position: "right",
size: 120
}
}
combined_options = $.extend({}, default_options, user_options);
jw_player_instance = jwplayer(player_id).setup( combined_options );
CameraTag.players[player_id] = jw_player_instance;
setup_player_events();
}
var setup_player_events = function() {
jw_player_instance.onReady(function(){
CameraTag.fire(player_id, "ready", {});
})
jw_player_instance.onSetupError(function(fallback, message){
CameraTag.fire(player_id, "setupError", {fallback: fallback, message: message});
})
jw_player_instance.onPlaylist(function(playlist){
CameraTag.fire(player_id, "playlist", {playlist: playlist});
})
jw_player_instance.onPlaylistItem(function(index, playlist){
CameraTag.fire(player_id, "playlistItem", {index: index, playlist: playlist});
})
jw_player_instance.onPlaylistComplete(function(){
CameraTag.fire(player_id, "playlistComplete", {});
})
jw_player_instance.onBufferChange(function(buffer){
CameraTag.fire(player_id, "bufferChange", {buffer: buffer});
})
jw_player_instance.onPlay(function(){
CameraTag.fire(player_id, "play", {});
if (allow_play_count) {
$.ajax({
url: "//"+appServer+"/api/v"+CameraTag.version+"/cameras/"+camera.uuid+"/videos/"+jw_player_instance.getPlaylistItem().uuid+"/play_count",
type:"post",
success: function() {
// no need
}
})
// hack for double callbacks
allow_play_count = false;
setTimeout(function(){
allow_play_count = true;
}, 3000)
}
})
jw_player_instance.onPause(function(oldstate){
CameraTag.fire(player_id, "pause", {oldstate: oldstate});
})
jw_player_instance.onBuffer(function(){
CameraTag.fire(player_id, "buffer", {});
})
jw_player_instance.onIdle(function(){
CameraTag.fire(player_id, "idle", {});
})
jw_player_instance.onComplete(function(){
CameraTag.fire(player_id, "complete", {});
})
jw_player_instance.onError(function(message){
CameraTag.fire(player_id, "error", {message: message});
})
jw_player_instance.onSeek(function(position, offset){
CameraTag.fire(player_id, "seek", {position: position, offset: offset});
})
jw_player_instance.onTime(function(duration, position){
CameraTag.fire(player_id, "time", {duration: duration, position: position});
})
jw_player_instance.onMute(function(muted){
CameraTag.fire(player_id, "mute", {muted: muted});
})
jw_player_instance.onVolume(function(volume){
CameraTag.fire(player_id, "volume", {volume: volume});
})
jw_player_instance.onFullscreen(function(fullscreen){
CameraTag.fire(player_id, "fullscreen", {fullscreen: fullscreen});
})
jw_player_instance.onResize(function(width, height){
CameraTag.fire(player_id, "resize", {width: width, height: height});
})
jw_player_instance.onQualityLevels(function(levels){
CameraTag.fire(player_id, "levels", {levels: levels});
})
jw_player_instance.onQualityChange(function(currentQuality){
CameraTag.fire(player_id, "qualityChange", {currentQuality: currentQuality});
})
jw_player_instance.onCaptionsList(function(tracks){
CameraTag.fire(player_id, "captionsList", {tracks: tracks});
})
jw_player_instance.onCaptionsChange(function(track){
CameraTag.fire(player_id, "captionsChange", {track: track});
})
jw_player_instance.onControls(function(controls){
CameraTag.fire(player_id, "controls", {controls: controls});
})
jw_player_instance.onDisplayClick(function(){
CameraTag.fire(player_id, "displayClick", {});
})
jw_player_instance.onAdClick(function(tag){
CameraTag.fire(player_id, "adClick", {tag: tag});
})
jw_player_instance.onAdCompanions(function(tag, companions){
CameraTag.fire(player_id, "adCompanions", {tag: tag, companions: companions});
})
jw_player_instance.onAdComplete(function(tag){
CameraTag.fire(player_id, "adComplete", {tag: tag});
})
jw_player_instance.onAdSkipped(function(tag){
CameraTag.fire(player_id, "adSkipped", {tag: tag});
})
jw_player_instance.onAdError(function(tag, message){
CameraTag.fire(player_id, "adError", {tag: tag, message: message});
})
jw_player_instance.onAdImpression(function(tag){
CameraTag.fire(player_id, "adImpression", {tag: tag});
})
jw_player_instance.onAdTime(function(tag, position, duration){
CameraTag.fire(player_id, "adTime", {tag: tag, position: position, duration: duration});
})
jw_player_instance.onBeforePlay(function(){
CameraTag.fire(player_id, "beforePlay", {});
})
jw_player_instance.onBeforeComplete(function(){
CameraTag.fire(player_id, "beforeComplete", {});
})
jw_player_instance.onMeta(function(metadata){
CameraTag.fire(player_id, "metadata", {metadata: metadata});
})
}
setup();
}
// end of CameraTagPlayer
/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a