diff --git a/changelog.html b/changelog.html index 8fc04e55..be1ff263 100644 --- a/changelog.html +++ b/changelog.html @@ -1,7 +1,21 @@ +
3.5.342
+ + +
3.5.341
+ +
3.5.340
3.5.339
diff --git a/gulpfile.js b/gulpfile.js index 8e407e98..a6a4b08c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -118,15 +118,17 @@ gulp.task('default', ['styles', 'scripts']); // Deploy gulp.task('minify_script', ['scripts'], function() { - return gulp.src(['script.js']) + return gulp.src(['*.js', '!*.min.js', '!gulpfile.js']) .pipe(uglify()) - .pipe(rename('script.min.js')) + .pipe(rename(function(path) { + path.basename += '.min'; + })) .pipe(gulp.dest(__dirname)) .on('error', util.log); }); gulp.task('minify_style', function() { - return gulp.src(['style.css', 'style-clips.css', 'dark.css', 'dark-clips.css']) + return gulp.src(['*.css', '!*.min.css']) .pipe(cleanCSS()) .pipe(rename(function(path) { path.basename += '.min'; diff --git a/src/badges.js b/src/badges.js index a4d018ac..d6c4022f 100644 --- a/src/badges.js +++ b/src/badges.js @@ -270,6 +270,7 @@ if ( constants.IS_WEBKIT ) // -------------------- FFZ.prototype.setup_badges = function() { + this.log("Preparing badge system."); if ( ! this.has_bttv ) { var val = this.settings.transparent_badges; this.toggle_style('badges-rounded', val === 1); @@ -287,14 +288,16 @@ FFZ.prototype.setup_badges = function() { this.toggle_style('badges-legacy-mod', this.settings.legacy_badges !== 0); this.toggle_style('badges-legacy-turbo', this.settings.legacy_badges > 1); - this.log("Preparing badge system."); - this.badges = {}; - this.log("Creating badge style element."); var s = this._badge_style = document.createElement('style'); s.id = "ffz-badge-css"; document.head.appendChild(s); + this.log("Generating CSS for existing API badges."); + for(var badge_id in this.badges) + if ( this.badges.hasOwnProperty(badge_id) ) + utils.update_css(s, badge_id, utils.badge_css(this.badges[badge_id])); + this.log("Generating CSS for existing Twitch badges."); for(var badge_id in CSS_BADGES) { var badge_data = CSS_BADGES[badge_id], diff --git a/src/ember/room.js b/src/ember/room.js index f6c2b79d..21e01879 100644 --- a/src/ember/room.js +++ b/src/ember/room.js @@ -69,8 +69,6 @@ try { // -------------------- FFZ.prototype.setup_room = function() { - this.rooms = {}; - this.log("Creating room style element."); var f = this, s = this._room_style = document.createElement("style"); @@ -946,6 +944,9 @@ FFZ.prototype.add_room = function(id, room) { this.load_room(id); // Announce this room to any extension callback functions. + this.api_trigger('room-add', id); + + // Legacy announcement. for(var api_id in this._apis) { var api = this._apis[api_id]; api._room_callbacks(id, data); @@ -978,6 +979,8 @@ FFZ.prototype.remove_room = function(id) { if ( ! this.global_sets.contains(room.set) && ! set.users.length ) this.unload_set(room.set); } + + this.api_trigger('room-remove', id); } @@ -1072,6 +1075,9 @@ FFZ.prototype._modify_room = function(room) { mru_list: [], ffzUpdateBadges: function() { + if ( this.get('isGroupRoom') ) + return; + var room_name = this.get('id'), room_id = this.get('roomProperties._id'), ffz_room = f.rooms && f.rooms[room_name]; @@ -1950,10 +1956,7 @@ FFZ.prototype._modify_room = function(room) { // Message Filtering - var i = f._chat_filters.length; - while(i--) - if ( f._chat_filters[i](msg) === false ) - return; + f.api_trigger('room-message', msg); // Also update chatters. @@ -1988,10 +1991,6 @@ FFZ.prototype._modify_room = function(room) { //return this._super(msg); }, - ffzChatFilters: function(msg) { - var i = f._chat_filters.length; - }, - setHostMode: function(e) { this.set('ffz_host_target', e && e.hostTarget || null); var user = f.get_user(); diff --git a/src/emoticons.js b/src/emoticons.js index 0d0527a0..94771122 100644 --- a/src/emoticons.js +++ b/src/emoticons.js @@ -19,23 +19,23 @@ var FFZ = window.FrankerFaceZ, FFZ.prototype.setup_emoticons = function() { this.log("Preparing emoticon system."); - this.emoji_data = {}; - this.emoji_names = {}; - - this.emote_sets = {}; - this.global_sets = []; - this.default_sets = []; - this._last_emote_id = 0; - // Usage Data this.emote_usage = {}; - this.log("Creating emoticon style element."); var s = this._emote_style = document.createElement('style'); s.id = "ffz-emoticon-css"; document.head.appendChild(s); + this.log("Generating CSS for existing API emoticon sets."); + for(var set_id in this.emote_sets) { + var es = this.emote_sets[set_id]; + if ( es && es.pending_css ) { + utils.update_css(s, set_id, es.pending_css + (es.css || '')); + es.pending_css = null; + } + } + this.log("Loading global emote sets."); this.load_global_sets(); @@ -44,27 +44,6 @@ FFZ.prototype.setup_emoticons = function() { this.log("Watching Twitch emoticon parser to ensure it loads."); this._twitch_emote_check = setTimeout(this.check_twitch_emotes.bind(this), 10000); - - - if ( this._apis ) { - for(var api_id in this._apis) { - var api = this._apis[api_id]; - for(var es_id in api.emote_sets) - this.emote_sets[es_id] = api.emote_sets[es_id]; - - for(var i=0; i < api.global_sets.length; i++) { - var es_id = api.global_sets[i]; - if ( this.global_sets.indexOf(es_id) === -1 ) - this.global_sets.push(es_id); - } - - for(var i=0; i < api.default_sets.length; i++) { - var es_id = api.default_sets[i]; - if ( this.default_sets.indexOf(es_id) === -1 ) - this.default_sets.push(es_id); - } - } - } } @@ -282,24 +261,36 @@ FFZ.prototype.load_global_sets = function(callback, tries) { var f = this; jQuery.getJSON(constants.API_SERVER + "v1/set/global") .done(function(data) { - f.default_sets = data.default_sets; - var gs = f.global_sets = [], + // Apply default sets. + var ds = f.default_sets, + gs = f.global_sets, sets = data.sets || {}; - if ( f.feature_friday && f.feature_friday.set ) { - if ( f.global_sets.indexOf(f.feature_friday.set) === -1 ) - f.global_sets.push(f.feature_friday.set); - if ( f.default_sets.indexOf(f.feature_friday.set) === -1 ) - f.default_sets.push(f.feature_friday.set); + // Remove non-API sets from default and global sets. + for(var i=ds.length; i--; ) { + var set_id = ds[i]; + if ( data.default_sets.indexOf(set_id) === -1 && (! f.emote_sets[set_id] || ! f.emote_sets[set_id].source_ext) ) + ds.splice(i, 1); } - for(var key in sets) { - if ( ! sets.hasOwnProperty(key) ) - continue; + for(var i=0; i < data.default_sets.length; i++) { + var set_id = data.default_sets[i]; + if ( ds.indexOf(set_id) === -1 ) + ds.push(set_id); + } - var set = sets[key]; - gs.push(key); - f._load_set_json(key, undefined, set); + for(var i=gs.length; i--; ) { + var set_id = gs[i]; + if ( ! sets[set_id] && (! f.emote_sets[set_id] || ! f.emote_sets[set_id].source_ext) ) + gs.splice(i, 1); + } + + for(var set_id in sets) { + var set = sets[set_id]; + if ( gs.indexOf(set_id) === -1 ) + gs.push(set_id); + + f._load_set_json(set_id, undefined, set); } f._load_set_users(data.users); diff --git a/src/ext/api.js b/src/ext/api.js index c086b972..7ad33dbb 100644 --- a/src/ext/api.js +++ b/src/ext/api.js @@ -61,6 +61,7 @@ var API = FFZ.API = function(instance, name, icon, version, name_key) { } } + this._events = {}; this.ffz._apis[this.id] = this; @@ -71,7 +72,6 @@ var API = FFZ.API = function(instance, name, icon, version, name_key) { this.badges = {}; this.users = {}; - this.chat_filters = []; this.on_room_callbacks = []; this.name = name || ("Extension#" + this.id); @@ -110,6 +110,47 @@ API.prototype.error = function(msg, error, to_json, log_json) { } +// --------------------- +// Events +// --------------------- + +API.prototype.on = function(event, func) { + var e = this._events[event] = this._events[event] || []; + if ( e.indexOf(func) === -1 ) + e.push(func); +} + + +API.prototype.off = function(event, func) { + if ( func === undefined ) + this._events[event] = []; + else { + var e = this._events[event] = this._events[event] || [], + ind = e.indexOf(func); + if ( ind !== -1 ) + e.splice(ind, 1); + } +} + + +var slice = Array.prototype.slice; + +API.prototype.trigger = function(event /*, args... */) { + var e = this._events[event]; + if ( e && e.length ) + for(var i=0; i < e.length; i++) + e[i].apply(this, slice.call(arguments, 1)); +} + + +FFZ.prototype.api_trigger = function(/*event, args...*/) { + for(var api_id in this._apis) { + var api = this._apis[api_id]; + api.trigger.apply(api, arguments); + } +} + + // --------------------- // Set Loading // --------------------- @@ -210,7 +251,10 @@ API.prototype._load_set = function(real_id, set_id, data) { } // Use the real ID for building CSS. - utils.update_css(this.ffz._emote_style, real_id, output_css + (emote_set.css || "")); + if ( this.ffz._emote_style ) + utils.update_css(this.ffz._emote_style, real_id, output_css + (emote_set.css || "")); + else + emote_set.pending_css = output_css; if ( this.ffz._cindex ) this.ffz._cindex.ffzFixTitle(); @@ -249,15 +293,14 @@ API.prototype.unload_set = function(set_id) { // First, let's unregister it as a global. this.unregister_global_set(set_id); - // Now, remove the set data. - utils.update_css(this.ffz._emote_style, exact_id, null); + if ( this.ffz._emote_style ) + utils.update_css(this.ffz._emote_style, exact_id, null); this.emote_sets[set_id] = undefined; if ( this.ffz.emote_sets ) this.ffz.emote_sets[exact_id] = undefined; - // Remove from all its Rooms if ( emote_set.users ) { for(var i=0; i < emote_set.users.length; i++) { @@ -303,7 +346,7 @@ API.prototype.register_global_set = function(set_id, emote_set) { // Make sure the set is still available with FFZ. - if ( ! this.ffz.emote_sets[exact_id] ) + if ( this.ffz.emote_sets && ! this.ffz.emote_sets[exact_id] ) this.ffz.emote_sets[exact_id] = emote_set; @@ -440,15 +483,25 @@ API.prototype.add_badge = function(badge_id, badge) { replaces_type: badge.replaces_type }; - this.ffz.badges[exact_id] = this.badges[badge_id] = real_badge; - utils.update_css(this.ffz._badge_style, exact_id, utils.badge_css(real_badge)); + this.badges[badge_id] = real_badge; + + if ( this.ffz.badges ) + this.ffz.badges[exact_id] = real_badge; + + if ( this.ffz._badge_style ) + utils.update_css(this.ffz._badge_style, exact_id, utils.badge_css(real_badge)); } API.prototype.remove_badge = function(badge_id) { var exact_id = this.id + '-' + badge_id; - this.ffz.badges[exact_id] = this.badges[badge_id] = undefined; - utils.update_css(this.ffz._badge_style, exact_id); + this.badges[badge_id] = undefined; + + if ( this.ffz.badges ) + this.ffz.badges[exact_id] = undefined; + + if ( this.ffz._badge_style ) + utils.update_css(this.ffz._badge_style, exact_id); } @@ -532,24 +585,25 @@ API.prototype.user_remove_set = function(username, set_id) { // ----------------------- API.prototype.register_chat_filter = function(filter) { - this.chat_filters.push(filter); - this.ffz._chat_filters.push(filter); + this.on('room-message', filter); } API.prototype.unregister_chat_filter = function(filter) { - var ind = this.chat_filters.indexOf(filter); - if ( ind !== -1 ) - this.chat_filters.splice(ind, 1); - - ind = this.ffz._chat_filters.indexOf(filter); - if ( ind !== -1 ) - this.ffz._chat_filters.splice(ind, 1); + this.off('room-message', filter); } // ----------------------- // Channel Callbacks // ----------------------- +API.prototype.iterate_rooms = function(func) { + if ( func === undefined ) + func = this.trigger.bind(this, 'room-add'); + + for(var room_id in this.ffz.rooms) + func(room_id); +} + API.prototype._room_callbacks = function(room_id, room, specific_func) { var callback = this.register_room_set.bind(this, room_id); @@ -574,23 +628,23 @@ API.prototype._room_callbacks = function(room_id, room, specific_func) { API.prototype.register_on_room_callback = function(callback, dont_iterate) { - this.on_room_callbacks.push(callback); - var register_callback = this.register_room_set.bind(this, room_id); + var a = this, + thing = function(room_id) { + callback(room_id, a.register_room_set.bind(a, room_id)); + }; - // Call this for all current rooms. - if ( ! dont_iterate && this.ffz.rooms ) { - for(var room_id in this.ffz.rooms) - try { - callback(room_id, register_callback); - } catch(err) { - this.error("Error in On-Room Callback", err); - } - } + thing.original_func = callback; + this.on('room-add', thing); + + if ( ! dont_iterate ) + this.iterate_rooms(thing); } - API.prototype.unregister_on_room_callback = function(callback) { - var ind = this.on_room_callbacks.indexOf(callback); - if ( ind !== -1 ) - this.on_room_callbacks.splice(ind, 1); + var e = this._events['room-add'] || []; + for(var i=e.length; i--;) { + var cb = e[i]; + if ( cb && cb.original_func === callback ) + e.splice(i, 1); + } } \ No newline at end of file diff --git a/src/ext/betterttv.js b/src/ext/betterttv.js index 829a65b4..21758034 100644 --- a/src/ext/betterttv.js +++ b/src/ext/betterttv.js @@ -388,4 +388,5 @@ FFZ.prototype.setup_bttv = function(delay) { } this.update_ui_link(); + this.api_trigger('bttv-initialized'); } \ No newline at end of file diff --git a/src/main.js b/src/main.js index 2e4780b7..75e8262f 100644 --- a/src/main.js +++ b/src/main.js @@ -8,7 +8,20 @@ var FFZ = window.FrankerFaceZ = function() { // Logging this._log_data = []; this._apis = {}; - this._chat_filters = []; + + // Data structures + this.badges = {}; + this.users = {}; + this.rooms = {}; + + this.emoji_data = {}; + this.emoji_names = {}; + + this.emote_sets = {}; + this.global_sets = []; + this.default_sets = []; + this._last_emote_id = 0; + // Error Logging var t = this; @@ -21,8 +34,21 @@ var FFZ = window.FrankerFaceZ = function() { //t.log("JavaScript Error: " + event.message + " [" + event.filename + ":" + event.lineno + ":" + event.colno + "]", has_stack ? event.error.stack : undefined, false, has_stack); }); - // Get things started. - this.initialize(); + // Initialize settings as early as possible. + this.load_settings(); + + // Detect if we need to polyfill + if ( ! window.fetch ) { + this.log("Fetch is not detected. Requesting polyfill."); + var script = document.createElement('script'); + script.id = 'ffz-polyfill'; + script.type = 'text/javascript'; + script.src = FrankerFaceZ.constants.SERVER + 'script/fetch.polyfill.' + (FrankerFaceZ.constants.DEBUG ? '' : 'min.') + 'js?_=' + Date.now(); + document.head.appendChild(script); + + } else + // Get things started. + this.initialize(); } @@ -35,7 +61,7 @@ FFZ.channel_metadata = {}; // Version var VER = FFZ.version_info = { - major: 3, minor: 5, revision: 340, + major: 3, minor: 5, revision: 342, toString: function() { return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || ""); } @@ -240,6 +266,8 @@ require('./ext/warpworld'); // Initialization // --------------- +FFZ.prototype.initialized = false; + FFZ.prototype.initialize = function(increment, delay) { // Make sure that FrankerFaceZ doesn't start setting itself up until the // Twitch ember application is ready. @@ -287,7 +315,6 @@ FFZ.prototype.init_clips = function(delay) { this.log("Found Twitch Clips after " + (delay||0) + " ms at: " + location); this.log("Initializing FrankerFaceZ version " + FFZ.version_info); - this.users = {}; this.is_dashboard = false; this.embed_in_dash = false; this.is_clips = true; @@ -295,12 +322,14 @@ FFZ.prototype.init_clips = function(delay) { this.embed_in_clips = window.top !== window && window.top.location.hostname === 'clips.twitch.tv'; } catch(err) { this.embed_in_clips = false; } - this.load_settings(); this.setup_dark(); this.setup_css(); this.add_clips_darken_button(); + this.initialized = true; + this.api_trigger('initialized'); + var end = (window.performance && performance.now) ? performance.now() : Date.now(), duration = end - start; @@ -313,18 +342,19 @@ FFZ.prototype.init_player = function(delay) { this.log("Found Twitch Player after " + (delay||0) + " ms at: " + location); this.log("Initializing FrankerFaceZ version " + FFZ.version_info); - this.users = {}; this.is_dashboard = false; try { this.embed_in_dash = window.top !== window && /\/[^\/]+\/dashboard/.test(window.top.location.pathname) && !/bookmarks$/.test(window.top.location.pathname); } catch(err) { this.embed_in_dash = false; } // Literally only make it dark. - this.load_settings(); this.setup_dark(); this.setup_css(); this.setup_player(); + this.initialized = true; + this.api_trigger('initialized'); + var end = (window.performance && performance.now) ? performance.now() : Date.now(), duration = end - start; @@ -337,14 +367,12 @@ FFZ.prototype.init_normal = function(delay, no_socket) { this.log("Found non-Ember Twitch after " + (delay||0) + " ms at: " + location); this.log("Initializing FrankerFaceZ version " + FFZ.version_info); - this.users = {}; this.is_dashboard = false; try { this.embed_in_dash = window.top !== window && /\/[^\/]+\/dashboard/.test(window.top.location.pathname) && !/bookmarks$/.test(window.top.location.pathname); } catch(err) { this.embed_in_dash = false; } // Initialize all the modules. - this.load_settings(); this.setup_ember_wrapper(); // Start this early, for quick loading. @@ -373,6 +401,9 @@ FFZ.prototype.init_normal = function(delay, no_socket) { this.fix_tooltips(); this.find_bttv(10); + this.initialized = true; + this.api_trigger('initialized'); + var end = (window.performance && performance.now) ? performance.now() : Date.now(), duration = end - start; @@ -390,12 +421,10 @@ FFZ.prototype.init_dashboard = function(delay) { var match = location.pathname.match(/\/([^\/]+)/); this.dashboard_channel = match && match[1] || undefined; - this.users = {}; this.is_dashboard = true; this.embed_in_dash = false; // Initialize all the modules. - this.load_settings(); this.setup_ember_wrapper(); // Start this early, for quick loading. @@ -430,6 +459,9 @@ FFZ.prototype.init_dashboard = function(delay) { this.fix_tooltips(); this.find_bttv(10); + this.initialized = true; + this.api_trigger('initialized'); + var end = (window.performance && performance.now) ? performance.now() : Date.now(), duration = end - start; @@ -442,17 +474,12 @@ FFZ.prototype.init_ember = function(delay) { this.log("Found Twitch application after " + (delay||0) + " ms at: " + location); this.log("Initializing FrankerFaceZ version " + FFZ.version_info); - this.users = {}; this.is_dashboard = false; try { this.embed_in_dash = window.top !== window && /\/[^\/]+\/dashboard/.test(window.top.location.pathname) && !/bookmarks$/.test(window.top.location.pathname); } catch(err) { this.embed_in_dash = false; } - - // Settings are important. - this.load_settings(); - // Is debug mode enabled? Scratch that, everyone gets error handlers! if ( true ) { //this.settings.developer_mode ) { // Set up an error listener for RSVP. @@ -539,6 +566,9 @@ FFZ.prototype.init_ember = function(delay) { this.check_ff(); this.refresh_chat(); + this.initialized = true; + this.api_trigger('initialized'); + var end = (window.performance && performance.now) ? performance.now() : Date.now(), duration = end - start;