<commit_before>var BadgeStore = function(channel) {
  var self = this;

  $.ajax({
    url: 'https://api.twitch.tv/kraken/chat/' + channel + '/badges',
    success: function(data) {
      for (var key in data)
        self[key] = data[key].image;
    }
  })
}

var ChatAssetStore = function() {
  var self = this;

  this.emotes = {};
  this.badges = {};

  this.fetchBadges = function(channel) {
    this.badges[channel] = new BadgeStore(channel);
  }

  // Global emotes
  $.ajax({
    url: 'http://twitchemotes.com/api_cache/v2/global.json',
    success: function(data) {
      var template = data.template.small;

      for (var key in data.emotes) {
        var imageId = data.emotes[key].image_id.toString();
        self.emotes[key] = template.replace('{image_id}', imageId);
      }
    }
  });
  // Subscriber emotes
  $.ajax({
    url: 'http://twitchemotes.com/api_cache/v2/subscriber.json',
    success: function(data) {
      var template = data.template.small;

      for (var channel in data.channels) {
        var emotes = data.channels[channel].emotes;

        for (var i = 0; i < emotes.length; ++i) {
          var code = emotes[i].code;
          var imageId = emotes[i].image_id.toString();
          self.emotes[code] = template.replace('{image_id}', imageId);
        }
      }
    }
  });
  // BTTV emotes
  $.ajax({
    url: 'https://api.betterttv.net/2/emotes',
    success: function(data) {
      var template = data.urlTemplate;

      for (var i = 0; i < data.emotes.length; i++) {
        var emote = data.emotes[i];
        var code = emote.code;
        self.emotes[code] = template.replace('{{id}}', emote.id)
                                    .replace('{{image}}', '1x');
      }
    }
  });
}

var CHAT_DELAY = 0; // seconds

var currentChannel = null;
var chatSocket = null;

var chatContainer = $('#chat');
var chatLines = $('#chat-lines');

var chatAssetStore = new ChatAssetStore();

function toggleChat(visible) {
  chatContainer.toggle(visible);
}

function displayChatRaw(text) {
  var line = $('
').text(text);
  chatLines.append(line);
}

function connectToChat(channel) {
  chatAssetStore.fetchBadges(channel);

  // Close any previous connection
  if (chatSocket)
    chatSocket.close();

  // Clear chat's content
  chatLines.empty();
  // Attempt to connect to the new channel
  displayChatRaw('Joining chat: ' + channel + ' ...');
  chatSocket = new WebSocket('ws://datcoloc.com/chat/' + channel);

  chatSocket.onopen = function(e) {
    currentChannel = channel;
    displayChatRaw('Successfully joined the channel!');
    displayChatRaw('Delaying chat for: ' + CHAT_DELAY + ' seconds')
  };

  chatSocket.onerror = function(e) {
    displayChatRaw('Error: ' + e.reason);
  };

  chatSocket.onmessage = function(e) {
    var message = JSON.parse(e.data);
    setTimeout(displayPrivateMessage.bind(null, message), CHAT_DELAY * 1000);
  };
}

function displayPrivateMessage(message) {
  var color = message.tags ? message.tags.color : 'blue';

  var line = $('
')
    .addClass('chat-line');
  var nameSpan = $('')
    .addClass('chat-line-sender')
    .text(message.sender)
    .css('color', color);
  var contentSpan = makeContentSpan(message.content);

  prependBadges(message.tags, line);
  line.append(nameSpan);
  line.append(contentSpan);
  chatLines.append(line);

  chatContainer.scrollTop(chatContainer[0].scrollHeight);
}

function makeContentSpan(content) {
  var htmlParts = content.split(' ').map(function(word) {
    if (word in chatAssetStore.emotes)
      return '';

    return word;
  });
  return $('').html(': ' + htmlParts.join(' '));
}

function prependBadges(tags, line) {
  if (tags) {
    var specialAttributes = ['mod', 'subscriber', 'turbo']
      .filter(function(attribute) {
        return (tags[attribute] == '1');
      });

    if (tags['user-type'])
      specialAttributes.push(tags['user-type']);

    if (specialAttributes.length > 0) {
      var badgeWrapper = $('')
        .addClass('badge-wrapper');
      var badgeContainer = $('
');

      $.unique(specialAttributes).forEach(function(attribute) {
        var badge = $('')
          .attr('src', chatAssetStore.badges[currentChannel][attribute]);
        badgeContainer.append(badge);
      })

      badgeWrapper.wrapInner(badgeContainer);
      line.append(badgeWrapper);
    }

  }
}
<commit_msg>Fixed the hardcoded chat location<commit_after>var BadgeStore = function(channel) {
  var self = this;

  $.ajax({
    url: 'https://api.twitch.tv/kraken/chat/' + channel + '/badges',
    success: function(data) {
      for (var key in data)
        self[key] = data[key].image;
    }
  })
}

var ChatAssetStore = function() {
  var self = this;

  this.emotes = {};
  this.badges = {};

  this.fetchBadges = function(channel) {
    this.badges[channel] = new BadgeStore(channel);
  }

  // Global emotes
  $.ajax({
    url: 'http://twitchemotes.com/api_cache/v2/global.json',
    success: function(data) {
      var template = data.template.small;

      for (var key in data.emotes) {
        var imageId = data.emotes[key].image_id.toString();
        self.emotes[key] = template.replace('{image_id}', imageId);
      }
    }
  });
  // Subscriber emotes
  $.ajax({
    url: 'http://twitchemotes.com/api_cache/v2/subscriber.json',
    success: function(data) {
      var template = data.template.small;

      for (var channel in data.channels) {
        var emotes = data.channels[channel].emotes;

        for (var i = 0; i < emotes.length; ++i) {
          var code = emotes[i].code;
          var imageId = emotes[i].image_id.toString();
          self.emotes[code] = template.replace('{image_id}', imageId);
        }
      }
    }
  });
  // BTTV emotes
  $.ajax({
    url: 'https://api.betterttv.net/2/emotes',
    success: function(data) {
      var template = data.urlTemplate;

      for (var i = 0; i < data.emotes.length; i++) {
        var emote = data.emotes[i];
        var code = emote.code;
        self.emotes[code] = template.replace('{{id}}', emote.id)
                                    .replace('{{image}}', '1x');
      }
    }
  });
}

var CHAT_DELAY = 0; // seconds

var currentChannel = null;
var chatSocket = null;

var chatContainer = $('#chat');
var chatLines = $('#chat-lines');

var chatAssetStore = new ChatAssetStore();

function toggleChat(visible) {
  chatContainer.toggle(visible);
}

function displayChatRaw(text) {
  var line = $('
').text(text);
  chatLines.append(line);
}

function connectToChat(channel) {
  chatAssetStore.fetchBadges(channel);

  // Close any previous connection
  if (chatSocket)
    chatSocket.close();

  // Clear chat's content
  chatLines.empty();
  // Attempt to connect to the new channel
  displayChatRaw('Joining chat: ' + channel + ' ...');

  chatSocket = new WebSocket('ws://' + window.location.host + '/chat/' + channel);

  chatSocket.onopen = function(e) {
    currentChannel = channel;
    displayChatRaw('Successfully joined the channel!');
    displayChatRaw('Delaying chat for: ' + CHAT_DELAY + ' seconds')
  };

  chatSocket.onerror = function(e) {
    displayChatRaw('Error: ' + e.reason);
  };

  chatSocket.onmessage = function(e) {
    var message = JSON.parse(e.data);
    setTimeout(displayPrivateMessage.bind(null, message), CHAT_DELAY * 1000);
  };
}

function displayPrivateMessage(message) {
  var color = message.tags ? message.tags.color : 'blue';

  var line = $('
')
    .addClass('chat-line');
  var nameSpan = $('')
    .addClass('chat-line-sender')
    .text(message.sender)
    .css('color', color);
  var contentSpan = makeContentSpan(message.content);

  prependBadges(message.tags, line);
  line.append(nameSpan);
  line.append(contentSpan);
  chatLines.append(line);

  chatContainer.scrollTop(chatContainer[0].scrollHeight);
}

function makeContentSpan(content) {
  var htmlParts = content.split(' ').map(function(word) {
    if (word in chatAssetStore.emotes)
      return '';

    return word;
  });
  return $('').html(': ' + htmlParts.join(' '));
}

function prependBadges(tags, line) {
  if (tags) {
    var specialAttributes = ['mod', 'subscriber', 'turbo']
      .filter(function(attribute) {
        return (tags[attribute] == '1');
      });

    if (tags['user-type'])
      specialAttributes.push(tags['user-type']);

    if (specialAttributes.length > 0) {
      var badgeWrapper = $('')
        .addClass('badge-wrapper');
      var badgeContainer = $('
');

      $.unique(specialAttributes).forEach(function(attribute) {
        var badge = $('')
          .attr('src', chatAssetStore.badges[currentChannel][attribute]);
        badgeContainer.append(badge);
      })

      badgeWrapper.wrapInner(badgeContainer);
      line.append(badgeWrapper);
    }

  }
}
<|endoftext|>


<commit_before>// We use this function throughout all the things to send and recieve form our
// django-rest-framework API
function quickFetch(url, method, body) {
    let csrftoken = Cookies.get('csrftoken');
    method = (typeof method !== 'undefined') ? method : 'get';

    // Give us back a promise we can .then() on, data can be accessed via
    // .then(function(data) {console.log(data)})
    return fetch(url, {
        credentials: 'include',
        headers: new Headers({
            'content-type': 'application/json',
            'X-CSRFToken': csrftoken
        }),
        method: method,
        body: JSON.stringify(body)
    }).then(function(response) {
        // Delete response throws an error with .json().
        // TODO: Figure out a proper way to return information on DELETE.
        if (method != 'delete') {
            return response.json();
        }
    });
}

// Append number with 0 if there is only 1 digit
function pad(num) {
    num = num.toString();
    if (num.length === 1) {
        num = '0' + num;
    }
    return num;
}

// Convert a decimal duration to a string (0:00).
function durationToString(duration) {
    if (typeof(duration) === 'number') {
        let hours = Math.floor(duration);
        let minutes = Math.round((duration - hours) * 60);
        duration = hours + ':' + pad(minutes);
    }
    return duration;
}
<commit_msg>Check response status codes in quickFetch.<commit_after>// We use this function throughout all the things to send and recieve form our
// django-rest-framework API
function quickFetch(url, method, body) {
    let csrftoken = Cookies.get('csrftoken');
    method = (typeof method !== 'undefined') ? method : 'get';

    // Give us back a promise we can .then() on, data can be accessed via
    // .then(function(data) {console.log(data)})
    return fetch(url, {
        credentials: 'include',
        headers: new Headers({
            'content-type': 'application/json',
            'X-CSRFToken': csrftoken
        }),
        method: method,
        body: JSON.stringify(body)
    }).then(function(response) {
        let result = null;
        switch (response.status) {
            case 200: // HTTP_200_OK
            case 201: // HTTP_201_CREATED
                result = response.json();
                break;
            default:
                result = response;
                break;
        }
        return result;
    });
}

// Append number with 0 if there is only 1 digit
function pad(num) {
    num = num.toString();
    if (num.length === 1) {
        num = '0' + num;
    }
    return num;
}

// Convert a decimal duration to a string (0:00).
function durationToString(duration) {
    if (typeof(duration) === 'number') {
        let hours = Math.floor(duration);
        let minutes = Math.round((duration - hours) * 60);
        duration = hours + ':' + pad(minutes);
    }
    return duration;
}
<|endoftext|>


<commit_before>'use strict';

const _ = require('underscore');
const ms = require('ms');
const Cache = require('caching-map');
const Promise = require('bluebird');

const debug = require('debug')('node-thrift2-hbase:cache');

class HBaseThriftClientCache extends Cache {

    constructor(fetchfunction, options) {
        super((options && options.limit ) || 500000);

        this.fetch = fetchfunction;
        this.ttl = (options && options.ttl) || ms('5m');
        this.materialize = function (getObj) {
            debug('materializing', getObj);
            return fetchfunction(getObj.table, getObj);
        }
    }

    get(table, getObj, options, callback) {
        getObj.table = table;
        debug(getObj);

        super.get(getObj)
            .then(value => callback(null, Promise.resolve(value)))
            .catch(err => callback(err));
    }

}

Promise.promisifyAll(HBaseThriftClientCache.prototype);

module.exports = HBaseThriftClientCache;<commit_msg>fix cache<commit_after>'use strict';

const _ = require('underscore');
const ms = require('ms');
const Promise = require('bluebird');

const debug = require('debug')('node-thrift2-hbase:cache');

// LRU cache for people who like ES6 and promises
const Util = require('util');

// Convert any value into a limit: a positive number, zero or Infinity.
function actualLimit(limit) {
    return Number.isFinite(limit) ? Math.max(0, limit) : Infinity;
}

// Convert any value into a cost: a positive number or zero.  If no number
// provided (undefined, NaN, etc) return the default cost of one.
function actualCost(cost) {
    return Number.isFinite(cost) ? Math.max(0, cost) : 1;
}

// TTL (milliseconds) to expiration (timestamp)
function ttlToExpires(ttl) {
    return Number.isInteger(ttl) ? Date.now() + ttl : Infinity;
}

// Returns true if the entry has expired already
function hasExpired(link) {
    return (link.expires <= Date.now());
}

// 1. We don't want these properties to show up in console.log(cache)
// 2. Private methods could come here, when io.js supports them
const _cost   = Symbol('cost');
const _head   = Symbol('head');
const _tail   = Symbol('tail');
const _limit  = Symbol('limit');
const _map    = Symbol('map');

class Cache {

    constructor(limit, source) {
        this[_map]  = new Map();
        this[_cost] = 0;

        if (limit && limit[Symbol.iterator]) {
            source = limit;
            limit  = (source instanceof Cache) ? source.limit : undefined;
        }
        this.limit = limit;
        this.materialize = null;

        if (source instanceof Cache)
            this._cloneCache(source);
        else if (source)
            this._cloneIterator(source);
    }

    _cloneIterator(source) {
        for (let entry of source)
            this.set(entry[0], entry[1]);
    }

    _cloneCache(source) {
        let link = source[_tail];
        while (link) {
            this.set(link.key, link.value, link);
            link = link.previous;
        }
        this.materialize = source.materialize;
    }

    // Remove link from linked list.  Used when deleting, and when moving link
    // to head (during get).
    _removeFromList(link) {
        if (this[_head] === link)
            this[_head] = link.next;
        if (this[_tail] === link)
            this[_tail] = link.previous;

        if (link.next)
            link.next.previous = link.previous;
        if (link.previous)
            link.previous.next = link.next;
    }

    // Prepend to linked list.  Used when adding new link (set) or when moving
    // existing link to beginning of linked list (get).
    _prependToList(link) {
        link.previous  = null;
        link.next      = this[_head];
        if (this[_head])
            this[_head].previous = link;
        this[_head]            = link;
        if (!this[_tail])
            this[_tail] = link;
    }

    get limit() {
        // Getter because we use a setter
        return this[_limit];
    }

    // Sets the cache limit
    set limit(value) {
        // You can use whatever value you want, but we need it to be a positive
        // number, possibly Infinity
        this[_limit] = actualLimit(value);
    }

    // Returns the current cost
    get cost() {
        return this[_cost];
    }

    get size() {
        return this[_map].size;
    }

    clear() {
        this[_map].clear();
        this[_cost] = 0;
        this[_head] = null;
        this[_tail] = null;
    }

    delete(key) {
        const link = this[_map].get(key);
        if (!link)
            return false;

        this._removeFromList(link);
        this[_map].delete(key);

        // Discount
        this[_cost] = this[_cost] - link.cost;

        return true;
    }

    // Evicts as many expired entries an least recently used entries to keep
    // cache under limit.  This can be O(N) as it may iterate over the entire
    // cache twice, but in practice we generally evict as many entries as we add,
    // so viewed over long time horizon, this is an O(1) operation.
    _evict(limit) {
        // Only evicts enough expired keys to make room for new key, if you need to
        // evict all expired keys, use the iterator.
        for (let link of this[_map].values()) {
            if (this[_cost] <= limit)
                break;
            if (hasExpired(link))
                this.delete(link.key);
        }

        // Remove from the tail is potentiall O(N), we in practice we usually evict
        // as many entries as we add, so evict is O(1) spread over time
        while (this.size && this[_cost] > limit) {
            const leastRecent = this[_tail];
            this.delete(leastRecent.key);
        }
    }

    // Returns the key value if set and not evicted yet.
    get(key) {
        const link = this[_map].get(key);
        // Although we do have the value, the contract is that we don't return
        // expired values
        if (link && !hasExpired(link)) {
            this._moveLinkToHead(link);
            return link.value;
        } else if (this.materialize)
            return this._materializeAndCache(key);
        else
            return undefined;
    }

    _moveLinkToHead(link) {
        // Link becomes most recently used
        const mostRecent = (this[_head] === link);
        if (!mostRecent) {
            // This is not the most CPU efficient, there's some redundant linked list
            // changes that we can consolidate if we implemented a move; but in real
            // life, you won't be able to measure the difference, so we opt to reuse
            // existing methods, this gives us better test coverage
            this._removeFromList(link);
            this._prependToList(link);
        }
    }

    _materializeAndCache(key) {
        const self    = this;
        const promise = Promise.resolve(key).then(this.materialize);

        function deleteIfRejected() {
            const entry = self[_map].get(key);
            if (entry && entry.value === promise)
                self.delete(key);
        }

        this.set(key, promise,{ttl:this.ttl});
        promise.catch(deleteIfRejected);
        return promise;
    }

    // Returns true if key has been set and not evicted yet.
    has(key) {
        const link = this[_map].get(key);
        if (!link)
            return false;
        return !hasExpired(link);
    }

    *entries() {
        let link = this[_head];
        while (link) {
            // We take this opportunity to get rid of expired keys
            if (hasExpired(link))
                this.delete(link.key);
            else
                yield [link.key, link.value];
            link = link.next;
        }
    }

    *values() {
        // This could be `let [key] of` in future version
        for (let entry of this.entries())
            yield entry[1];
    }

    *keys() {
        // This could be `let [key, value] of` in future version
        for (let entry of this.entries())
            yield entry[0];
    }

    forEach(callback, thisArg) {
        // This could be `let [key, value] of` in future version
        for (let entry of this.entries())
            callback.call(thisArg, entry[1], entry[0], this);
    }

    // Stores the key and value.
    //
    // Each key is associated with a cost.  The cost is a positive number, and
    // the default value is 1.  When the total cost is higher than the cache
    // limit, it will start evicting least recently used values.  You can use a
    // cost of zero to keep the key indefinitely (or until it expires).
    //
    // Each key has a TTL associated with it.  Expired keys are evicted first to
    // make room for new keys.
    //
    // The following two are equivalent:
    //
    //   set(key, value)
    //   set(key, value, { cost: 1, ttl: Infinity })
    set(key, value, options) {
        const cost      = actualCost(options && options.cost);
        const expires   = ttlToExpires(options && options.ttl);

        this.delete(key);

        // If TTL is zero we're never going to return this key, we don't want to
        // evict older keys either
        if (expires <= Date.now())
            return this;

        // If this key can't fit, we don't want to evict other keys to make room
        const canHoldKey = (cost <= this.limit);
        if (!canHoldKey)
            return this;

        // Evict enough keys to make room for this one
        const leaveRoomForKey = (this.limit - cost);
        this._evict(leaveRoomForKey);

        // Double linked list (previous, next) for O(1) reordering of recently used
        // keys.  Every place you see a link, it refes to an object with these
        // properties.
        //
        // We need the key here as well so we can evict least recently used entries
        const link = {
            key,
            value,
            previous: null,
            next:     null,
            cost,
            expires,

            inspect(depth, inspectOptions) {
                // console.log(cache) calls inspect(cache) on the Map, which ends up
                // calling inspect on each map value (i.e. this link object).  We want
                // to show the stored value (just like a Map).
                return Util.inspect(value, inspectOptions);
            }
        };

        this._prependToList(link);
        this[_map].set(key, link);
        this[_cost] = this[_cost] + cost;

        // Map allows you to chain calls to set()
        return this;
    }

    // Util.inspect(cache) calls this, and Node's console.log uses inspect
    inspect(depth, inspectOptions) {
        return Util.inspect(this[_map], inspectOptions);
    }

}

// Just like Map, the default iterator iterates over all entries (except our order is different)
Cache.prototype[Symbol.iterator] = Cache.prototype.entries;

class KeyObjectCache extends Cache {

    // Returns the key value if set and not evicted yet.
    get(objKey) {
        const key = this.generateKey(objKey);
        const link = this[_map].get(key);
        // Although we do have the value, the contract is that we don't return
        // expired values
        if (link && !hasExpired(link)) {
            this._moveLinkToHead(link);
            return link.value;
        } else if (this.materialize)
            return this._materializeAndCache(key, objKey);
        else
            return undefined;
    }

    _materializeAndCache(key, objKey) {
        const self    = this;
        const promise = Promise.resolve(objKey).then(this.materialize);

        function deleteIfRejected() {
            const entry = self[_map].get(key);
            if (entry && entry.value === promise)
                self.delete(key);
        }

        this.set(key, promise,{ttl:this.ttl});
        promise.catch(deleteIfRejected);
        return promise;
    }

}

class HBaseThriftClientCache extends KeyObjectCache {

    generateKey(keyObj) {
        return [keyObj.table,
            keyObj.row,
            keyObj.maxVersions,
            keyObj.timeRange,
            keyObj.columns.map(cell => cell.family + ":" + cell.qualifier).sort().join(","),
            Object.entries(keyObj.columnTypes).map(keyValue => keyValue.join(":")).sort().join(",")].join(".");

    }

    constructor(fetchfunction, options) {
        super((options && options.limit ) || 500000);

        this.fetch = fetchfunction;
        this.ttl = (options && options.ttl) || ms('5m');
        this.materialize = function (getObj) {
            debug('materializing', getObj);
            return fetchfunction(getObj.table, getObj);
        }
    }

    get(table, getObj, options, callback) {
        getObj.table = table;
        debug(getObj);

        const key = this.generateKey(getObj);

        super.get(getObj)
            .then(value => callback(null, Promise.resolve(value)))
            .catch(err => callback(err));
    }

}

Promise.promisifyAll(HBaseThriftClientCache.prototype);

module.exports = HBaseThriftClientCache;<|endoftext|>


<commit_before>function json_macro(param, object) {
	// If param.include is defined, only include the keys defined in that
	// coma seperated string:
	if (param.include) {
		var include = param.include.split(',').each(function(val) {
			this[val] = true;
		}, {});
		object = object.each(function(val, key) {
			if (include[key])
				this[key] = val;
		}, {});
	}
	return Json.encode(object, param.singleQuotes == 'true');
}

function link_macro(param) {
	function createHexString(str) {
		res.push();
		if (str != null) {
			for (var i = 0; i < str.length; i++) {
				// two \\ needed because it's javascript encoded (for the client side)
				res.write('\\x');
				res.write(str.charCodeAt(i).toString(16));
			}
		}
		return res.pop();
	}
	if (param.id) {
		var node = HopObject.get(param.id);
		if (node)
			node.renderLink(param.text, res);
	} else {
		if (param.mail) {
			var parts = param.mail.split('@');
			if (parts.length == 2)
				return renderLink(param.text, "javascript:mailTo('" + createHexString(parts[0]) + "','" + createMailHexString(parts[1]) + "')", null, res);
		} else if (param.url) {
			var options = null;
			if (!/^\//.test(param.url)) // not a local page
				options = { attributes: { target: '_blank' }};
			return renderLink(param.text, param.url, options, res);
		}
	}
}

function renderText_macro(param) {
	if (param.text) {
		var font = Font.getInstance(param.font);
		if (font) {
			font.renderText(param.text, param, res);
		}
	}
}
<commit_msg>Removed undefined values form json macro.<commit_after>function json_macro(param, object) {
	// If param.include is defined, only include the keys defined in that
	// coma seperated string:
	if (param.include) {
		var include = param.include.split(',').each(function(val) {
			this[val] = true;
		}, {});
		object = object.each(function(val, key) {
			if (include[key] && val !== undefined)
				this[key] = val;
		}, {});
	}
	return Json.encode(object, param.singleQuotes == 'true');
}

function link_macro(param) {
	function createHexString(str) {
		res.push();
		if (str != null) {
			for (var i = 0; i < str.length; i++) {
				// two \\ needed because it's javascript encoded (for the client side)
				res.write('\\x');
				res.write(str.charCodeAt(i).toString(16));
			}
		}
		return res.pop();
	}
	if (param.id) {
		var node = HopObject.get(param.id);
		if (node)
			node.renderLink(param.text, res);
	} else {
		if (param.mail) {
			var parts = param.mail.split('@');
			if (parts.length == 2)
				return renderLink(param.text, "javascript:mailTo('" + createHexString(parts[0]) + "','" + createMailHexString(parts[1]) + "')", null, res);
		} else if (param.url) {
			var options = null;
			if (!/^\//.test(param.url)) // not a local page
				options = { attributes: { target: '_blank' }};
			return renderLink(param.text, param.url, options, res);
		}
	}
}

function renderText_macro(param) {
	if (param.text) {
		var font = Font.getInstance(param.font);
		if (font) {
			font.renderText(param.text, param, res);
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>Get Programming<commit_after><|endoftext|>


<commit_before>const isString = require("lodash/isString")
const swap = require("reaks/swap")
const label = require("uiks/reaks/label").reaks

module.exports = pageGenerator => {
  return ctx => {
    const getPage = pageGenerator(ctx)
    return {
      title: swap(() => {
        const page = getPage()
        const title = page.title
        return isString(title) ? label(title) : title
      }),
      content: swap(() => {
        const page = getPage()
        return page.content
      }),
      action: swap(() => {
        const page = getPage()
        return page.action
      }),
    }
  }
}
<commit_msg>pageSwap memoized<commit_after>const isString = require("lodash/isString")
const swap = require("reaks/swap")
const label = require("uiks/reaks/label").reaks
const { observable } = require("kobs")

module.exports = pageGenerator => {
  return ctx => {
    const getPage = observable(pageGenerator(ctx))
    return {
      title: swap(() => {
        const page = getPage()
        const title = page.title
        return isString(title) ? label(title) : title
      }),
      content: swap(() => {
        const page = getPage()
        return page.content
      }),
      action: swap(() => {
        const page = getPage()
        return page.action
      }),
    }
  }
}
<|endoftext|>


<commit_before>/**
 * Created with RubyMine.
 * User: teamco
 * Date: 3/3/14
 * Time: 12:14 AM
 */

define([
    'config/anthill'
], function defineRenderer(AntHill) {

    /**
     * Define renderer
     * @class Renderer
     * @extends AntHill
     * @extends BaseElement
     * @constructor
     */
    var Renderer = function Renderer() {

    };

    return Renderer.extend('Renderer', {

        /**
         * Render tooltip
         * @param {{
         *      $container: *|jQuery,
         *      title: string,
         *      [description]: string,
         *      [imageUrl]: string,
         *      [imageFloat]: string
         * }} opts
         * @returns {*|jQuery}
         */
        renderTooltip: function renderTooltip(opts) {

            var $title = $('<h2 />').text(opts.title),
                $description = $('<p />').html(opts.description),
                $image;

            if (opts.imageUrl) {

                $image = $('<img />').attr({
                    src: opts.imageUrl,
                    alt: opts.title
                }).css({
                    cssFloat: opts.imageFloat || 'none'
                });
            }

            var $tooltip = $('<div />').append([
                $title,
                $image,
                $description
            ]).addClass('tooltip');

            if (!opts.$container) {
                return $tooltip;
            }

            opts.$container.$.hover(
                function on() {

                    opts.$container.$.append(

                        $tooltip.stop().
                            fadeTo('slow', 0.9)

                    ).attr({
                            title: ''
                        });

                    opts.$container.$.on('mousemove.gallery', function (e) {

                        /**
                         * Define top
                         * @type {number}
                         */
                        var topL = e.pageY - $tooltip.height() - 20,
                            topM = e.pageY + $tooltip.height();

                        /**
                         * Define left
                         * @type {number}
                         */
                        var leftL = e.pageX - 100,
                            leftM = e.pageX;

                        $tooltip.offset({
                            top: topL < 0 ? topM : topL,
                            left: leftL < 0 ? leftM : leftL
                        });
                    });
                },

                function off() {

                    $tooltip.remove();

                    opts.$container.$.off('mousemove.gallery').attr({
                        title: opts.title
                    });
                }
            );
        },

        /**
         * Render label
         * @member Renderer
         * @param {*|string} uuid
         * @param {string} text
         * @param {*|string} [type]
         * @param {boolean} [visible]
         * @returns {*|jQuery}
         */
        renderLabel: function renderLabel(uuid, text, type, visible) {

            /**
             * Parse Html
             * @type {Array}
             */
            var parse = $.parseHTML(text) || [''],
                title = parse[parse.length - 1].data || '';

            var $label = $('<label />').attr({
                'for': uuid,
                title: title.toUpperCase()
            }).addClass(type).html(text);

            if (!visible) {
                $label.hide();
            }

            return $label;
        },

        /**
         * Render link event
         * @param opts
         * @returns {*|jQuery}
         */
        renderEventLink: function renderEventLink(opts) {

            /**
             * Create UUID
             * @type {String}
             */
            var uuid = this.base.lib.generator.UUID() + '-event';

            /**
             * Define $link
             * @type {*|jQuery}
             */
            var $link = $('<div />').attr({
                rel: opts.name,
                id: uuid,
                title: opts.title
            }).text(opts.title).
                addClass(opts.name.toLowerCase()).append(
                $('<input />').attr({
                    name: opts.group,
                    type: 'radio'
                })
            );

            $link.on(
                opts.events.join(' '),

                /**
                 * On event
                 * @private
                 */
                function _onEvent(e) {

                    e.preventDefault();
                    e.stopPropagation();

                    /**
                     * Define widget content
                     * @type {WidgetContent}
                     */
                    var content = this.view.scope;

                    // Reset to default value
                    $('input:radio[name="' + e.target.name + '"]').val('on');

                    // Set new value
                    $('input', $(e.target)).prop({
                        checked: true
                    }).val(opts.name);

                    content.observer.publish(
                        content.eventmanager.eventList.executeOnWidgetEvent,
                        opts.name
                    );

                }.bind(this)
            );

            if (!opts.visible) {
                $link.hide();
            }

            return $link;
        },

        /**
         * Render text field
         * @member Renderer
         * @param {{text: string, name: string, [placeholder]: string, value, [disabled]: boolean, [monitor]}} opts
         * @returns {*[]}
         */
        renderTextField: function renderTextField(opts) {

            /**
             * Create UUID
             * @type {String}
             */
            var uuid = this.base.lib.generator.UUID() + '-input';

            /**
             * Define $input
             * @type {*|jQuery}
             */
            var $input = $('<input />').attr({
                name: opts.name,
                type: 'text',
                id: uuid,
                placeholder: opts.placeholder,
                title: opts.value,
                disabled: this.base.defineBoolean(opts.disabled, false, true)
            }).val(opts.value);

            if (opts.monitor) {

                $input.on(
                    opts.monitor.events.join(','),
                    opts.monitor.callback
                );
            }

            if (!opts.visible) {
                $input.hide();
            }

            return [
                this.renderLabel(uuid, opts.text, 'text', opts.visible),
                $input
            ];
        },

        /**
         * Render checkbox
         * @member Renderer
         * @param {{text: string, name: string, value, [checked]: boolean, [disabled]: boolean, [monitor]}} opts
         * @returns {*[]}
         */
        renderCheckbox: function renderCheckbox(opts) {

            /**
             * Create UUID
             * @type {String}
             */
            var uuid = this.base.lib.generator.UUID() + '-checkbox',
                checked = this.base.defineBoolean(opts.checked, false, true);

            /**
             * Define $input
             * @type {*|jQuery}
             */
            var $input = $('<input />').attr({
                name: opts.name,
                type: 'checkbox',
                id: uuid,
                title: opts.value,
                checked: checked,
                disabled: this.base.defineBoolean(opts.disabled, false, true)
            }).val(opts.value);

            $input.prop(
                'checked',
                checked
            );

            if (opts.monitor) {

                $input.on(
                    opts.monitor.events.join(','),
                    opts.monitor.callback
                );
            }

            if (!opts.visible) {
                $input.hide();
            }

            return [
                $input,
                this.renderLabel(uuid, opts.text, 'text', opts.visible)
            ];
        },

        /**
         * Render text area
         * @member Renderer
         * @param {{text: string, name: string, [placeholder]: string, value, [disabled]: boolean}} opts
         * @returns {*[]}
         */
        renderTextArea: function renderTextArea(opts) {

            /**
             * Create UUID
             * @type {String}
             */
            var uuid = this.base.lib.generator.UUID() + '-textarea',
                $input;

            if (this.base.defineBoolean(opts.disabled, false, true)) {

                /**
                 * Define $input
                 * @type {*|jQuery}
                 */
                $input = $('<p />').attr({
                    name: opts.name,
                    id: uuid,
                    title: opts.value
                }).addClass('textarea').text(opts.value);

            } else {

                /**
                 * Define $input
                 * @type {*|jQuery}
                 */
                $input = $('<textarea />').attr({
                    name: opts.name,
                    id: uuid,
                    placeholder: opts.placeholder,
                    title: opts.value
                }).val(opts.value);
            }

            if (!opts.visible) {
                $input.hide();
            }

            return [
                this.renderLabel(uuid, opts.text, 'textarea', opts.visible),
                $input
            ];
        },

        /**
         * Render combo box
         * @member Renderer
         * @param {Array} data
         * @param selected
         * @param {string} name
         * @param {string} index
         * @param {{type: string, callback: function}} [event]
         * @param {boolean} [visible]
         */
        renderCombobox: function renderCombobox(data, selected, name, index, event, visible) {

            /**
             * Define container
             * @type {*|jQuery}
             */
            var $div = $('<div style="display: none;" />').addClass('combo-box').attr({
                id: this.base.lib.generator.UUID() + '-combobox'
            }).append(
                $('<input class="hidden" />').attr({
                    name: index,
                    disabled: true,
                    type: 'text',
                    value: selected
                })
            );

            /**
             * Open combo box
             * @private
             */
            function _open() {
                // close all como-boxes
                $('.combo-box').removeClass('open');
                $div.addClass('open');
            }

            /**
             * Hide combo box
             * @private
             */
            function _hide() {
                $div.removeClass('open');
            }

            /**
             * Store prefs
             * @param $selected
             * @param selected
             * @returns {boolean}
             * @private
             */
            function _store($selected, selected) {

                /**
                 * Define value
                 * @type {String}
                 */
                var value = $selected.text();

                if (value === selected) {
                    return false;
                }

                $('input[name="' + index + '"]', $div).val(value);
            }

            /**
             * Define $ul
             * @type {*|jQuery}
             */
            var $ul = $('<ul />');

            for (var i = 0, l = data.length; i < l; i++) {

                var field = data[i],
                    $li = $('<li />');

                if (field.type === 'text') {
                    $li.text(field.value);
                }

                if (field.type === 'html') {
                    $li.html(field.value);
                }

                if (field.type === 'field') {
                    $li.append(
                        this.renderTextField({
                            name: field.name,
                            placeholder: field.placeholder,
                            value: field.value,
                            disabled: field.disabled
                        })
                    );
                }

                if (selected === field.value) {
                    $li.addClass('selected');
                }

                $li.on(
                    'click.comboBoxInternal',

                    /**
                     * Select combo box item
                     * @param e
                     * @returns {boolean}
                     */
                    function comboBoxInternalEvent(e) {

                        $div.hasClass('open') ?
                            _hide() : _open();

                        /**
                         * Define selected $li
                         * @type {*|jQuery|HTMLElement}
                         */
                        var $selected = $(e.target);

                        if ($selected.hasClass('selected')) {
                            _store($selected, selected);
                            return false;
                        }

                        $('li', $selected.parent()).removeClass('selected');
                        $selected.addClass('selected');

                        _store($selected, selected);

                    }.bind(this)
                );

                // hide on mouse leave
                $ul.on('mouseleave.comboBoxInternal', _hide);

                if (this.base.isDefined(event)) {
                    if (this.base.isFunction(event.callback)) {
                        $li.on(event.type, function comboBoxEvent(e) {
                            event.callback($(e.target).attr('rel'));
                        });
                    }
                }

                $li.attr({rel: field.key || field.value}).appendTo($ul);
            }

            // fix to define modal dialog height
            setTimeout(function () {
                visible ? $div.show() : $div.hide();
            }, 500);

            return [
                this.renderLabel(undefined, name, undefined, visible),
                $div.append([
                    $ul,
                    $('<div />').addClass('combo-box-arrow')
                ])
            ];
        }

    }, AntHill.prototype);
});<commit_msg>fix top position<commit_after>/**
 * Created with RubyMine.
 * User: teamco
 * Date: 3/3/14
 * Time: 12:14 AM
 */

define([
    'config/anthill'
], function defineRenderer(AntHill) {

    /**
     * Define renderer
     * @class Renderer
     * @extends AntHill
     * @extends BaseElement
     * @constructor
     */
    var Renderer = function Renderer() {

    };

    return Renderer.extend('Renderer', {

        /**
         * Render tooltip
         * @param {{
         *      $container: *|jQuery,
         *      title: string,
         *      [description]: string,
         *      [imageUrl]: string,
         *      [imageFloat]: string
         * }} opts
         * @returns {*|jQuery}
         */
        renderTooltip: function renderTooltip(opts) {

            var $title = $('<h2 />').text(opts.title),
                $description = $('<p />').html(opts.description),
                $image;

            if (opts.imageUrl) {

                $image = $('<img />').attr({
                    src: opts.imageUrl,
                    alt: opts.title
                }).css({
                    cssFloat: opts.imageFloat || 'none'
                });
            }

            var $tooltip = $('<div />').append([
                $title,
                $image,
                $description
            ]).addClass('tooltip');

            if (!opts.$container) {
                return $tooltip;
            }

            opts.$container.$.hover(
                function on() {

                    opts.$container.$.append(

                        $tooltip.stop().
                            fadeTo('slow', 0.9)

                    ).attr({
                            title: ''
                        });

                    opts.$container.$.on('mousemove.gallery', function (e) {

                        /**
                         * Define top
                         * @type {number}
                         */
                        var topL = e.pageY - $tooltip.height() - 20,
                            topM = e.pageY + 20;

                        /**
                         * Define left
                         * @type {number}
                         */
                        var leftL = e.pageX - 100,
                            leftM = e.pageX;

                        $tooltip.offset({
                            top: topL < 0 ? topM : topL,
                            left: leftL < 0 ? leftM : leftL
                        });
                    });
                },

                function off() {

                    $tooltip.remove();

                    opts.$container.$.off('mousemove.gallery').attr({
                        title: opts.title
                    });
                }
            );
        },

        /**
         * Render label
         * @member Renderer
         * @param {*|string} uuid
         * @param {string} text
         * @param {*|string} [type]
         * @param {boolean} [visible]
         * @returns {*|jQuery}
         */
        renderLabel: function renderLabel(uuid, text, type, visible) {

            /**
             * Parse Html
             * @type {Array}
             */
            var parse = $.parseHTML(text) || [''],
                title = parse[parse.length - 1].data || '';

            var $label = $('<label />').attr({
                'for': uuid,
                title: title.toUpperCase()
            }).addClass(type).html(text);

            if (!visible) {
                $label.hide();
            }

            return $label;
        },

        /**
         * Render link event
         * @param opts
         * @returns {*|jQuery}
         */
        renderEventLink: function renderEventLink(opts) {

            /**
             * Create UUID
             * @type {String}
             */
            var uuid = this.base.lib.generator.UUID() + '-event';

            /**
             * Define $link
             * @type {*|jQuery}
             */
            var $link = $('<div />').attr({
                rel: opts.name,
                id: uuid,
                title: opts.title
            }).text(opts.title).
                addClass(opts.name.toLowerCase()).append(
                $('<input />').attr({
                    name: opts.group,
                    type: 'radio'
                })
            );

            $link.on(
                opts.events.join(' '),

                /**
                 * On event
                 * @private
                 */
                function _onEvent(e) {

                    e.preventDefault();
                    e.stopPropagation();

                    /**
                     * Define widget content
                     * @type {WidgetContent}
                     */
                    var content = this.view.scope;

                    // Reset to default value
                    $('input:radio[name="' + e.target.name + '"]').val('on');

                    // Set new value
                    $('input', $(e.target)).prop({
                        checked: true
                    }).val(opts.name);

                    content.observer.publish(
                        content.eventmanager.eventList.executeOnWidgetEvent,
                        opts.name
                    );

                }.bind(this)
            );

            if (!opts.visible) {
                $link.hide();
            }

            return $link;
        },

        /**
         * Render text field
         * @member Renderer
         * @param {{text: string, name: string, [placeholder]: string, value, [disabled]: boolean, [monitor]}} opts
         * @returns {*[]}
         */
        renderTextField: function renderTextField(opts) {

            /**
             * Create UUID
             * @type {String}
             */
            var uuid = this.base.lib.generator.UUID() + '-input';

            /**
             * Define $input
             * @type {*|jQuery}
             */
            var $input = $('<input />').attr({
                name: opts.name,
                type: 'text',
                id: uuid,
                placeholder: opts.placeholder,
                title: opts.value,
                disabled: this.base.defineBoolean(opts.disabled, false, true)
            }).val(opts.value);

            if (opts.monitor) {

                $input.on(
                    opts.monitor.events.join(','),
                    opts.monitor.callback
                );
            }

            if (!opts.visible) {
                $input.hide();
            }

            return [
                this.renderLabel(uuid, opts.text, 'text', opts.visible),
                $input
            ];
        },

        /**
         * Render checkbox
         * @member Renderer
         * @param {{text: string, name: string, value, [checked]: boolean, [disabled]: boolean, [monitor]}} opts
         * @returns {*[]}
         */
        renderCheckbox: function renderCheckbox(opts) {

            /**
             * Create UUID
             * @type {String}
             */
            var uuid = this.base.lib.generator.UUID() + '-checkbox',
                checked = this.base.defineBoolean(opts.checked, false, true);

            /**
             * Define $input
             * @type {*|jQuery}
             */
            var $input = $('<input />').attr({
                name: opts.name,
                type: 'checkbox',
                id: uuid,
                title: opts.value,
                checked: checked,
                disabled: this.base.defineBoolean(opts.disabled, false, true)
            }).val(opts.value);

            $input.prop(
                'checked',
                checked
            );

            if (opts.monitor) {

                $input.on(
                    opts.monitor.events.join(','),
                    opts.monitor.callback
                );
            }

            if (!opts.visible) {
                $input.hide();
            }

            return [
                $input,
                this.renderLabel(uuid, opts.text, 'text', opts.visible)
            ];
        },

        /**
         * Render text area
         * @member Renderer
         * @param {{text: string, name: string, [placeholder]: string, value, [disabled]: boolean}} opts
         * @returns {*[]}
         */
        renderTextArea: function renderTextArea(opts) {

            /**
             * Create UUID
             * @type {String}
             */
            var uuid = this.base.lib.generator.UUID() + '-textarea',
                $input;

            if (this.base.defineBoolean(opts.disabled, false, true)) {

                /**
                 * Define $input
                 * @type {*|jQuery}
                 */
                $input = $('<p />').attr({
                    name: opts.name,
                    id: uuid,
                    title: opts.value
                }).addClass('textarea').text(opts.value);

            } else {

                /**
                 * Define $input
                 * @type {*|jQuery}
                 */
                $input = $('<textarea />').attr({
                    name: opts.name,
                    id: uuid,
                    placeholder: opts.placeholder,
                    title: opts.value
                }).val(opts.value);
            }

            if (!opts.visible) {
                $input.hide();
            }

            return [
                this.renderLabel(uuid, opts.text, 'textarea', opts.visible),
                $input
            ];
        },

        /**
         * Render combo box
         * @member Renderer
         * @param {Array} data
         * @param selected
         * @param {string} name
         * @param {string} index
         * @param {{type: string, callback: function}} [event]
         * @param {boolean} [visible]
         */
        renderCombobox: function renderCombobox(data, selected, name, index, event, visible) {

            /**
             * Define container
             * @type {*|jQuery}
             */
            var $div = $('<div style="display: none;" />').addClass('combo-box').attr({
                id: this.base.lib.generator.UUID() + '-combobox'
            }).append(
                $('<input class="hidden" />').attr({
                    name: index,
                    disabled: true,
                    type: 'text',
                    value: selected
                })
            );

            /**
             * Open combo box
             * @private
             */
            function _open() {
                // close all como-boxes
                $('.combo-box').removeClass('open');
                $div.addClass('open');
            }

            /**
             * Hide combo box
             * @private
             */
            function _hide() {
                $div.removeClass('open');
            }

            /**
             * Store prefs
             * @param $selected
             * @param selected
             * @returns {boolean}
             * @private
             */
            function _store($selected, selected) {

                /**
                 * Define value
                 * @type {String}
                 */
                var value = $selected.text();

                if (value === selected) {
                    return false;
                }

                $('input[name="' + index + '"]', $div).val(value);
            }

            /**
             * Define $ul
             * @type {*|jQuery}
             */
            var $ul = $('<ul />');

            for (var i = 0, l = data.length; i < l; i++) {

                var field = data[i],
                    $li = $('<li />');

                if (field.type === 'text') {
                    $li.text(field.value);
                }

                if (field.type === 'html') {
                    $li.html(field.value);
                }

                if (field.type === 'field') {
                    $li.append(
                        this.renderTextField({
                            name: field.name,
                            placeholder: field.placeholder,
                            value: field.value,
                            disabled: field.disabled
                        })
                    );
                }

                if (selected === field.value) {
                    $li.addClass('selected');
                }

                $li.on(
                    'click.comboBoxInternal',

                    /**
                     * Select combo box item
                     * @param e
                     * @returns {boolean}
                     */
                    function comboBoxInternalEvent(e) {

                        $div.hasClass('open') ?
                            _hide() : _open();

                        /**
                         * Define selected $li
                         * @type {*|jQuery|HTMLElement}
                         */
                        var $selected = $(e.target);

                        if ($selected.hasClass('selected')) {
                            _store($selected, selected);
                            return false;
                        }

                        $('li', $selected.parent()).removeClass('selected');
                        $selected.addClass('selected');

                        _store($selected, selected);

                    }.bind(this)
                );

                // hide on mouse leave
                $ul.on('mouseleave.comboBoxInternal', _hide);

                if (this.base.isDefined(event)) {
                    if (this.base.isFunction(event.callback)) {
                        $li.on(event.type, function comboBoxEvent(e) {
                            event.callback($(e.target).attr('rel'));
                        });
                    }
                }

                $li.attr({rel: field.key || field.value}).appendTo($ul);
            }

            // fix to define modal dialog height
            setTimeout(function () {
                visible ? $div.show() : $div.hide();
            }, 500);

            return [
                this.renderLabel(undefined, name, undefined, visible),
                $div.append([
                    $ul,
                    $('<div />').addClass('combo-box-arrow')
                ])
            ];
        }

    }, AntHill.prototype);
});<|endoftext|>


<commit_before>'use strict';

import React              from 'react';
import ReduxUtil          from '../util/ReduxUtil';
import colors             from 'material-ui/lib/styles/colors';
import Paper              from 'material-ui/lib/paper';
import Table              from 'material-ui/lib/table/table';
import TableBody          from 'material-ui/lib/table/table-body';
import TableRow           from 'material-ui/lib/table/table-row';
import TableRowColumn     from 'material-ui/lib/table/table-row-column';
import Avatar             from 'material-ui/lib/avatar';
import FontIcon           from 'material-ui/lib/font-icon';
import CircularProgress   from 'material-ui/lib/circular-progress';
import autoBindAllMethods from '../../shared/util/autoBindAllMethods';
import {AC}               from '../state/actions';

// FYI: following used to add AppBar to Students page
import AppBar             from 'material-ui/lib/app-bar';
import IconMenu           from 'material-ui/lib/menus/icon-menu';
import IconButton         from 'material-ui/lib/icon-button';
import MoreVertIcon       from 'material-ui/lib/svg-icons/navigation/more-vert';
import MenuItem           from 'material-ui/lib/menus/menu-item';

/**
 * The Students component displays a list of students.
 */
const Students = ReduxUtil.wrapCompWithInjectedProps(

  class Students extends React.Component { // component definition
    constructor(props, context) {
      super(props, context);
      autoBindAllMethods(this);
    }

    render() {
      const { inProgress, students, selectedStudent, studentsShown, selectStudentFn } = this.props;

      // ?? use this in
 to show detail student
      //    ... onCellClick={this.showStudent}

      const myStyle = {
        margin:    '15px auto', // 15px spacing top/bottom, center left/right
        textAlign: 'left',
        width:     '97%',       // ColWidth: HONORED (adding to inline
 style),
        // 'auto' has NO impact
        // '90%' is honored
        // 'max-content'/'fit-content' works on chrome NOT IE
        // 'available' still big
        // ... can't even read/understand code: node_modules/material-ui/lib/paper.js
      };
      // we actually hide the students if NOT displayed as an attempted optimization for large list
      // ... doesn't seem to help - in fact it even takes longer to take it down ... hmmmm
      // >>> one side-benefit is that we retain scrolling state from previous renderings
      if (!studentsShown) {
        myStyle.display = 'none';
      }

      const inProgressIndicator =  inProgress ? <CircularProgress size={0.3} color={colors.white}/> : ;

      return <Paper className="app-content"
                    style={myStyle}
                    zDepth={4}>

        <Paper className="page"
               style={{
                 textAlign: 'left',
                 width:     '100%',
               }}
               zDepth={1}>

          <AppBar className="page-header"
                  style={{
                    backgroundColor: colors.blueGrey700, // also like lime900
                  }}
                  title={Students{inProgressIndicator}}
                  iconElementLeft={}
                  iconElementRight={
                    <IconMenu iconButtonElement={ <IconButton><MoreVertIcon/></IconButton> }
                              targetOrigin={{vertical: 'top', horizontal: 'right', }}
                              anchorOrigin={{vertical: 'top', horizontal: 'right'}}>
                      <MenuItem primaryText="Select"/>
                      <MenuItem primaryText="Filter"/>
                      <MenuItem primaryText="... more"/>
                    </IconMenu>}/>

selectStudentFn(selectedRows.length===0 ? null : students[selectedRows[0]])}
                 style={{
                     width: 'auto', // ColWidth: HONORED at this level and changes table width (from 'fixed')
                   }}>
            <TableBody deselectOnClickaway={false}
                       displayRowCheckbox={false}
                       showRowHover={true}
                       stripedRows={false}>
              {/* Alternative to Avatar (may be too expensive for a LARGE list)
              <Avatar src={`https://robohash.org/${student.firstName+student.lastName}.bmp?size=100x100&set=set2&bgset=any`} size={20}/>
              - vs -
              <FontIcon className="material-icons" color={colors.blue900}>face</FontIcon>
              <FontIcon className="material-icons" color={colors.blue900}>person</FontIcon>
              */}
              {students.map( (student, indx) => (
              indx > 100 ? '' : // TODO: ??? temporally narrow entries till we figure out how to handle big lists or make them unneeded
              <TableRow key={student.studentNum}
                        selected={student===selectedStudent}>
                <TableRowColumn>
                  {
                  student.gender==='M'
                  ? <FontIcon className="material-icons" color={colors.blue900}>person</FontIcon>
                  : <FontIcon className="material-icons" color={colors.pink300}>person</FontIcon>
                  }
                  {' ' + student.firstName + ' ' + student.lastName}
                  {` (${student.studentNum})`}
                </TableRowColumn>
                <TableRowColumn>{student.degree}</TableRowColumn>
                <TableRowColumn>{student.graduation || ''}</TableRowColumn>
                <TableRowColumn>GPA: {student.gpa}</TableRowColumn>
                <TableRowColumn>birth: {student.birthday}</TableRowColumn>
              </TableRow>
              ))}
            </TableBody>

        </Paper>
      </Paper>
    }
  }, // end of ... component definition

  { // component property injection
    mapStateToProps(appState, ownProps) {
      return {
        inProgress:      appState.students.inProgress ? true : false,
        students:        appState.students.items,
        selectedStudent: appState.students.selectedStudent,
        studentsShown:   appState.mainPage==='students',
      }
    },
    mapDispatchToProps(dispatch, ownProps) {
      return {
        selectStudentFn: (student) => { dispatch( AC.selectStudent(student) )},
      }
    }
  }); // end of ... component property injection

// define expected props
Students.propTypes = {
  selectStudentFn: React.PropTypes.func, // .isRequired - injected via self's wrapper
}

export default Students;
<commit_msg>attempted to get in-progress animation working better - Material UI is very limited/buggy<commit_after>'use strict';

import React              from 'react';
import ReduxUtil          from '../util/ReduxUtil';
import colors             from 'material-ui/lib/styles/colors';
import Paper              from 'material-ui/lib/paper';
import Table              from 'material-ui/lib/table/table';
import TableBody          from 'material-ui/lib/table/table-body';
import TableRow           from 'material-ui/lib/table/table-row';
import TableRowColumn     from 'material-ui/lib/table/table-row-column';
import Avatar             from 'material-ui/lib/avatar';
import FontIcon           from 'material-ui/lib/font-icon';
import RefreshIndicator   from 'material-ui/lib/refresh-indicator';
import autoBindAllMethods from '../../shared/util/autoBindAllMethods';
import {AC}               from '../state/actions';

// FYI: following used to add AppBar to Students page
import AppBar             from 'material-ui/lib/app-bar';
import IconMenu           from 'material-ui/lib/menus/icon-menu';
import IconButton         from 'material-ui/lib/icon-button';
import MoreVertIcon       from 'material-ui/lib/svg-icons/navigation/more-vert';
import MenuItem           from 'material-ui/lib/menus/menu-item';

/**
 * The Students component displays a list of students.
 */
const Students = ReduxUtil.wrapCompWithInjectedProps(

  class Students extends React.Component { // component definition
    constructor(props, context) {
      super(props, context);
      autoBindAllMethods(this);
    }

    render() {
      const { inProgress, students, selectedStudent, studentsShown, selectStudentFn } = this.props;

      // ?? use this in
 to show detail student
      //    ... onCellClick={this.showStudent}

      const myStyle = {
        margin:    '15px auto', // 15px spacing top/bottom, center left/right
        textAlign: 'left',
        width:     '97%',       // ColWidth: HONORED (adding to inline
 style),
        // 'auto' has NO impact
        // '90%' is honored
        // 'max-content'/'fit-content' works on chrome NOT IE
        // 'available' still big
        // ... can't even read/understand code: node_modules/material-ui/lib/paper.js
      };
      // we actually hide the students if NOT displayed as an attempted optimization for large list
      // ... doesn't seem to help - in fact it even takes longer to take it down ... hmmmm
      // >>> one side-benefit is that we retain scrolling state from previous renderings
      if (!studentsShown) {
        myStyle.display = 'none';
      }

      // NOTE: Our retrieval is SO FAST, I am using a determinate mode because the animation is starting so small there is no time to see it
      // NOTE: <CircularProgress> is VERY HOAKY ... insists on 50px by 50px outer rectangle (NO WAY TO DECREASE)
      //       const inProgressIndicator =  inProgress ? <CircularProgress size={0.3} color={colors.white}/> : ;
      // NOTE: <RefreshIndicator> is NOT MUCH BETTER ... any size under 50 has mucho transformation problems
      const refreshInd = <RefreshIndicator size={50}
                                           top={10}
                                           left={60}
                                           percentage={80}
                                           color={"red"}
                                           status="ready"
                                           style={{display:  'inline-block',
                                                   position: 'relative'}}/>
      const inProgressIndicator =  inProgress ? refreshInd : ;

      return <Paper className="app-content"
                    style={myStyle}
                    zDepth={4}>

        <Paper className="page"
               style={{
                 textAlign: 'left',
                 width:     '100%',
               }}
               zDepth={1}>

          <AppBar className="page-header"
                  style={{
                    backgroundColor: colors.blueGrey700, // also like lime900
                  }}
                  title={Students{inProgressIndicator}}
                  iconElementLeft={}
                  iconElementRight={
                    <IconMenu iconButtonElement={ <IconButton><MoreVertIcon/></IconButton> }
                              targetOrigin={{vertical: 'top', horizontal: 'right', }}
                              anchorOrigin={{vertical: 'top', horizontal: 'right'}}>
                      <MenuItem primaryText="Select"/>
                      <MenuItem primaryText="Filter"/>
                      <MenuItem primaryText="... more"/>
                    </IconMenu>}/>

selectStudentFn(selectedRows.length===0 ? null : students[selectedRows[0]])}
                 style={{
                     width: 'auto', // ColWidth: HONORED at this level and changes table width (from 'fixed')
                   }}>
            <TableBody deselectOnClickaway={false}
                       displayRowCheckbox={false}
                       showRowHover={true}
                       stripedRows={false}>
              {/* Alternative to Avatar (may be too expensive for a LARGE list)
              <Avatar src={`https://robohash.org/${student.firstName+student.lastName}.bmp?size=100x100&set=set2&bgset=any`} size={20}/>
              - vs -
              <FontIcon className="material-icons" color={colors.blue900}>face</FontIcon>
              <FontIcon className="material-icons" color={colors.blue900}>person</FontIcon>
              */}
              {students.map( (student, indx) => (
              indx > 100 ? '' : // TODO: ??? temporally narrow entries till we figure out how to handle big lists or make them unneeded
              <TableRow key={student.studentNum}
                        selected={student===selectedStudent}>
                <TableRowColumn>
                  {
                  student.gender==='M'
                  ? <FontIcon className="material-icons" color={colors.blue900}>person</FontIcon>
                  : <FontIcon className="material-icons" color={colors.pink300}>person</FontIcon>
                  }
                  {' ' + student.firstName + ' ' + student.lastName}
                  {` (${student.studentNum})`}
                </TableRowColumn>
                <TableRowColumn>{student.degree}</TableRowColumn>
                <TableRowColumn>{student.graduation || ''}</TableRowColumn>
                <TableRowColumn>GPA: {student.gpa}</TableRowColumn>
                <TableRowColumn>birth: {student.birthday}</TableRowColumn>
              </TableRow>
              ))}
            </TableBody>

        </Paper>
      </Paper>
    }
  }, // end of ... component definition

  { // component property injection
    mapStateToProps(appState, ownProps) {
      return {
        inProgress:      appState.students.inProgress ? true : false,
        students:        appState.students.items,
        selectedStudent: appState.students.selectedStudent,
        studentsShown:   appState.mainPage==='students',
      }
    },
    mapDispatchToProps(dispatch, ownProps) {
      return {
        selectStudentFn: (student) => { dispatch( AC.selectStudent(student) )},
      }
    }
  }); // end of ... component property injection

// define expected props
Students.propTypes = {
  selectStudentFn: React.PropTypes.func, // .isRequired - injected via self's wrapper
}

export default Students;
<|endoftext|>


<commit_before>module.exports = {
  main: {
    files: [
      { expand: true, flatten: true, src: ['<%= src.lib %>/*.js'], dest: 'dist/' }
    ]
  }
}
<commit_msg>copy ember-oauth2.js es6 file to the dist dir<commit_after>module.exports = {
  main: {
    files: [
      { expand: true, flatten: true, src: ['<%= lib %>/*.js'], dest: 'dist/' }
    ]
  }
}
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Vola Admin/IP Tools
// @version      45
// @description  Does a bunch of stuff for mods.
// @namespace    https://volafile.org
// @icon         https://volafile.org/favicon.ico
// @author       topkuk productions
// @match        https://volafile.org/r/*
// @require      https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @require      https://cdn.rawgit.com/RealDolos/volascripts/51b76c05be26adca7b4a4897115f67f10d9df668/dry.js
// @run-at       document-start
// @grant        none
// ==/UserScript==

/* global dry, GM */

dry.once("dom", () => {
  "use strict";

  const IS_BAN = /\b(?:banned|muted|hellbanned|unbanned|un-muted|timed-in|did nothing to)\b/;

  function adjustBanPart(p, users, ips) {
    const {value} = p;
    if (!IS_BAN.test(value)) {
      return;
    }
    let slash = value.indexOf(" / ");
    if (slash < 0) {
      slash = value.length;
    }
    const pre = value.slice(0, slash).replace("did nothing to", "did-nothing-to");
    const post = value.slice(slash);
    const pieces = pre.split(/ /g);
    let idx = 1;
    while (idx < pieces.length) {
      if (!pieces[idx++].endsWith(",")) {
        break;
      }
    }
    const pusers = pieces.slice(idx).map(u => {
      u = u.replace(/(?:[()]|,$)/g, "").trim();
      if (/^\d+\.\d+\.\d+\.\d+$/.test(u)) {
        ips.push(u);
        return null;
      }
      if (u) {
        users.push(u);
      }
      return u;
    }).filter(e => e).sort();

    if (!pusers.length) {
      pusers.push("some cuck");
    }

    p.value = `${pieces.slice(0, idx).join(" ").replace("did-nothing-to", "did nothing to")} ${pusers.join(", ")}${post}`;
  }

  function $e(tag, attrs, text) {
    let rv = document.createElement(tag);
    attrs = attrs || {};
    for (let a in attrs) {
        rv.setAttribute(a, attrs[a]);
    }
    if (text) {
        rv.textContent = text;
    }
    return rv;
  }

  const $ = (sel, el) => (el || document).querySelector(sel);

  console.log(
    "running", GM.info.script.name, GM.info.script.version, dry.version);

  const style = $e("style", {id: "iptools-style"}, `
body[noipspls] a.username > span:not([class]),
body[noipspls] a.username > span[class=""],
body[noipspls] .tag_key_ip {
  display: none;
}
.username.ban {
  display: inline-block;
}
@-moz-document url-prefix() {
.username.ban {
  display: inline;
}
}
.username.ban {
  vertical-align: top;
  color: white !important;
  font-size: 50%;
  padding: 0;
  opacity: 0.4;
}
.username.ban:hover {
  opacity: 1;
}
.username.ban icon-hammer {
  padding: 0;
}
.icon-untick {
  margin: 0 !important;
}
.untick-button {
  position: relative;
  z-index: 150;
  font-size: 18px;
  padding-bottom: 1px;
  margin-right: 1ex;
}
`);
  document.body.appendChild(style);
  let state = localStorage.getItem("noipspls") !== "false";
  if (state !== false) {
    state = true;
  }
  const update = () => {
    if (state) {
      document.body.setAttribute("noipspls", "true");
    }
    else {
      document.body.removeAttribute("noipspls");
    }
    localStorage.setItem("noipspls", state.toString());
    if (dry.exts) {
      dry.exts.chat.scrollState.bottom();
    }
  };
  const toggle = () => {
    state = !state;
    update();
  };
  update();

  const btn = document.createElement("a");
  btn.textContent = "IP";
  btn.style.padding = "0 1ex";
  const uc = document.querySelector("#user_count_icon");
  uc.parentElement.insertBefore(btn, uc.nextSibling);
  btn.addEventListener("click", toggle);

  const roomqueue = new Map();
  let resolving = false;
  const resolve_rooms = async () => {
    if (resolving) {
      return;
    }
    resolving = true;
    try {
      while (roomqueue.size) {
        const [room, elems] = roomqueue[Symbol.iterator]().next().value;
        try {
          let res = await new Promise((resolve, reject) => {
            dry.unsafeWindow.Volafile.makeAPIRequest("getRoomConfig", {
              room
            }, (err, res) => err || !res || !res.name ? reject(err) : resolve(res));
          });
          for (const el of elems) {
            el.textContent = res.name;
          }
        }
        catch (ex) {
          console.error(ex);
        }
        roomqueue.delete(room);
      }
    }
    finally {
      resolving = false;
    }
  };

  dry.replaceEarly("chat", "showMessage",
    function(orig, nick, message, options, data, ...args) {
      try {
        if (nick === "Log" && options.staff && !options.profile && !(data && data.ip)) {
          const users = [];
          const ips = [];
          if (message[0].type === "url" && message[0].href === "/reports") {
            if (message[1].value.startsWith(" / BLACKLIST ")) {
              adjustBanPart(message[4], users, ips);
            }
            else {
              const [, p] = message;
              const m = p.value.match(/ \((\d+\.\d+\.\d+\.\d+)\)/);
              if (m) {
                p.value = p.value.replace(m[0], "").trim();
                ips.push(m[1]);
              }
            }
          }
          else {
            for (const p of message) {
              if (p.type !== "text") {
                continue;
              }
              adjustBanPart(p, users, ips);
            }
          }
          if (users.length) {
            options.profile = users.sort().join(" user:");
            options.unprofile = true;
          }
          if (ips.length) {
            if (!data) {
              data = new dry.unsafeWindow.Object();
            }
            data.ip = ips.join(" ip:");
          }
        }
      }
      catch (ex) {
        console.error(ex);
      }
      const msg = orig(...[nick, message, options, data].concat(args));
      try {
        if (options.unprofile && msg.nick_elem) {
          const newnick = document.createElement("a");
          if (msg.ip_elem) {
            msg.ip_elem.parentElement.removeChild(msg.ip_elem);
          }
          newnick.textContent = msg.nick_elem.textContent;
          if (msg.ip_elem) {
            newnick.appendChild(msg.ip_elem);
          }
          newnick.className = msg.nick_elem.className;
          msg.nick_elem.parentElement.replaceChild(newnick, msg.nick_elem);
          msg.nick_elem = newnick;
          msg.elem.classList.remove("profile");
        }
        if (msg && (msg.ip_elem || msg.options.profile)) {
          msg.ban_elem = document.createElement("span");
          const hammer = document.createElement("i");
          hammer.setAttribute("class", "chat_message_icon icon-hammer");
          msg.ban_elem.appendChild(hammer);
          msg.ban_elem.setAttribute("class", "username clickable ban");
          msg.ban_elem.addEventListener("click", msg.showBanWindow.bind(msg));
          msg.nick_elem.appendChild(msg.ban_elem);
        }
        if (msg && nick === "Log" && msg.elem) {
          for (const el of msg.elem.children) {
            if (!el.textContent.startsWith("#")) {
              continue;
            }
            let m = /https:\/\/volafile.org\/r\/(.+)$/.exec(el.href);
            if (m) {
              const l = roomqueue.get(m[1]);
              if (!l) {
                roomqueue.set(m[1], [el]);
              }
              else {
                l.push(el);
              }
            }
          }
        }
      }
      catch (ex) {
        console.error(ex);
      }
      finally {
        resolve_rooms().catch(console.error);
      }
      return msg;
    });

  new class extends dry.Commands {
    ip() {
      toggle();
      return true;
    }
  }();

  function nukeRoom() {
    dry.exts.ui.showQuestion({
      title: "Disable this room",
      text: "Are you sure you want to disable this room?",
      positive: "Disable",
      negative: "Abort"
    }, res => {
      if (!res) {
        return;
      }
      dry.exts.connection.call("editInfo", {
        name: "closed",
        motd: "",
        disabled: true
      });
    });
  }

  dry.replaceEarly("ui", "showContextMenu", function(orig, el, options) {
    try {
      if (options && options.dedupe === "admin_contextmenu" && dry.exts.admin.isAdmin) {
        options.buttons.push({
          icon: "icon-rules",
          text: "Nuke Room",
          admin: true,
          click: nukeRoom
        });
      }
      if (options && options.dedupe === "room_contextmenu" && dry.exts.admin.isAdmin) {
        const idx = options.buttons.findIndex(e => e.text === "Copy URL");
        if (idx >= 0) {
          options.buttons.splice(idx, 1);
        }
      }
    }
    catch (ex) {
      console.error("ex", ex);
    }
    return orig(el, options);
  });

  // fixup ban templates
  for (const b of Object.values(window._templates.bans)) {
    b.lock = b.locked = false;
     if (b.upload && b.hours <= 24) {
       b.upload = false;
     }
  }

  const doet = Symbol("doet");
  const cont = $("#upload_container");
  const button = $e("label", {
    "for": "untick-button",
    "id": "untick-button",
    "class": "button untick-button",
    "title": "Untick all!"
  });
  button.appendChild($e("span", {
    "class": "icon-minus"
  }));
  button.addEventListener("click", () => dry.exts.adminButtons.untickAll(doet));
  cont.insertBefore(button, cont.firstChild);

  dry.replaceEarly("adminButtons", "untickAll", function(orig, kek) {
    if (kek !== doet) {
      return;
    }
    return orig();
  });
});
<commit_msg>Blacklisted!<commit_after>// ==UserScript==
// @name         Vola Admin/IP Tools
// @version      46
// @description  Does a bunch of stuff for mods.
// @namespace    https://volafile.org
// @icon         https://volafile.org/favicon.ico
// @author       topkuk productions
// @match        https://volafile.org/r/*
// @require      https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @require      https://cdn.rawgit.com/RealDolos/volascripts/51b76c05be26adca7b4a4897115f67f10d9df668/dry.js
// @run-at       document-start
// @grant        none
// ==/UserScript==

/* global dry, GM */

dry.once("dom", () => {
  "use strict";

  const IS_BAN = /^\S+ (?:banned|muted|hellbanned|unbanned|un-muted|timed-in)\b/;

  function adjustBanPart(p, users, ips) {
    const {value} = p;
    let slash = value.indexOf(" / ");
    if (slash < 0) {
      slash = value.length;
    }
    const pre = value.slice(0, slash).replace("did nothing to", "did-nothing-to");
    const post = value.slice(slash);
    const pieces = pre.split(/ /g);
    let idx = 1;
    while (idx < pieces.length) {
      if (!pieces[idx++].endsWith(",")) {
        break;
      }
    }
    const pusers = pieces.slice(idx).map(u => {
      u = u.replace(/(?:[()]|,$)/g, "").trim();
      if (/^\d+\.\d+\.\d+\.\d+$/.test(u)) {
        ips.push(u);
        return null;
      }
      if (u) {
        users.push(u);
      }
      return u;
    }).filter(e => e).sort();

    if (!pusers.length) {
      pusers.push("some cuck");
    }

    p.value = `${pieces.slice(0, idx).join(" ").replace("did-nothing-to", "did nothing to")} ${pusers.join(", ")}${post}`;
  }

  function $e(tag, attrs, text) {
    let rv = document.createElement(tag);
    attrs = attrs || {};
    for (let a in attrs) {
        rv.setAttribute(a, attrs[a]);
    }
    if (text) {
        rv.textContent = text;
    }
    return rv;
  }

  const $ = (sel, el) => (el || document).querySelector(sel);

  console.log(
    "running", GM.info.script.name, GM.info.script.version, dry.version);

  const style = $e("style", {id: "iptools-style"}, `
body[noipspls] a.username > span:not([class]),
body[noipspls] a.username > span[class=""],
body[noipspls] .tag_key_ip {
  display: none;
}
.username.ban {
  display: inline-block;
}
@-moz-document url-prefix() {
.username.ban {
  display: inline;
}
}
.username.ban {
  vertical-align: top;
  color: white !important;
  font-size: 50%;
  padding: 0;
  opacity: 0.4;
}
.username.ban:hover {
  opacity: 1;
}
.username.ban icon-hammer {
  padding: 0;
}
.icon-untick {
  margin: 0 !important;
}
.untick-button {
  position: relative;
  z-index: 150;
  font-size: 18px;
  padding-bottom: 1px;
  margin-right: 1ex;
}
`);
  document.body.appendChild(style);
  let state = localStorage.getItem("noipspls") !== "false";
  if (state !== false) {
    state = true;
  }
  const update = () => {
    if (state) {
      document.body.setAttribute("noipspls", "true");
    }
    else {
      document.body.removeAttribute("noipspls");
    }
    localStorage.setItem("noipspls", state.toString());
    if (dry.exts) {
      dry.exts.chat.scrollState.bottom();
    }
  };
  const toggle = () => {
    state = !state;
    update();
  };
  update();

  const btn = document.createElement("a");
  btn.textContent = "IP";
  btn.style.padding = "0 1ex";
  const uc = document.querySelector("#user_count_icon");
  uc.parentElement.insertBefore(btn, uc.nextSibling);
  btn.addEventListener("click", toggle);

  const roomqueue = new Map();
  let resolving = false;
  const resolve_rooms = async () => {
    if (resolving) {
      return;
    }
    resolving = true;
    try {
      while (roomqueue.size) {
        const [room, elems] = roomqueue[Symbol.iterator]().next().value;
        try {
          let res = await new Promise((resolve, reject) => {
            dry.unsafeWindow.Volafile.makeAPIRequest("getRoomConfig", {
              room
            }, (err, res) => err || !res || !res.name ? reject(err) : resolve(res));
          });
          for (const el of elems) {
            el.textContent = res.name;
          }
        }
        catch (ex) {
          console.error(ex);
        }
        roomqueue.delete(room);
      }
    }
    finally {
      resolving = false;
    }
  };

  dry.replaceEarly("chat", "showMessage",
    function(orig, nick, message, options, data, ...args) {
      try {
        if (nick === "Log" && options.staff && !options.profile && !(data && data.ip)) {
          const users = [];
          const ips = [];
          if (message[0].type === "url" && message[0].href === "/reports") {
            if (message[1].value.startsWith(" / BLACKLIST ")) {
              adjustBanPart(message[4], users, ips);
            }
            else {
              const [, p] = message;
              const m = p.value.match(/ \((\d+\.\d+\.\d+\.\d+)\)/);
              if (m) {
                p.value = p.value.replace(m[0], "").trim();
                ips.push(m[1]);
              }
            }
          }
          else {
            for (const p of message) {
              if (p.type !== "text") {
                continue;
              }
              if (!IS_BAN.test(p.value)) {
                continue;
              }

              adjustBanPart(p, users, ips);
            }
          }
          if (users.length) {
            options.profile = users.sort().join(" user:");
            options.unprofile = true;
          }
          if (ips.length) {
            if (!data) {
              data = new dry.unsafeWindow.Object();
            }
            data.ip = ips.join(" ip:");
          }
        }
      }
      catch (ex) {
        console.error(ex);
      }
      const msg = orig(...[nick, message, options, data].concat(args));
      try {
        if (options.unprofile && msg.nick_elem) {
          const newnick = document.createElement("a");
          if (msg.ip_elem) {
            msg.ip_elem.parentElement.removeChild(msg.ip_elem);
          }
          newnick.textContent = msg.nick_elem.textContent;
          if (msg.ip_elem) {
            newnick.appendChild(msg.ip_elem);
          }
          newnick.className = msg.nick_elem.className;
          msg.nick_elem.parentElement.replaceChild(newnick, msg.nick_elem);
          msg.nick_elem = newnick;
          msg.elem.classList.remove("profile");
        }
        if (msg && (msg.ip_elem || msg.options.profile)) {
          msg.ban_elem = document.createElement("span");
          const hammer = document.createElement("i");
          hammer.setAttribute("class", "chat_message_icon icon-hammer");
          msg.ban_elem.appendChild(hammer);
          msg.ban_elem.setAttribute("class", "username clickable ban");
          msg.ban_elem.addEventListener("click", msg.showBanWindow.bind(msg));
          msg.nick_elem.appendChild(msg.ban_elem);
        }
        if (msg && nick === "Log" && msg.elem) {
          for (const el of msg.elem.children) {
            if (!el.textContent.startsWith("#")) {
              continue;
            }
            let m = /https:\/\/volafile.org\/r\/(.+)$/.exec(el.href);
            if (m) {
              const l = roomqueue.get(m[1]);
              if (!l) {
                roomqueue.set(m[1], [el]);
              }
              else {
                l.push(el);
              }
            }
          }
        }
      }
      catch (ex) {
        console.error(ex);
      }
      finally {
        resolve_rooms().catch(console.error);
      }
      return msg;
    });

  new class extends dry.Commands {
    ip() {
      toggle();
      return true;
    }
  }();

  function nukeRoom() {
    dry.exts.ui.showQuestion({
      title: "Disable this room",
      text: "Are you sure you want to disable this room?",
      positive: "Disable",
      negative: "Abort"
    }, res => {
      if (!res) {
        return;
      }
      dry.exts.connection.call("editInfo", {
        name: "closed",
        motd: "",
        disabled: true
      });
    });
  }

  dry.replaceEarly("ui", "showContextMenu", function(orig, el, options) {
    try {
      if (options && options.dedupe === "admin_contextmenu" && dry.exts.admin.isAdmin) {
        options.buttons.push({
          icon: "icon-rules",
          text: "Nuke Room",
          admin: true,
          click: nukeRoom
        });
      }
      if (options && options.dedupe === "room_contextmenu" && dry.exts.admin.isAdmin) {
        const idx = options.buttons.findIndex(e => e.text === "Copy URL");
        if (idx >= 0) {
          options.buttons.splice(idx, 1);
        }
      }
    }
    catch (ex) {
      console.error("ex", ex);
    }
    return orig(el, options);
  });

  // fixup ban templates
  for (const b of Object.values(window._templates.bans)) {
    b.lock = b.locked = false;
     if (b.upload && b.hours <= 24) {
       b.upload = false;
     }
  }

  const doet = Symbol("doet");
  const cont = $("#upload_container");
  const button = $e("label", {
    "for": "untick-button",
    "id": "untick-button",
    "class": "button untick-button",
    "title": "Untick all!"
  });
  button.appendChild($e("span", {
    "class": "icon-minus"
  }));
  button.addEventListener("click", () => dry.exts.adminButtons.untickAll(doet));
  cont.insertBefore(button, cont.firstChild);

  dry.replaceEarly("adminButtons", "untickAll", function(orig, kek) {
    if (kek !== doet) {
      return;
    }
    return orig();
  });
});
<|endoftext|>