<commit_before>/**
 * @description
 * 简单上传:点击按钮,直接选择文件上传
 * @author Jinqn
 * @date 2014-03-31
 */
UE.plugin.register('simpleupload', function (){
    var me = this,
        isLoaded = false,
        containerBtn;

    function initUploadBtn(){
        var w = containerBtn.offsetWidth || 20,
            h = containerBtn.offsetHeight || 20,
            btnIframe = document.createElement('iframe'),
            btnStyle = 'display:block;width:' + w + 'px;height:' + h + 'px;overflow:hidden;border:0;margin:0;padding:0;position:absolute;top:0;left:0;filter:alpha(opacity=0);-moz-opacity:0;-khtml-opacity: 0;opacity: 0;cursor:pointer;';

        domUtils.on(btnIframe, 'load', function(){

            var timestrap = (+new Date()).toString(36),
                wrapper,
                btnIframeDoc,
                btnIframeBody;

            btnIframeDoc = (btnIframe.contentDocument || btnIframe.contentWindow.document);
            btnIframeBody = btnIframeDoc.body;
            wrapper = btnIframeDoc.createElement('div');

            wrapper.innerHTML = '<form id="edui_form_' + timestrap + '" target="edui_iframe_' + timestrap + '" method="POST" enctype="multipart/form-data" action="' + me.getOpt('serverUrl') + '" ' +
            'style="' + btnStyle + '">' +
            '<input id="edui_input_' + timestrap + '" type="file" accept="image/*" name="' + me.options.imageFieldName + '" ' +
            'style="' + btnStyle + '">' +
            '</form>' +
            '';

            wrapper.className = 'edui-' + me.options.theme;
            wrapper.id = me.ui.id + '_iframeupload';
            btnIframeBody.style.cssText = btnStyle;
            btnIframeBody.style.width = w + 'px';
            btnIframeBody.style.height = h + 'px';
            btnIframeBody.appendChild(wrapper);

            if (btnIframeBody.parentNode) {
                btnIframeBody.parentNode.style.width = w + 'px';
                btnIframeBody.parentNode.style.height = w + 'px';
            }

            var form = btnIframeDoc.getElementById('edui_form_' + timestrap);
            var input = btnIframeDoc.getElementById('edui_input_' + timestrap);
            var iframe = btnIframeDoc.getElementById('edui_iframe_' + timestrap);

            domUtils.on(input, 'change', function(){
                if(!input.value) return;
                var loadingId = 'loading_' + (+new Date()).toString(36);
                var params = utils.serializeParam(me.queryCommandValue('serverparam')) || '';

                var imageActionUrl = me.getActionUrl(me.getOpt('imageActionName'));
                var allowFiles = me.getOpt('imageAllowFiles');

                me.focus();
                me.execCommand('inserthtml', '');

                function callback(){
                    try{
                        var link, json, loader,
                            body = (iframe.contentDocument || iframe.contentWindow.document).body,
                            result = body.innerText || body.textContent || '';
                        json = (new Function("return " + result))();
                        link = me.options.imageUrlPrefix + json.url;
                        if(json.state == 'SUCCESS' && json.url) {
                            loader = me.document.getElementById(loadingId);
                            domUtils.removeClasses(loader, 'loadingclass');
                            loader.setAttribute('src', link);
                            loader.setAttribute('_src', link);
                            loader.setAttribute('alt', json.original || '');
                            loader.removeAttribute('id');
                        } else {
                            showErrorLoader && showErrorLoader(json.state);
                        }
                    }catch(er){
                        showErrorLoader && showErrorLoader(me.getLang('simpleupload.loadError'));
                    }
                    form.reset();
                    domUtils.un(iframe, 'load', callback);
                }
                function showErrorLoader(title){
                    if(loadingId) {
                        var loader = me.document.getElementById(loadingId);
                        loader && domUtils.remove(loader);
                        me.fireEvent('showmessage', {
                            'id': loadingId,
                            'content': title,
                            'type': 'error',
                            'timeout': 4000
                        });
                    }
                }

                /* 判断后端配置是否没有加载成功 */
                if (!me.getOpt('imageActionName')) {
                    errorHandler(me.getLang('autoupload.errorLoadConfig'));
                    return;
                }
                // 判断文件格式是否错误
                var filename = input.value,
                    fileext = filename ? filename.substr(filename.lastIndexOf('.')):'';
                if (!fileext || (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)) {
                    showErrorLoader(me.getLang('simpleupload.exceedTypeError'));
                    return;
                }

                domUtils.on(iframe, 'load', callback);
                form.action = utils.formatUrl(imageActionUrl + (imageActionUrl.indexOf('?') == -1 ? '?':'&') + params);
                form.submit();
            });

            var stateTimer;
            me.addListener('selectionchange', function () {
                clearTimeout(stateTimer);
                stateTimer = setTimeout(function() {
                    var state = me.queryCommandState('simpleupload');
                    if (state == -1) {
                        input.disabled = 'disabled';
                    } else {
                        input.disabled = false;
                    }
                }, 400);
            });
            isLoaded = true;
        });

        btnIframe.style.cssText = btnStyle;
        containerBtn.appendChild(btnIframe);
    }

    return {
        bindEvents:{
            'ready': function() {
                //设置loading的样式
                utils.cssRule('loading',
                    '.loadingclass{display:inline-block;cursor:default;background: url(\''
                    + this.options.themePath
                    + this.options.theme +'/images/loading.gif\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;}\n' +
                    '.loaderrorclass{display:inline-block;cursor:default;background: url(\''
                    + this.options.themePath
                    + this.options.theme +'/images/loaderror.png\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;' +
                    '}',
                    this.document);
            },
            /* 初始化简单上传按钮 */
            'simpleuploadbtnready': function(type, container) {
                containerBtn = container;
                me.afterConfigReady(initUploadBtn);
            }
        },
        outputRule: function(root){
            utils.each(root.getNodesByTagName('img'),function(n){
                if (/\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))) {
                    n.parentNode.removeChild(n);
                }
            });
        },
        commands: {
            'simpleupload': {
                queryCommandState: function () {
                    return isLoaded ? 0:-1;
                }
            }
        }
    }
});<commit_msg>修复图片上传后不会触发contentChange事件的bug<commit_after>/**
 * @description
 * 简单上传:点击按钮,直接选择文件上传
 * @author Jinqn
 * @date 2014-03-31
 */
UE.plugin.register('simpleupload', function (){
    var me = this,
        isLoaded = false,
        containerBtn;

    function initUploadBtn(){
        var w = containerBtn.offsetWidth || 20,
            h = containerBtn.offsetHeight || 20,
            btnIframe = document.createElement('iframe'),
            btnStyle = 'display:block;width:' + w + 'px;height:' + h + 'px;overflow:hidden;border:0;margin:0;padding:0;position:absolute;top:0;left:0;filter:alpha(opacity=0);-moz-opacity:0;-khtml-opacity: 0;opacity: 0;cursor:pointer;';

        domUtils.on(btnIframe, 'load', function(){

            var timestrap = (+new Date()).toString(36),
                wrapper,
                btnIframeDoc,
                btnIframeBody;

            btnIframeDoc = (btnIframe.contentDocument || btnIframe.contentWindow.document);
            btnIframeBody = btnIframeDoc.body;
            wrapper = btnIframeDoc.createElement('div');

            wrapper.innerHTML = '<form id="edui_form_' + timestrap + '" target="edui_iframe_' + timestrap + '" method="POST" enctype="multipart/form-data" action="' + me.getOpt('serverUrl') + '" ' +
            'style="' + btnStyle + '">' +
            '<input id="edui_input_' + timestrap + '" type="file" accept="image/*" name="' + me.options.imageFieldName + '" ' +
            'style="' + btnStyle + '">' +
            '</form>' +
            '';

            wrapper.className = 'edui-' + me.options.theme;
            wrapper.id = me.ui.id + '_iframeupload';
            btnIframeBody.style.cssText = btnStyle;
            btnIframeBody.style.width = w + 'px';
            btnIframeBody.style.height = h + 'px';
            btnIframeBody.appendChild(wrapper);

            if (btnIframeBody.parentNode) {
                btnIframeBody.parentNode.style.width = w + 'px';
                btnIframeBody.parentNode.style.height = w + 'px';
            }

            var form = btnIframeDoc.getElementById('edui_form_' + timestrap);
            var input = btnIframeDoc.getElementById('edui_input_' + timestrap);
            var iframe = btnIframeDoc.getElementById('edui_iframe_' + timestrap);

            domUtils.on(input, 'change', function(){
                if(!input.value) return;
                var loadingId = 'loading_' + (+new Date()).toString(36);
                var params = utils.serializeParam(me.queryCommandValue('serverparam')) || '';

                var imageActionUrl = me.getActionUrl(me.getOpt('imageActionName'));
                var allowFiles = me.getOpt('imageAllowFiles');

                me.focus();
                me.execCommand('inserthtml', '');

                function callback(){
                    try{
                        var link, json, loader,
                            body = (iframe.contentDocument || iframe.contentWindow.document).body,
                            result = body.innerText || body.textContent || '';
                        json = (new Function("return " + result))();
                        link = me.options.imageUrlPrefix + json.url;
                        if(json.state == 'SUCCESS' && json.url) {
                            loader = me.document.getElementById(loadingId);
                            domUtils.removeClasses(loader, 'loadingclass');
                            loader.setAttribute('src', link);
                            loader.setAttribute('_src', link);
                            loader.setAttribute('alt', json.original || '');
                            loader.removeAttribute('id');
                            me.fireEvent('contentChange');
                        } else {
                            showErrorLoader && showErrorLoader(json.state);
                        }
                    }catch(er){
                        showErrorLoader && showErrorLoader(me.getLang('simpleupload.loadError'));
                    }
                    form.reset();
                    domUtils.un(iframe, 'load', callback);
                }
                function showErrorLoader(title){
                    if(loadingId) {
                        var loader = me.document.getElementById(loadingId);
                        loader && domUtils.remove(loader);
                        me.fireEvent('showmessage', {
                            'id': loadingId,
                            'content': title,
                            'type': 'error',
                            'timeout': 4000
                        });
                    }
                }

                /* 判断后端配置是否没有加载成功 */
                if (!me.getOpt('imageActionName')) {
                    errorHandler(me.getLang('autoupload.errorLoadConfig'));
                    return;
                }
                // 判断文件格式是否错误
                var filename = input.value,
                    fileext = filename ? filename.substr(filename.lastIndexOf('.')):'';
                if (!fileext || (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)) {
                    showErrorLoader(me.getLang('simpleupload.exceedTypeError'));
                    return;
                }

                domUtils.on(iframe, 'load', callback);
                form.action = utils.formatUrl(imageActionUrl + (imageActionUrl.indexOf('?') == -1 ? '?':'&') + params);
                form.submit();
            });

            var stateTimer;
            me.addListener('selectionchange', function () {
                clearTimeout(stateTimer);
                stateTimer = setTimeout(function() {
                    var state = me.queryCommandState('simpleupload');
                    if (state == -1) {
                        input.disabled = 'disabled';
                    } else {
                        input.disabled = false;
                    }
                }, 400);
            });
            isLoaded = true;
        });

        btnIframe.style.cssText = btnStyle;
        containerBtn.appendChild(btnIframe);
    }

    return {
        bindEvents:{
            'ready': function() {
                //设置loading的样式
                utils.cssRule('loading',
                    '.loadingclass{display:inline-block;cursor:default;background: url(\''
                    + this.options.themePath
                    + this.options.theme +'/images/loading.gif\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;}\n' +
                    '.loaderrorclass{display:inline-block;cursor:default;background: url(\''
                    + this.options.themePath
                    + this.options.theme +'/images/loaderror.png\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;' +
                    '}',
                    this.document);
            },
            /* 初始化简单上传按钮 */
            'simpleuploadbtnready': function(type, container) {
                containerBtn = container;
                me.afterConfigReady(initUploadBtn);
            }
        },
        outputRule: function(root){
            utils.each(root.getNodesByTagName('img'),function(n){
                if (/\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))) {
                    n.parentNode.removeChild(n);
                }
            });
        },
        commands: {
            'simpleupload': {
                queryCommandState: function () {
                    return isLoaded ? 0:-1;
                }
            }
        }
    }
});<|endoftext|>


<commit_before>import SlackBot from 'slackbots'
import Slack from 'slack-node'
import Game from './Game'
import { GROUPS, CHANNELS } from './settings/gameSettings'
import _ from 'lodash'
import { EventEmitter } from 'events'

let main = {
  run: () => {
    let bot = new SlackBot({
      token: process.env.MAFIA_API_TOKEN,
      name: process.env.MAFIA_API_NAME
    })

    const webApi = new Slack(process.env.MAFIA_API_TOKEN)

    let game
    let gameInitiated = false
    let gamehasStarted = false

    let gameEmitter = new EventEmitter()

    function initGroups(botID) {
      webApi.api('groups.list', (err, response) => {
        _.forEach(response.groups, group => {
          if (_.indexOf(_.keys(GROUPS), group.name) > -1) {
            _.forEach(group.members, member => {
              if (member != botID)
                webApi.api('groups.kick', { channel: group.id, user: member })
            })
          }
        })
        const chans = _.map(response.groups, "name")
        _.forEach(_.keys(GROUPS), room => {
          if (_.indexOf(chans, room) == -1) {
            webApi.api('groups.create', { name: room })
          }
        })
      })
    }

    function initChannels(botID) {
      webApi.api('channels.list', (err, response) => {
        _.forEach(response.channels, channel => {
          if (_.indexOf(_.keys(CHANNELS), channel.name) > -1) {
            _.forEach(channel.members, member => {
              if (member != botID)
                webApi.api('channels.kick', { channel: channel.id, user: member })
            })
          }
        })
        const chans = _.map(response.channels, "name")
        _.forEach(_.keys(CHANNELS), room => {
          if (_.indexOf(chans, room) == -1) {
            webApi.api('channels.create', { name: room })
          }
        })
      })
    }

    function initializer(botID) {
      initChannels(botID)
      initGroups(botID)
      game = null
      gameInitiated = false
      gamehasStarted = false
    }

    bot.on('start', () => {
      const botID = bot.self.id
      webApi.botID = botID
      webApi.botIM = _.find(bot.ims, { user: botID })
        .id

      bot.on('message', data => {
        if (data.type == 'message' && data.text) {
          if (data.text.slice(0, 6) == '!mafia') {

            const command = _.lowerCase(data.text.slice(7))

            switch (command) {

              case 'init':
                initializer(botID)
                break;

              case 'newgame':
                bot.getGroups()
                  .then(dataGroups => bot.getChannels()
                    .then(dataChannels => {
                      var chans = []
                      _.forEach(dataGroups.groups, group => {
                        if (_.indexOf(_.keys(GROUPS), group.name) > -1) {
                          chans.push({ name: group.name, id: group.id, team: GROUPS[group.name] })
                        }
                      })
                      _.forEach(dataChannels.channels, channel => {
                        if (_.indexOf(_.keys(CHANNELS), channel.name) > -1) {
                          chans.push({ name: channel.name, id: channel.id, team: CHANNELS[channel.name] })
                        }
                      })
                      bot.getChannel(_.find(chans, o => o.team == 'town-room')
                          .name)
                        .then(data => {
                          bot.getUsers()
                            .then(dataUsers => {
                              var players = []
                              _.forEach(dataUsers.members, member => {
                                if (!member.is_bot && member.name != 'slackbot' && member.name != process.env.MAFIA_API_NAME) {
                                  if (_.indexOf(data.members, member.id) > -1)
                                    players.push({ id: member.id, name: member.name })
                                }
                              })
                              if (!gameInitiated) {
                                game = new Game(gameEmitter, webApi, players, chans)
                                game.init()
                                gameInitiated = true
                              }
                            })
                        })
                    }))
                break

              case 'start':
                if (gameInitiated && !gamehasStarted) {
                  game.start()
                  gamehasStarted = true
                }
                break

              case 'force-restart ' + process.env.MAFIA_PASSWORD:
                break

              default:
                break

            }
          }

        }
        if (data.type == 'message' && data.user != botID) {
          gameEmitter.emit('newMessage', data)
        }
      })

    })

  }
}

export default main
<commit_msg>fix: webApi not itialiazed yet<commit_after>import SlackBot from 'slackbots'
import Slack from 'slack-node'
import Game from './Game'
import { GROUPS, CHANNELS } from './settings/gameSettings'
import _ from 'lodash'
import { EventEmitter } from 'events'

let main = {
  run: () => {
    let bot = new SlackBot({
      token: process.env.MAFIA_API_TOKEN,
      name: process.env.MAFIA_API_NAME
    })

    const webApi = new Slack(process.env.MAFIA_API_TOKEN)

    let game
    let gameInitiated = false
    let gamehasStarted = false

    let gameEmitter = new EventEmitter()

    function initGroups(botID) {
      webApi.api('groups.list', (err, response) => {
        _.forEach(response.groups, group => {
          if (_.indexOf(_.keys(GROUPS), group.name) > -1) {
            _.forEach(group.members, member => {
              if (member != botID)
                webApi.api('groups.kick', { channel: group.id, user: member })
            })
          }
        })
        const chans = _.map(response.groups, "name")
        _.forEach(_.keys(GROUPS), room => {
          if (_.indexOf(chans, room) == -1) {
            webApi.api('groups.create', { name: room })
          }
        })
      })
    }

    function initChannels(botID) {
      webApi.api('channels.list', (err, response) => {
        _.forEach(response.channels, channel => {
          if (_.indexOf(_.keys(CHANNELS), channel.name) > -1) {
            _.forEach(channel.members, member => {
              if (member != botID)
                webApi.api('channels.kick', { channel: channel.id, user: member })
            })
          }
        })
        const chans = _.map(response.channels, "name")
        _.forEach(_.keys(CHANNELS), room => {
          if (_.indexOf(chans, room) == -1) {
            webApi.api('channels.create', { name: room })
          }
        })
      })
    }

    function initializer(botID) {
      webApi.botID = botID
      webApi.botIM = _.find(bot.ims, { user: botID })
        .id
      initChannels(botID)
      initGroups(botID)
      game = null
      gameInitiated = false
      gamehasStarted = false
    }

    bot.on('start', () => {
      const botID = bot.self.id

      bot.on('message', data => {
        if (data.type == 'message' && data.text) {
          if (data.text.slice(0, 6) == '!mafia') {

            const command = _.lowerCase(data.text.slice(7))

            switch (command) {

              case 'init':
                initializer(botID)
                break;

              case 'newgame':
                bot.getGroups()
                  .then(dataGroups => bot.getChannels()
                    .then(dataChannels => {
                      var chans = []
                      _.forEach(dataGroups.groups, group => {
                        if (_.indexOf(_.keys(GROUPS), group.name) > -1) {
                          chans.push({ name: group.name, id: group.id, team: GROUPS[group.name] })
                        }
                      })
                      _.forEach(dataChannels.channels, channel => {
                        if (_.indexOf(_.keys(CHANNELS), channel.name) > -1) {
                          chans.push({ name: channel.name, id: channel.id, team: CHANNELS[channel.name] })
                        }
                      })
                      bot.getChannel(_.find(chans, o => o.team == 'town-room')
                          .name)
                        .then(data => {
                          bot.getUsers()
                            .then(dataUsers => {
                              var players = []
                              _.forEach(dataUsers.members, member => {
                                if (!member.is_bot && member.name != 'slackbot' && member.name != process.env.MAFIA_API_NAME) {
                                  if (_.indexOf(data.members, member.id) > -1)
                                    players.push({ id: member.id, name: member.name })
                                }
                              })
                              if (!gameInitiated) {
                                game = new Game(gameEmitter, webApi, players, chans)
                                game.init()
                                gameInitiated = true
                              }
                            })
                        })
                    }))
                break

              case 'start':
                if (gameInitiated && !gamehasStarted) {
                  game.start()
                  gamehasStarted = true
                }
                break

              case 'force-restart ' + process.env.MAFIA_PASSWORD:
                break

              default:
                break

            }
          }

        }
        if (data.type == 'message' && data.user != botID) {
          gameEmitter.emit('newMessage', data)
        }
      })

    })

  }
}

export default main
<|endoftext|>


<commit_before>/*
Mag.JS AddOns v0.1.4
(c) Michael Glazer
https://github.com/magnumjs/mag.js/new/master/new
*/
mag.addons = {};
// helper function for non proxy supported browser i.e. NOT firefox
// state.fom = mag.addons.binds(state)
mag.addons.binds = function(data, attachTo, callback) {
    var handler = function(e) {
        var val =  e.target.type == 'checkbox' ? e.target.checked : e.target.value
        var name = e.target.name
        if(data[name] && data[name].type=='fun' && typeof data[name] == 'function'){
            data[name](val)
        } else {
            data[name] = val
        }
        if(callback && typeof callback == 'function') callback()
    }
    var addThis = {}

    var events = ['_onchange', '_oninput']
    for (var k in events) addThis[events[k]] = handler
    if (attachTo) mag.addons.merge(addThis, attachTo)
    return addThis
};

mag.addons.when=function(arrayOfPromises, callback){
    Promise.all(arrayOfPromises).then(callback)
}

mag.addons.merge = function(source, destination) {
    for (var k in source) destination[k] = source[k]
}

// return object of getter values
mag.addons.getProp=function(data){
    var newData={}
    Object.keys(data).forEach(function(k){
        if(data[k].type=='fun' && typeof data[k] == 'function') newData[k] = data[k]()
    })
    return newData
}

// show hide

mag.addons.show = function(condition) {
    return {
        _config: function(n) {
            if (!condition) n.style.display = 'none';
            else n.style.display = 'block';
        }
    };
}

mag.addons.hide = function(condition) {
    return {
        _config: function(n) {
            if (condition) n.style.display = 'none';
        }
    }
}

mag.addons.onload = function(element) {
    element.classList.remove("hide")
};

// hookins

mag.hookin('attributes', 'key', function(data) {
    // remove system key from being added to attributes in html
    data.value = null
})

mag.hookin('attributes', 'className', function(data) {
    if (!data.node.classList.contains(data.value)) {
        data.value = data.node.classList.length > 0 ? data.node.classList + ' ' + data.value : data.value
    }
    // same thing
    //data.node.classList.add(data.value)
    data.key = 'class'
})
<commit_msg>Update mag.addons.js<commit_after>/*
Mag.JS AddOns v0.1.6
(c) Michael Glazer
https://github.com/magnumjs/mag.js/new/master/new
*/
mag.addons = {};
// helper function for non proxy supported browser i.e. NOT firefox
// state.fom = mag.addons.binds(state)
mag.addons.binds = function(data, attachTo, callback) {
    var handler = function(e) {
        var val =  e.target.type == 'checkbox' ? e.target.checked : e.target.value
        var name = e.target.name
        if(data[name] && data[name].type=='fun' && typeof data[name] == 'function'){
            data[name](val)
        } else {
            data[name] = val
        }

        // two-way bindings
        Object.observe(data, function(changes) {
          // update target with changes
          changes.forEach(function(change) {
            if (change.type == 'update') {
              // update the related dom
              if(e.target.name ==change.name)
                e.target.value = change.object[change.name]
            }
          })
        })

        if(callback && typeof callback == 'function') callback()
    }
    var addThis = {}

    Object.observe

    var events = ['_onchange', '_oninput']
    for (var k in events) addThis[events[k]] = handler
    if (attachTo) mag.addons.merge(addThis, attachTo)
    return addThis
};

mag.addons.when=function(arrayOfPromises, callback){
    Promise.all(arrayOfPromises).then(callback)
}

mag.addons.merge = function(source, destination) {
    for (var k in source) destination[k] = source[k]
}

// return object of getter values
mag.addons.getProp=function(data){
    var newData={}
    Object.keys(data).forEach(function(k){
        if(data[k].type=='fun' && typeof data[k] == 'function') newData[k] = data[k]()
    })
    return newData
}

// show hide

mag.addons.show = function(condition) {
    return {
        _config: function(n) {
            if (!condition) n.style.display = 'none';
            else n.style.display = 'block';
        }
    };
}

mag.addons.hide = function(condition) {
    return {
        _config: function(n) {
            if (condition) n.style.display = 'none';
        }
    }
}

mag.addons.onload = function(element) {
    element.classList.remove("hide")
};

// hookins

mag.hookin('attributes', 'key', function(data) {
    // remove system key from being added to attributes in html
    data.value = null
})

mag.hookin('attributes', 'className', function(data) {
    if (!data.node.classList.contains(data.value)) {
        data.value = data.node.classList.length > 0 ? data.node.classList + ' ' + data.value : data.value
    }
    // same thing
    //data.node.classList.add(data.value)
    data.key = 'class'
})
<|endoftext|>


<commit_before>var app = require('app');
var BrowserWindow = require('browser-window');
var menu = require('./menu');

var mainWindow = null;

app.on('window-all-closed', function() {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('ready', onReady);
app.on('activate-with-no-open-windows', onReady);

function onReady() {

  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    'min-height': 400,
    'min-width': 600,
    resizable: true,
    'web-preferences': {
			'overlay-scrollbars': true,
      'plugins': true
		},
    show: false,
    title: 'Mancy',
  });

  mainWindow.loadUrl('file://' + __dirname + '/../index.html');

  mainWindow.on('closed', function() {
    mainWindow = null;
  });

  mainWindow.webContents.on('did-finish-load', function() {
    mainWindow.show();
    mainWindow.focus();
  });
console.log(menu)
  menu.setMenu(app, mainWindow);

  // testing
  //TODO: configure for dev only environment
  mainWindow.openDevTools({detach: true});
  // var name = BrowserWindow.addDevToolsExtension('devtools/react-devtools/shells/chrome');
}
<commit_msg>Remove debug<commit_after>var app = require('app');
var BrowserWindow = require('browser-window');
var menu = require('./menu');

var mainWindow = null;

app.on('window-all-closed', function() {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('ready', onReady);
app.on('activate-with-no-open-windows', onReady);

function onReady() {

  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    'min-height': 400,
    'min-width': 600,
    resizable: true,
    'web-preferences': {
			'overlay-scrollbars': true,
      'plugins': true
		},
    show: false,
    title: 'Mancy',
  });

  mainWindow.loadUrl('file://' + __dirname + '/../index.html');

  mainWindow.on('closed', function() {
    mainWindow = null;
  });

  mainWindow.webContents.on('did-finish-load', function() {
    mainWindow.show();
    mainWindow.focus();
  });

  menu.setMenu(app, mainWindow);

  // testing
  //TODO: configure for dev only environment
  mainWindow.openDevTools({detach: true});
  // var name = BrowserWindow.addDevToolsExtension('devtools/react-devtools/shells/chrome');
}
<|endoftext|>


<commit_before>import { Menu, dialog, app, ipcMain as ipc, BrowserWindow } from 'electron';
import { resolve } from 'path';

import Rx from 'rxjs/Rx';

import {
  launch,
  launchNewNotebook,
} from './launch';

import { defaultMenu, loadFullMenu } from './menu';

import prepareEnv from './prepare-env';

const path = require('path');

const log = require('electron-log');

const kernelspecs = require('kernelspecs');

const version = require('../../package.json').version;

log.info('args', process.argv);

const sliceAt = process.argv[0].match('nteract') ? 1 : 2;

const argv = require('yargs')
  .version(version)
  .parse(process.argv.slice(sliceAt));

const notebooks = argv._.filter(x => /(.ipynb)$/.test(x));

ipc.on('new-kernel', (event, newKernel) => {
  launchNewNotebook(newKernel);
});

ipc.on('open-notebook', (event, filename) => {
  launch(path.resolve(filename));
});

const electronReady$ = Rx.Observable.fromEvent(app, 'ready');

const fullAppReady$ = Rx.Observable.zip(
  electronReady$,
  prepareEnv
).first();

const kernelSpecsPromise = prepareEnv
  .toPromise()
  .then(() => kernelspecs.findAll());

/**
 * Creates an Rx.Subscriber that will create a splash page onNext and close the
 * splash page onComplete.
 * @return {Rx.Subscriber} Splash Window subscriber
 */
export function createSplashSubscriber() {
  let win;

  return Rx.Subscriber.create(() => {
    win = new BrowserWindow({
      width: 900,
      height: 400,
      title: 'loading',
      frame: false,
    });

    const index = path.join(__dirname, '..', '..', 'static', 'splash.html');
    win.loadURL(`file://${index}`);
    win.show();
  }, null,
  () => {
    // Close the splash page when completed
    if (win) {
      win.close();
    }
  });
}

const appAndKernelSpecsReady = Rx.Observable.zip(fullAppReady$, kernelSpecsPromise);

const splashSubscription = electronReady$
  // TODO: Take until first window is shown
  .takeUntil(appAndKernelSpecsReady)
  .subscribe(createSplashSubscriber());

function closeAppOnNonDarwin() {
  // On OS X, we want to keep the app and menu bar active
  if (process.platform !== 'darwin') {
    app.quit();
  }
}
const windowAllClosed = Rx.Observable.fromEvent(app, 'window-all-closed');
const windowClosedSubscription = windowAllClosed
  .skipUntil(appAndKernelSpecsReady)
  .subscribe(closeAppOnNonDarwin);

const openFile$ = Rx.Observable.fromEvent(
  app,
  'open-file', (event, filename) => ({ event, filename })
);

function openFileFromEvent({ event, filename }) {
  event.preventDefault();
  launch(path.resolve(filename));
}

// Since we can't launch until app is ready
// and OS X will send the open-file events early,
// buffer those that come early.
openFile$
  .buffer(fullAppReady$) // Form an array of open-file events from before app-ready
  .first() // Should only be the first
  .subscribe(buffer => {
    // Now we can choose whether to open the default notebook
    // based on if arguments went through argv or through open-file events
    if (notebooks.length <= 0 && buffer.length <= 0) {
      log.info('launching an empty notebook by default');
      kernelSpecsPromise.then(specs => {
        const defaultKernel = 'python3';
        let kernel = defaultKernel;

        if ('python3' in specs) {
          kernel = 'python3';
        } else if ('python2' in specs) {
          kernel = 'python2';
        } else {
          const specList = Object.keys(specs);
          specList.sort();
          kernel = specList[0];
        }

        launchNewNotebook(kernel);
      }
      );
    } else {
      notebooks
        .forEach(f => {
          try {
            launch(resolve(f));
          } catch (e) {
            log.error(e);
            console.error(e);
          }
        });
    }
    buffer.forEach(openFileFromEvent);
  });

// All open file events after app is ready
openFile$
  .skipUntil(fullAppReady$)
  .subscribe(openFileFromEvent);

fullAppReady$
  .subscribe(() => {
    kernelSpecsPromise.then(kernelSpecs => {
      if (Object.keys(kernelSpecs).length !== 0) {
        // Get the default menu first
        Menu.setApplicationMenu(defaultMenu);
        // Let the kernels/languages come in after
        loadFullMenu().then(menu => Menu.setApplicationMenu(menu));
      } else {
        dialog.showMessageBox({
          type: 'warning',
          title: 'No Kernels Installed',
          buttons: [],
          message: 'No kernels are installed on your system.',
          detail: 'No kernels are installed on your system so you will not be ' +
            'able to execute code cells in any language. You can read about ' +
            'installing kernels at ' +
            'https://ipython.readthedocs.io/en/latest/install/kernel_install.html',
        }, (index) => {
          if (index === 0) {
            app.quit();
          }
        });
      }
    }).catch(err => {
      dialog.showMessageBox({
        type: 'error',
        title: 'No Kernels Installed',
        buttons: [],
        message: 'No kernels are installed on your system.',
        detail: 'No kernels are installed on your system so you will not be ' +
          'able to execute code cells in any language. You can read about ' +
          'installing kernels at ' +
          'https://ipython.readthedocs.io/en/latest/install/kernel_install.html' +
          `\nFull error: ${err.message}`,
      }, (index) => {
        if (index === 0) {
          app.quit();
        }
      });
    });
  });
<commit_msg>feat(CLI): Specify initial kernel via command line<commit_after>import { Menu, dialog, app, ipcMain as ipc, BrowserWindow } from 'electron';
import { resolve } from 'path';

import Rx from 'rxjs/Rx';

import {
  launch,
  launchNewNotebook,
} from './launch';

import { defaultMenu, loadFullMenu } from './menu';

import prepareEnv from './prepare-env';

const path = require('path');

const log = require('electron-log');

const kernelspecs = require('kernelspecs');

const argv = require('yargs')
  .alias('k', 'kernel')
  .default('kernel', 'python3')
  .argv;

log.info('args', argv);

const notebooks = argv._.filter(x => /(.ipynb)$/.test(x));

ipc.on('new-kernel', (event, newKernel) => {
  launchNewNotebook(newKernel);
});

ipc.on('open-notebook', (event, filename) => {
  launch(path.resolve(filename));
});

const electronReady$ = Rx.Observable.fromEvent(app, 'ready');

const fullAppReady$ = Rx.Observable.zip(
  electronReady$,
  prepareEnv
).first();

const kernelSpecsPromise = prepareEnv
  .toPromise()
  .then(() => kernelspecs.findAll());

/**
 * Creates an Rx.Subscriber that will create a splash page onNext and close the
 * splash page onComplete.
 * @return {Rx.Subscriber} Splash Window subscriber
 */
export function createSplashSubscriber() {
  let win;

  return Rx.Subscriber.create(() => {
    win = new BrowserWindow({
      width: 900,
      height: 400,
      title: 'loading',
      frame: false,
    });

    const index = path.join(__dirname, '..', '..', 'static', 'splash.html');
    win.loadURL(`file://${index}`);
    win.show();
  }, null,
  () => {
    // Close the splash page when completed
    if (win) {
      win.close();
    }
  });
}

const appAndKernelSpecsReady = Rx.Observable.zip(fullAppReady$, kernelSpecsPromise);

const splashSubscription = electronReady$
  // TODO: Take until first window is shown
  .takeUntil(appAndKernelSpecsReady)
  .subscribe(createSplashSubscriber());

function closeAppOnNonDarwin() {
  // On OS X, we want to keep the app and menu bar active
  if (process.platform !== 'darwin') {
    app.quit();
  }
}
const windowAllClosed = Rx.Observable.fromEvent(app, 'window-all-closed');
const windowClosedSubscription = windowAllClosed
  .skipUntil(appAndKernelSpecsReady)
  .subscribe(closeAppOnNonDarwin);

const openFile$ = Rx.Observable.fromEvent(
  app,
  'open-file', (event, filename) => ({ event, filename })
);

function openFileFromEvent({ event, filename }) {
  event.preventDefault();
  launch(path.resolve(filename));
}

// Since we can't launch until app is ready
// and OS X will send the open-file events early,
// buffer those that come early.
openFile$
  .buffer(fullAppReady$) // Form an array of open-file events from before app-ready
  .first() // Should only be the first
  .subscribe(buffer => {
    // Now we can choose whether to open the default notebook
    // based on if arguments went through argv or through open-file events
    if (notebooks.length <= 0 && buffer.length <= 0) {
      log.info('launching an empty notebook by default');
      kernelSpecsPromise.then(specs => {
        let kernel;

        if (argv.kernel in specs) {
          kernel = argv.kernel;
        } else if ('python2' in specs) {
          kernel = 'python2';
        } else {
          const specList = Object.keys(specs);
          specList.sort();
          kernel = specList[0];
        }

        launchNewNotebook(kernel);
      }
      );
    } else {
      notebooks
        .forEach(f => {
          try {
            launch(resolve(f));
          } catch (e) {
            log.error(e);
            console.error(e);
          }
        });
    }
    buffer.forEach(openFileFromEvent);
  });

// All open file events after app is ready
openFile$
  .skipUntil(fullAppReady$)
  .subscribe(openFileFromEvent);

fullAppReady$
  .subscribe(() => {
    kernelSpecsPromise.then(kernelSpecs => {
      if (Object.keys(kernelSpecs).length !== 0) {
        // Get the default menu first
        Menu.setApplicationMenu(defaultMenu);
        // Let the kernels/languages come in after
        loadFullMenu().then(menu => Menu.setApplicationMenu(menu));
      } else {
        dialog.showMessageBox({
          type: 'warning',
          title: 'No Kernels Installed',
          buttons: [],
          message: 'No kernels are installed on your system.',
          detail: 'No kernels are installed on your system so you will not be ' +
            'able to execute code cells in any language. You can read about ' +
            'installing kernels at ' +
            'https://ipython.readthedocs.io/en/latest/install/kernel_install.html',
        }, (index) => {
          if (index === 0) {
            app.quit();
          }
        });
      }
    }).catch(err => {
      dialog.showMessageBox({
        type: 'error',
        title: 'No Kernels Installed',
        buttons: [],
        message: 'No kernels are installed on your system.',
        detail: 'No kernels are installed on your system so you will not be ' +
          'able to execute code cells in any language. You can read about ' +
          'installing kernels at ' +
          'https://ipython.readthedocs.io/en/latest/install/kernel_install.html' +
          `\nFull error: ${err.message}`,
      }, (index) => {
        if (index === 0) {
          app.quit();
        }
      });
    });
  });
<|endoftext|>


<commit_before>import { inject as service } from '@ember/service';
import Component from '@ember/component';
import { computed } from '@ember/object';
import $ from 'jquery';

const UpfIconComponent = Component.extend({
  assetMap: service('asset-map'),

  icon: computed('params.[]', function() {
    return this.get('params')[0];
  }),

  color: computed('params.[]', function() {
    return this.get('params')[1];
  }),

  thickness: computed('params.[]', function() {
    return this.get('params')[2];
  }),

  willInsertElement() {
    $.get(
      this.get('assetMap').resolve(`assets/upf-icons/${this.get('icon')}.svg`),
      (data) => {
        let element = this.$()[0];

        if (this.get('color')) {
          data.children[0].classList += ` upf-icon--${this.get('color')}`;
        }

        if (this.get('thickness')) {
          data.children[0].classList +=
            ` upf-icon--stroke-${this.get('thickness')}`;
        }

        element.innerHTML = new XMLSerializer().serializeToString(data);
      }
    );
  }
});

UpfIconComponent.reopenClass({
  positionalParams: 'params'
});

export default UpfIconComponent;
<commit_msg>upf-icon: allow assets root path to be set by the callee<commit_after>import { inject as service } from '@ember/service';
import Component from '@ember/component';
import { computed } from '@ember/object';
import $ from 'jquery';

const UpfIconComponent = Component.extend({
  assetMap: service('asset-map'),

  assetsRoot: 'assets',

  icon: computed('params.[]', function() {
    return this.get('params')[0];
  }),

  color: computed('params.[]', function() {
    return this.get('params')[1];
  }),

  thickness: computed('params.[]', function() {
    return this.get('params')[2];
  }),

  willInsertElement() {
    $.get(
      this.get('assetMap').resolve(`${this.assetsRoot}/upf-icons/${this.get('icon')}.svg`),
      (data) => {
        let element = this.$()[0];

        if (this.get('color')) {
          data.children[0].classList += ` upf-icon--${this.get('color')}`;
        }

        if (this.get('thickness')) {
          data.children[0].classList +=
            ` upf-icon--stroke-${this.get('thickness')}`;
        }

        element.innerHTML = new XMLSerializer().serializeToString(data);
      }
    );
  }
});

UpfIconComponent.reopenClass({
  positionalParams: 'params'
});

export default UpfIconComponent;
<|endoftext|>


<commit_before>import Ember from 'ember';

/**
 * @module ember-osf
 * @submodule mixins
 */

/**
 * Controller mixin that implements common actions performed on nodes.
 * @class NodeActionsMixin
 * @extends Ember.Mixin
 */
export default Ember.Mixin.create({
    /**
     * The node to perform these actions on. If not specified, defaults to the model hook.
     * @property node
     * @type DS.Model
     */
    node: null,
    model: null,
    _node: Ember.computed.or('node', 'model'),
    /**
     * Helper method that maps all node contributors to format {contribId: Contributor}
     *
     * @method _generateContributorMap
     * @private
     * @param {Contributor[]} contributors A list of contributors to be included in the map
     * @return {Object} Returns a contributor map of the id to the contributor record
     */
    _generateContributorMap(contributors) {
        var contribMap = {};
        contributors.forEach(contrib => contribMap[contrib.id] = contrib);
        return contribMap;
    },
    /**
     * Helper method that affiliates an institution with a node.
     *
     * @method _affiliateNode
     * @private
     * @param {DS.Model} node Node record
     * @param {Object} institution Institution record
     * @return {Promise} Returns a promise that resolves to the updated node with the newly created institution relationship
     */
    _affiliateNode(node, institution) {
        node.get('affiliatedInstitutions').pushObject(institution);
        return node.save();
    },
    actions: {
        /**
         * Update a node
         *
         * @method updateNode
         * @param {String} title New title of the node
         * @param {String} description New Description of the node
         * @param {String} category New node category
         * @param {Boolean} isPublic Should this node be publicly-visible?
         * @return {Promise} Returns a promise that resolves to the updated node
         */
        updateNode(title, description, category, isPublic) {
            var node = this.get('_node');
            if (title) {
                node.set('title', title);
            }
            if (category) {
                node.set('category', category);
            }
            if (description) {
                node.set('description', description);
            }
            if (isPublic !== null) {
                node.set('public', isPublic);
            }
            return node.save();
        },
        /**
         * Delete a node
         *
         * @method deleteNode
         * @return {Promise} Returns a promise that resolves after the deletion of the node.
         */
        deleteNode() {
            var node = this.get('_node');
            return node.destroyRecord();
        },
        /**
         * Affiliate a node with an institution
         *
         * @method affiliateNode
         * @param {String} institutionId ID of the institutution to be affiliated
         * @return {Promise} Returns a promise that resolves to the updated node
         * with the newly affiliated institution relationship
         */
        affiliateNode(institutionId) {
            var node = this.get('_node');
            return this.store.findRecord('institution', institutionId)
                .then(institution => this._affiliateNode(node, institution));
        },
        /**
         * Unaffiliate a node with an institution
         *
         * @method unaffiliateNode
         * @param {Object} institution Institution relationship to be removed from node
         * @return {Promise} Returns a promise that resolves to the updated node
         * with the affiliated institution relationship removed.
         */
        unaffiliateNode(institution) {
            var node = this.get('_node');
            node.get('affiliatedInstitutions').removeObject(institution);
            return node.save();
        },
        /**
         * Add a contributor to a node
         *
         * @method addContributor
         * @param {String} userId ID of user that will be a contributor on the node
         * @param {String} permission User permission level. One of "read", "write", or "admin". Default: "write".
         * @param {Boolean} isBibliographic Whether user will be included in citations for the node. "default: true"
         * @return {Promise} Returns a promise that resolves to the updated node
         * with the new contributor relationship.
         */
        addContributor(userId, permission, isBibliographic) {
            var node = this.get('_node');
            var contributor = this.store.createRecord('contributor', {
                id: `${node.get('id')}-${userId}`,
                permission: permission,
                bibliographic: isBibliographic
            });
            node.get('contributors').pushObject(contributor);
            return node.save().then(() => contributor);
        },
        /**
         * Add unregistered contributor to a node.  Creates a user and then adds that user as a contributor.
         *
         * @method addUnregisteredContributor
         * @param {String} fullName Full name of user
         * @param {String} email User's email
         * @return {Promise} Returns a promise that resolves to the updated node
         * with the new contributor relationship.
         */
        addUnregisteredContributor(fullName, email, permission, isBibliographic) {
            var user = this.store.createRecord('user', {
                fullName: fullName,
                username: email
            });
            // After user has been saved, add user as a contributor
            return user.save().then(user => {
                this.send('addContributor', user.id, permission, isBibliographic);
            });
        },
        /**
         * Remove a contributor from a node
         *
         * @method removeContributor
         * @param {Object} contributor Contributor relationship that will be removed from node
         * @return {Promise} Returns a promise that will resolve upon contributor deletion.
         * User itself will not be removed.
         */
        removeContributor(contributor) {
            var node = this.get('_node');
            contributor.setProperties({
                nodeId: node.id
            });
            return contributor.destroyRecord();
        },
        /**
         * Update contributors of a node. Makes a bulk request to the APIv2.
         *
         * @method updateContributors
         * @param {Contributor[]} contributors Contributor relationships on the node.
         * @param {Object} permissionsChanges Dictionary mapping contributor ids to desired permissions.
         * @param {Object} bibliographicChanges Dictionary mapping contributor ids to desired bibliographic statuses
         * @return {Promise} Returns a promise that resolves to the updated node
         * with edited contributor relationships.
         */
        updateContributors(contributors, permissionsChanges, bibliographicChanges) {
            var node = this.get('_node');
            var contributorMap = this._generateContributorMap(contributors);
            for (let contributorId in permissionsChanges) {
                contributorMap[contributorId].set('permission', permissionsChanges[contributorId]);
            }
            for (let contributorId in bibliographicChanges) {
                contributorMap[contributorId].set('bibliographic', bibliographicChanges[contributorId]);
            }
            return node.save();
        },
        /**
         * Add a child (component) to a node.
         *
         * @method addChild
         * @param {String} title Title for the child
         * @param {String} description Description for the child
         * @param {String} category Category for the child
         * @return {Promise} Returns a promise that resolves to the updated node with the new child relationship.
         */
        addChild(title, description, category) {
            var node = this.get('_node');
            var child = this.store.createRecord('node', {
                title: title,
                category: category || 'project',
                description: description || null
            });
            node.get('children').pushObject(child);
            return node.save();
        },
        /**
         * Add a node link (pointer) to another node
         *
         * @method addNodeLink
         * @param {String} targetNodeId ID of the node for which you wish to create a pointer
         * @return {Promise} Returns a promise that resolves to the updated node with the
         * newly added nodeLink relationship
         */
        addNodeLink(targetNodeId) {
            var node = this.get('_node');
            var nodeLink = this.store.createRecord('node-link', {
                target: targetNodeId
            });
            node.get('nodeLinks').pushObject(nodeLink);
            return node.save();
        },
        /**
         * Remove a node link (pointer) to another node
         *
         * @method removeNodeLink
         * @param {Object} nodeLink nodeLink record to be destroyed.
         * @return {Promise} Returns a promise that resolves after the node link has been removed.  This does not delete
         * the target node itself.
         */
        removeNodeLink(nodeLink) {
            return nodeLink.destroyRecord();
        }
    }
});
<commit_msg>Do not call this.send because no promise is returned.<commit_after>import Ember from 'ember';

/**
 * @module ember-osf
 * @submodule mixins
 */

/**
 * Controller mixin that implements common actions performed on nodes.
 * @class NodeActionsMixin
 * @extends Ember.Mixin
 */
export default Ember.Mixin.create({
    /**
     * The node to perform these actions on. If not specified, defaults to the model hook.
     * @property node
     * @type DS.Model
     */
    node: null,
    model: null,
    _node: Ember.computed.or('node', 'model'),
    /**
     * Helper method that maps all node contributors to format {contribId: Contributor}
     *
     * @method _generateContributorMap
     * @private
     * @param {Contributor[]} contributors A list of contributors to be included in the map
     * @return {Object} Returns a contributor map of the id to the contributor record
     */
    _generateContributorMap(contributors) {
        var contribMap = {};
        contributors.forEach(contrib => contribMap[contrib.id] = contrib);
        return contribMap;
    },
    /**
     * Helper method that affiliates an institution with a node.
     *
     * @method _affiliateNode
     * @private
     * @param {DS.Model} node Node record
     * @param {Object} institution Institution record
     * @return {Promise} Returns a promise that resolves to the updated node with the newly created institution relationship
     */
    _affiliateNode(node, institution) {
        node.get('affiliatedInstitutions').pushObject(institution);
        return node.save();
    },
    actions: {
        /**
         * Update a node
         *
         * @method updateNode
         * @param {String} title New title of the node
         * @param {String} description New Description of the node
         * @param {String} category New node category
         * @param {Boolean} isPublic Should this node be publicly-visible?
         * @return {Promise} Returns a promise that resolves to the updated node
         */
        updateNode(title, description, category, isPublic) {
            var node = this.get('_node');
            if (title) {
                node.set('title', title);
            }
            if (category) {
                node.set('category', category);
            }
            if (description) {
                node.set('description', description);
            }
            if (isPublic !== null) {
                node.set('public', isPublic);
            }
            return node.save();
        },
        /**
         * Delete a node
         *
         * @method deleteNode
         * @return {Promise} Returns a promise that resolves after the deletion of the node.
         */
        deleteNode() {
            var node = this.get('_node');
            return node.destroyRecord();
        },
        /**
         * Affiliate a node with an institution
         *
         * @method affiliateNode
         * @param {String} institutionId ID of the institutution to be affiliated
         * @return {Promise} Returns a promise that resolves to the updated node
         * with the newly affiliated institution relationship
         */
        affiliateNode(institutionId) {
            var node = this.get('_node');
            return this.store.findRecord('institution', institutionId)
                .then(institution => this._affiliateNode(node, institution));
        },
        /**
         * Unaffiliate a node with an institution
         *
         * @method unaffiliateNode
         * @param {Object} institution Institution relationship to be removed from node
         * @return {Promise} Returns a promise that resolves to the updated node
         * with the affiliated institution relationship removed.
         */
        unaffiliateNode(institution) {
            var node = this.get('_node');
            node.get('affiliatedInstitutions').removeObject(institution);
            return node.save();
        },
        /**
         * Add a contributor to a node
         *
         * @method addContributor
         * @param {String} userId ID of user that will be a contributor on the node
         * @param {String} permission User permission level. One of "read", "write", or "admin". Default: "write".
         * @param {Boolean} isBibliographic Whether user will be included in citations for the node. "default: true"
         * @return {Promise} Returns a promise that resolves to created contributor
         */
        addContributor(userId, permission, isBibliographic) {
            var node = this.get('_node');
            var contributor = this.store.createRecord('contributor', {
                id: `${node.get('id')}-${userId}`,
                permission: permission,
                bibliographic: isBibliographic
            });
            node.get('contributors').pushObject(contributor);
            return node.save().then(() => contributor);
        },
        /**
         * Add unregistered contributor to a node.  Creates a user and then adds that user as a contributor.
         *
         * @method addUnregisteredContributor
         * @param {String} fullName Full name of user
         * @param {String} email User's email
         * @return {Promise} Returns a promise that resolves to the created contributor
         */
        addUnregisteredContributor(fullName, email, permission, isBibliographic) {
            var user = this.store.createRecord('user', {
                fullName: fullName,
                username: email
            });
            // After user has been saved, add user as a contributor
            return user.save().then(user => {
                var node = this.get('_node');
                var contributor = this.store.createRecord('contributor', {
                    id: `${node.get('id')}-${user.id}`,
                    permission: permission,
                    bibliographic: isBibliographic
                });
                node.get('contributors').pushObject(contributor);
                return node.save().then(() => contributor);
            });
        },
        /**
         * Remove a contributor from a node
         *
         * @method removeContributor
         * @param {Object} contributor Contributor relationship that will be removed from node
         * @return {Promise} Returns a promise that will resolve upon contributor deletion.
         * User itself will not be removed.
         */
        removeContributor(contributor) {
            var node = this.get('_node');
            contributor.setProperties({
                nodeId: node.id
            });
            return contributor.destroyRecord();
        },
        /**
         * Update contributors of a node. Makes a bulk request to the APIv2.
         *
         * @method updateContributors
         * @param {Contributor[]} contributors Contributor relationships on the node.
         * @param {Object} permissionsChanges Dictionary mapping contributor ids to desired permissions.
         * @param {Object} bibliographicChanges Dictionary mapping contributor ids to desired bibliographic statuses
         * @return {Promise} Returns a promise that resolves to the updated node
         * with edited contributor relationships.
         */
        updateContributors(contributors, permissionsChanges, bibliographicChanges) {
            var node = this.get('_node');
            var contributorMap = this._generateContributorMap(contributors);
            for (let contributorId in permissionsChanges) {
                contributorMap[contributorId].set('permission', permissionsChanges[contributorId]);
            }
            for (let contributorId in bibliographicChanges) {
                contributorMap[contributorId].set('bibliographic', bibliographicChanges[contributorId]);
            }
            return node.save();
        },
        /**
         * Add a child (component) to a node.
         *
         * @method addChild
         * @param {String} title Title for the child
         * @param {String} description Description for the child
         * @param {String} category Category for the child
         * @return {Promise} Returns a promise that resolves to the updated node with the new child relationship.
         */
        addChild(title, description, category) {
            var node = this.get('_node');
            var child = this.store.createRecord('node', {
                title: title,
                category: category || 'project',
                description: description || null
            });
            node.get('children').pushObject(child);
            return node.save();
        },
        /**
         * Add a node link (pointer) to another node
         *
         * @method addNodeLink
         * @param {String} targetNodeId ID of the node for which you wish to create a pointer
         * @return {Promise} Returns a promise that resolves to the updated node with the
         * newly added nodeLink relationship
         */
        addNodeLink(targetNodeId) {
            var node = this.get('_node');
            var nodeLink = this.store.createRecord('node-link', {
                target: targetNodeId
            });
            node.get('nodeLinks').pushObject(nodeLink);
            return node.save();
        },
        /**
         * Remove a node link (pointer) to another node
         *
         * @method removeNodeLink
         * @param {Object} nodeLink nodeLink record to be destroyed.
         * @return {Promise} Returns a promise that resolves after the node link has been removed.  This does not delete
         * the target node itself.
         */
        removeNodeLink(nodeLink) {
            return nodeLink.destroyRecord();
        }
    }
});
<|endoftext|>


<commit_before>import Ember from 'ember';

/**
 * @module ember-osf
 * @submodule mixins
 */

/**
 * Controller mixin that implements common actions performed on nodes.
 * @class NodeActionsMixin
 * @extends Ember.Mixin
 */
export default Ember.Mixin.create({
    /**
     * The node to perform these actions on. If not specified, defaults to the model hook.
     * @property node
     * @type DS.Model
     */
    node: null,
    model: null,
    _node: Ember.computed.or('node', 'model'),
    /**
     * Helper method that maps all node contributors to format {contribId: Contributor}
     *
     * @method _generateContributorMap
     * @private
     * @param {Contributor[]} contributors A list of contributors to be included in the map
     * @return {Object} Returns a contributor map of the id to the contributor record
     */
    _generateContributorMap(contributors) {
        var contribMap = {};
        contributors.forEach(contrib => contribMap[contrib.id] = contrib);
        return contribMap;
    },
    /**
     * Helper method that affiliates an institution with a node.
     *
     * @method _affiliateNode
     * @private
     * @param {DS.Model} node Node record
     * @param {Object} institution Institution record
     * @return {Promise} Returns a promise that resolves to the updated node with the newly created institution relationship
     */
    _affiliateNode(node, institution) {
        node.get('affiliatedInstitutions').pushObject(institution);
        return node.save();
    },
    actions: {
        /**
         * Update a node
         *
         * @method updateNode
         * @param {String} title New title of the node
         * @param {String} description New Description of the node
         * @param {String} category New node category
         * @param {Boolean} isPublic Should this node be publicly-visible?
         * @return {Promise} Returns a promise that resolves to the updated node
         */
        updateNode(title, description, category, isPublic) {
            var node = this.get('_node');
            if (title) {
                node.set('title', title);
            }
            if (category) {
                node.set('category', category);
            }
            if (description) {
                node.set('description', description);
            }
            if (isPublic !== null) {
                node.set('public', isPublic);
            }
            return node.save();
        },
        /**
         * Delete a node
         *
         * @method deleteNode
         * @return {Promise} Returns a promise that resolves after the deletion of the node.
         */
        deleteNode() {
            var node = this.get('_node');
            return node.destroyRecord();
        },
        /**
         * Affiliate a node with an institution
         *
         * @method affiliateNode
         * @param {String} institutionId ID of the institutution to be affiliated
         * @return {Promise} Returns a promise that resolves to the updated node
         * with the newly affiliated institution relationship
         */
        affiliateNode(institutionId) {
            var node = this.get('_node');
            return this.store.findRecord('institution', institutionId)
                .then(institution => this._affiliateNode(node, institution));
        },
        /**
         * Unaffiliate a node with an institution
         *
         * @method unaffiliateNode
         * @param {Object} institution Institution relationship to be removed from node
         * @return {Promise} Returns a promise that resolves to the updated node
         * with the affiliated institution relationship removed.
         */
        unaffiliateNode(institution) {
            var node = this.get('_node');
            node.get('affiliatedInstitutions').removeObject(institution);
            return node.save();
        },
        /**
         * Add a contributor to a node
         *
         * @method addContributor
         * @param {String} userId ID of user that will be a contributor on the node
         * @param {String} permission User permission level. One of "read", "write", or "admin". Default: "write".
         * @param {Boolean} isBibliographic Whether user will be included in citations for the node. "default: true"
         * @return {Promise} Returns a promise that resolves to the updated node
         * with the new contributor relationship.
         */
        addContributor(userId, permission, isBibliographic) {
            var node = this.get('_node');
            var contributor = this.store.createRecord('contributor', {
                id: `${node.get('id')}-${userId}`,
                permission: permission,
                bibliographic: isBibliographic
            });
            node.get('contributors').pushObject(contributor);
            return node.save();
        },
        /**
         * Remove a contributor from a node
         *
         * @method removeContributor
         * @param {Object} contributor Contributor relationship that will be removed from node
         * @return {Promise} Returns a promise that will resolve upon contributor deletion.
         * User itself will not be removed.
         */
        removeContributor(contributor) {
            var node = this.get('_node');
            contributor.setProperties({
                nodeId: node.id
            });
            return contributor.destroyRecord();
        },
        /**
         * Update contributors of a node. Makes a bulk request to the APIv2.
         *
         * @method updateContributors
         * @param {Contributor[]} contributors Contributor relationships on the node.
         * @param {Object} permissionsChanges Dictionary mapping contributor ids to desired permissions.
         * @param {Object} bibliographicChanges Dictionary mapping contributor ids to desired bibliographic statuses
         * @return {Promise} Returns a promise that resolves to the updated node
         * with edited contributor relationships.
         */
        updateContributors(contributors, permissionsChanges, bibliographicChanges) {
            var node = this.get('_node');
            var contributorMap = this._generateContributorMap(contributors);
            for (let contributorId in permissionsChanges) {
                contributorMap[contributorId].set('permission', permissionsChanges[contributorId]);
            }
            for (let contributorId in bibliographicChanges) {
                contributorMap[contributorId].set('bibliographic', bibliographicChanges[contributorId]);
            }
            return node.save();
        },
        /**
         * Add a child (component) to a node.
         *
         * @method addChild
         * @param {String} title Title for the child
         * @param {String} description Description for the child
         * @param {String} category Category for the child
         * @return {Promise} Returns a promise that resolves to the updated node with the new child relationship.
         */
        addChild(title, description, category) {
            var node = this.get('_node');
            var child = this.store.createRecord('node', {
                title: title,
                category: category || 'project',
                description: description || null
            });
            node.get('children').pushObject(child);
            return node.save();
        },
        /**
         * Add a node link (pointer) to another node
         *
         * @method addNodeLink
         * @param {String} targetNodeId ID of the node for which you wish to create a pointer
         * @return {Promise} Returns a promise that resolves to the updated node with the
         * newly added nodeLink relationship
         */
        addNodeLink(targetNodeId) {
            var node = this.get('_node');
            var nodeLink = this.store.createRecord('node-link', {
                target: targetNodeId
            });
            node.get('nodeLinks').pushObject(nodeLink);
            return node.save();
        },
        /**
         * Remove a node link (pointer) to another node
         *
         * @method removeNodeLink
         * @param {Object} nodeLink nodeLink record to be destroyed.
         * @return {Promise} Returns a promise that resolves after the node link has been removed.  This does not delete
         * the target node itself.
         */
        removeNodeLink(nodeLink) {
            return nodeLink.destroyRecord();
        }
    }
});
<commit_msg>NodeActions that create something should return the new object<commit_after>import Ember from 'ember';

/**
 * @module ember-osf
 * @submodule mixins
 */

/**
 * Controller mixin that implements common actions performed on nodes.
 * @class NodeActionsMixin
 * @extends Ember.Mixin
 */
export default Ember.Mixin.create({
    /**
     * The node to perform these actions on. If not specified, defaults to the model hook.
     * @property node
     * @type DS.Model
     */
    node: null,
    model: null,
    _node: Ember.computed.or('node', 'model'),
    /**
     * Helper method that maps all node contributors to format {contribId: Contributor}
     *
     * @method _generateContributorMap
     * @private
     * @param {Contributor[]} contributors A list of contributors to be included in the map
     * @return {Object} Returns a contributor map of the id to the contributor record
     */
    _generateContributorMap(contributors) {
        var contribMap = {};
        contributors.forEach(contrib => contribMap[contrib.id] = contrib);
        return contribMap;
    },
    /**
     * Helper method that affiliates an institution with a node.
     *
     * @method _affiliateNode
     * @private
     * @param {DS.Model} node Node record
     * @param {Object} institution Institution record
     * @return {Promise} Returns a promise that resolves to the updated node with the newly created institution relationship
     */
    _affiliateNode(node, institution) {
        node.get('affiliatedInstitutions').pushObject(institution);
        return node.save();
    },
    actions: {
        /**
         * Update a node
         *
         * @method updateNode
         * @param {String} title New title of the node
         * @param {String} description New Description of the node
         * @param {String} category New node category
         * @param {Boolean} isPublic Should this node be publicly-visible?
         * @return {Promise} Returns a promise that resolves to the updated node
         */
        updateNode(title, description, category, isPublic) {
            var node = this.get('_node');
            if (title) {
                node.set('title', title);
            }
            if (category) {
                node.set('category', category);
            }
            if (description) {
                node.set('description', description);
            }
            if (isPublic !== null) {
                node.set('public', isPublic);
            }
            return node.save();
        },
        /**
         * Delete a node
         *
         * @method deleteNode
         * @return {Promise} Returns a promise that resolves after the deletion of the node.
         */
        deleteNode() {
            var node = this.get('_node');
            return node.destroyRecord();
        },
        /**
         * Affiliate a node with an institution
         *
         * @method affiliateNode
         * @param {String} institutionId ID of the institutution to be affiliated
         * @return {Promise} Returns a promise that resolves to the updated node
         * with the newly affiliated institution relationship
         */
        affiliateNode(institutionId) {
            var node = this.get('_node');
            return this.store.findRecord('institution', institutionId)
                .then(institution => this._affiliateNode(node, institution));
        },
        /**
         * Unaffiliate a node with an institution
         *
         * @method unaffiliateNode
         * @param {Object} institution Institution relationship to be removed from node
         * @return {Promise} Returns a promise that resolves to the updated node
         * with the affiliated institution relationship removed.
         */
        unaffiliateNode(institution) {
            var node = this.get('_node');
            node.get('affiliatedInstitutions').removeObject(institution);
            return node.save();
        },
        /**
         * Add a contributor to a node
         *
         * @method addContributor
         * @param {String} userId ID of user that will be a contributor on the node
         * @param {String} permission User permission level. One of "read", "write", or "admin". Default: "write".
         * @param {Boolean} isBibliographic Whether user will be included in citations for the node. "default: true"
         * @return {Promise} Returns a promise that resolves to the updated node
         * with the new contributor relationship.
         */
        addContributor(userId, permission, isBibliographic) {
            var node = this.get('_node');
            var contributor = this.store.createRecord('contributor', {
                id: `${node.get('id')}-${userId}`,
                permission: permission,
                bibliographic: isBibliographic
            });
            node.get('contributors').pushObject(contributor);
            return node.save(() => contributor);
        },
        /**
         * Remove a contributor from a node
         *
         * @method removeContributor
         * @param {Object} contributor Contributor relationship that will be removed from node
         * @return {Promise} Returns a promise that will resolve upon contributor deletion.
         * User itself will not be removed.
         */
        removeContributor(contributor) {
            var node = this.get('_node');
            contributor.setProperties({
                nodeId: node.id
            });
            return contributor.destroyRecord();
        },
        /**
         * Update contributors of a node. Makes a bulk request to the APIv2.
         *
         * @method updateContributors
         * @param {Contributor[]} contributors Contributor relationships on the node.
         * @param {Object} permissionsChanges Dictionary mapping contributor ids to desired permissions.
         * @param {Object} bibliographicChanges Dictionary mapping contributor ids to desired bibliographic statuses
         * @return {Promise} Returns a promise that resolves to the updated node
         * with edited contributor relationships.
         */
        updateContributors(contributors, permissionsChanges, bibliographicChanges) {
            var node = this.get('_node');
            var contributorMap = this._generateContributorMap(contributors);
            for (let contributorId in permissionsChanges) {
                contributorMap[contributorId].set('permission', permissionsChanges[contributorId]);
            }
            for (let contributorId in bibliographicChanges) {
                contributorMap[contributorId].set('bibliographic', bibliographicChanges[contributorId]);
            }
            return node.save();
        },
        /**
         * Add a child (component) to a node.
         *
         * @method addChild
         * @param {String} title Title for the child
         * @param {String} description Description for the child
         * @param {String} category Category for the child
         * @return {Promise} Returns a promise that resolves to the updated node with the new child relationship.
         */
        addChild(title, description, category) {
            var node = this.get('_node');
            var child = this.store.createRecord('node', {
                title: title,
                category: category || 'project',
                description: description || null
            });
            node.get('children').pushObject(child);
            return node.save(() => child);
        },
        /**
         * Add a node link (pointer) to another node
         *
         * @method addNodeLink
         * @param {String} targetNodeId ID of the node for which you wish to create a pointer
         * @return {Promise} Returns a promise that resolves to the updated node with the
         * newly added nodeLink relationship
         */
        addNodeLink(targetNodeId) {
            var node = this.get('_node');
            var nodeLink = this.store.createRecord('node-link', {
                target: targetNodeId
            });
            node.get('nodeLinks').pushObject(nodeLink);
            return node.save(() => nodeLink);
        },
        /**
         * Remove a node link (pointer) to another node
         *
         * @method removeNodeLink
         * @param {Object} nodeLink nodeLink record to be destroyed.
         * @return {Promise} Returns a promise that resolves after the node link has been removed.  This does not delete
         * the target node itself.
         */
        removeNodeLink(nodeLink) {
            return nodeLink.destroyRecord();
        }
    }
});
<|endoftext|>


<commit_before>import Ember from 'ember';
import renderMaterialChart from '../utils/render-material-chart';
import renderClassicChart from '../utils/render-classic-chart';

const { set, getProperties, isBlank } = Ember;

export default Ember.Mixin.create({

  init() {
    this._super(...arguments);
    this._setRenderChart();
  },

  _setRenderChart() {
    let {design, defaultDesign} = getProperties(this, ['design', 'defaultDesign'])
      , renderChartList = {'classic': renderClassicChart, 'material': renderMaterialChart}
      , renderChartSelected = renderChartList[isBlank(design) ? defaultDesign : design];

    set(this, 'renderChart', renderChartSelected);
  },

});
<commit_msg>validate 'design' attribute value<commit_after>import Ember from 'ember';
import renderMaterialChart from '../utils/render-material-chart';
import renderClassicChart from '../utils/render-classic-chart';

const { set, getProperties, isPresent, assert } = Ember;

export default Ember.Mixin.create({

  init() {
    this._super(...arguments);
    this._setRenderChart();
  },

  _setRenderChart() {
    let {design, defaultDesign} = getProperties(this, ['design', 'defaultDesign'])
      , renderChartList = {'classic': renderClassicChart, 'material': renderMaterialChart}
      , renderChartSelected = renderChartList[isPresent(design) ? design : defaultDesign]
      , isValidRenderchart = isPresent(renderChartSelected);

    assert(`Invalid 'design' value.`, isValidRenderchart);
    set(this, 'renderChart', renderChartSelected);
  },

});
<|endoftext|>


<commit_before>import Ember from 'ember';
const { inject, computed } = Ember;
/* global window */

const DURATION = 400;

export default Ember.Mixin.create({
  util: inject.service(),
  rippleContainerSelector: '.md-container',

  center: false,
  dimBackground: false,
  fitRipple: false,
  colorElement: false,
  noink: false,

  rippleInk: computed('noink', 'rippleInkColor', function () {
    if (this.get('noink')) {
      return false;
    }
    if (this.get('rippleInkColor')) {
      return this.get('rippleInkColor');
    }
    return "";
  }),

  didInsertElement() {
    this._super(...arguments);
    this.rippleElement = this.$(this.get('rippleContainerSelector'));
    this.mousedown = false;
    this.ripples = [];
    this.timeout = null; // Stores a reference to the most-recent ripple timeout
    this.lastRipple = null;

    this._container = this.createContainer();

    this.rippleElement.addClass('md-ink-ripple');
    this.bindEvents();
  },
  autoCleanup(self, cleanupFn) {
    if (self.mousedown || self.lastRipple) {
      self.mousedown = false;
      self.get('util').nextTick(cleanupFn.bind(self), false);
    }
  },
  color(value) {
    const self = this;

    // If assigning a color value, apply it to background and the ripple color
    if (typeof value !== 'undefined') {
      self._color = self._parseColor(value);
    }

    // If color lookup, use assigned, defined, or inherited
    return self._color || self._parseColor(self.get('rippleInk')) || self._parseColor(getElementColor());

    /**
     * Finds the color element and returns its text color for use as default ripple color
     * @returns {string}
     */
    function getElementColor() {
      let items = self.get('colorElement') ? self.get('colorElement') : [];
      let elem = items.length ? items[0] : self.rippleElement[0];

      return elem ? window.getComputedStyle(elem).color : 'rgb(0,0,0)';
    }
  },
  calculateColor() {
    return this.color();
  },
  _parseColor(color, multiplier) {
    multiplier = multiplier || 1;

    if (!color) {
      return;
    }
    if (color.indexOf('rgba') === 0) {
      return color.replace(/\d?\.?\d*\s*\)\s*$/, (0.1 * multiplier).toString() + ')');
    }
    if (color.indexOf('rgb') === 0) {
      return rgbToRGBA(color);
    }
    if (color.indexOf('#') === 0) {
      return hexToRGBA(color);
    }

    /**
     * Converts hex value to RGBA string
     * @param color {string}
     * @returns {string}
     */
    function hexToRGBA(color) {
      let hex = color[0] === '#' ? color.substr(1) : color,
        dig = hex.length / 3,
        red = hex.substr(0, dig),
        green = hex.substr(dig, dig),
        blue = hex.substr(dig * 2);
      if (dig === 1) {
        red += red;
        green += green;
        blue += blue;
      }
      return 'rgba(' + parseInt(red, 16) + ',' + parseInt(green, 16) + ',' + parseInt(blue, 16) + ',0.1)';
    }

    /**
     * Converts an RGB color to RGBA
     * @param color {string}
     * @returns {string}
     */
    function rgbToRGBA(color) {
      return color.replace(')', ', 0.1)').replace('(', 'a(');
    }

  },
  bindEvents() {
    this.rippleElement.on('mousedown', this.handleMousedown.bind(this));
    this.rippleElement.on('mouseup touchend', this.handleMouseup.bind(this));
    this.rippleElement.on('mouseleave', this.handleMouseup.bind(this));
    this.rippleElement.on('touchmove', this.handleTouchmove.bind(this));
  },
  handleMousedown(event) {
    if (this.mousedown) {
      return;
    }

    // When jQuery is loaded, we have to get the original event
    if (event.hasOwnProperty('originalEvent')) {
      event = event.originalEvent;
    }
    this.mousedown = true;
    if (this.get('center')) {
      this.createRipple(this._container.prop('clientWidth') / 2, this._container.prop('clientWidth') / 2);
    } else {

      // We need to calculate the relative coordinates if the target is a sublayer of the ripple element
      if (event.srcElement !== this.rippleElement[0]) {
        let layerRect = this.rippleElement[0].getBoundingClientRect();
        let layerX = event.clientX - layerRect.left;
        let layerY = event.clientY - layerRect.top;

        this.createRipple(layerX, layerY);
      } else {
        this.createRipple(event.offsetX, event.offsetY);
      }
    }
  },
  handleMouseup() {
    this.autoCleanup(this, this.clearRipples);
  },
  handleTouchmove() {
    this.autoCleanup(this, this.deleteRipples);
  },
  deleteRipples() {
    for (let i = 0; i < this.ripples.length; i++) {
      this.ripples[i].remove();
    }
  },
  clearRipples() {
    for (let i = 0; i < this.ripples.length; i++) {
      this.fadeInComplete(this.ripples[i]);
    }
  },
  createContainer() {
    let container = Ember.$('

');
    this.rippleElement.append(container);
    return container;
  },
  clearTimeout() {
    if (this.timeout) {
      Ember.run.cancel(this.timeout);
      this.timeout = null;
    }
  },
  isRippleAllowed() {
    let element = this.rippleElement[0];
    do {
      if (!element.tagName || element.tagName === 'BODY') {
        break;
      }

      if (element && Ember.$.isFunction(element.hasAttribute)) {
        if (element.hasAttribute('disabled')) {
          return false;
        }
        if (this.get('rippleInk') === false) {
          return false;
        }
      }

    } while (element = element.parentNode);

    return true;
  },
  createRipple(left, top) {
    if (!this.isRippleAllowed()) {
      return;
    }

    let ctrl = this;
    let ripple = Ember.$('

');
    let width = this.rippleElement.prop('clientWidth');
    let height = this.rippleElement.prop('clientHeight');
    let x = Math.max(Math.abs(width - left), left) * 2;
    let y = Math.max(Math.abs(height - top), top) * 2;
    let size = getSize(this.get('fitRipple'), x, y);
    let color = this.calculateColor();

    ripple.css({
      left: left + 'px',
      top: top + 'px',
      background: 'black',
      width: size + 'px',
      height: size + 'px',
      backgroundColor: rgbaToRGB(color),
      borderColor: rgbaToRGB(color)
    });
    this.lastRipple = ripple;

    // we only want one timeout to be running at a time
    this.clearTimeout();
    this.timeout = Ember.run.later(this, function () {
      ctrl.clearTimeout();
      if (!ctrl.mousedown) {
        ctrl.fadeInComplete(ripple);
      }
    }, {}, DURATION * 0.35);

    if (this.get('dimBackground')) {
      this._container.css({backgroundColor: color});
    }
    this._container.append(ripple);
    this.ripples.push(ripple);
    ripple.addClass('md-ripple-placed');

    this.get('util').nextTick(function () {

      ripple.addClass('md-ripple-scaled md-ripple-active');
      Ember.run.later(this, function () {
        ctrl.clearRipples();
      }, {}, DURATION);

    }, false);

    function rgbaToRGB(color) {
      return color ? color.replace('rgba', 'rgb').replace(/,[^\),]+\)/, ')') : 'rgb(0,0,0)';
    }

    function getSize(fit, x, y) {
      return fit ? Math.max(x, y) : Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
    }
  },
  fadeInComplete(ripple) {
    if (this.lastRipple === ripple) {
      if (!this.timeout && !this.mousedown) {
        this.removeRipple(ripple);
      }
    } else {
      this.removeRipple(ripple);
    }
  },
  removeRipple(ripple) {
    let ctrl = this;
    let index = this.ripples.indexOf(ripple);
    if (index < 0) {
      return;
    }
    this.ripples.splice(this.ripples.indexOf(ripple), 1);
    ripple.removeClass('md-ripple-active');
    if (this.ripples.length === 0) {
      this._container.css({backgroundColor: ''});
    }
    // use a 2-second timeout in order to allow for the animation to finish
    // we don't actually care how long the animation takes
    Ember.run.later(this, function () {
      ctrl.fadeOutComplete(ripple);
    }, {}, DURATION);
  },
  fadeOutComplete(ripple) {
    ripple.remove();
    this.lastRipple = null;
  }
});
<commit_msg>Remove Ember.X in favor of ES6 style import with const in entire ripple-mixin.<commit_after>import Ember from 'ember';
const { inject, computed, Mixin, run, $ } = Ember;
/* global window */

const DURATION = 400;

export default Mixin.create({
  util: inject.service(),
  rippleContainerSelector: '.md-container',

  center: false,
  dimBackground: false,
  fitRipple: false,
  colorElement: false,
  noink: false,

  rippleInk: computed('noink', 'rippleInkColor', function () {
    if (this.get('noink')) {
      return false;
    }
    if (this.get('rippleInkColor')) {
      return this.get('rippleInkColor');
    }
    return "";
  }),

  didInsertElement() {
    this._super(...arguments);
    this.rippleElement = this.$(this.get('rippleContainerSelector'));
    this.mousedown = false;
    this.ripples = [];
    this.timeout = null; // Stores a reference to the most-recent ripple timeout
    this.lastRipple = null;

    this._container = this.createContainer();

    this.rippleElement.addClass('md-ink-ripple');
    this.bindEvents();
  },
  autoCleanup(self, cleanupFn) {
    if (self.mousedown || self.lastRipple) {
      self.mousedown = false;
      self.get('util').nextTick(cleanupFn.bind(self), false);
    }
  },
  color(value) {
    const self = this;

    // If assigning a color value, apply it to background and the ripple color
    if (typeof value !== 'undefined') {
      self._color = self._parseColor(value);
    }

    // If color lookup, use assigned, defined, or inherited
    return self._color || self._parseColor(self.get('rippleInk')) || self._parseColor(getElementColor());

    /**
     * Finds the color element and returns its text color for use as default ripple color
     * @returns {string}
     */
    function getElementColor() {
      let items = self.get('colorElement') ? self.get('colorElement') : [];
      let elem = items.length ? items[0] : self.rippleElement[0];

      return elem ? window.getComputedStyle(elem).color : 'rgb(0,0,0)';
    }
  },
  calculateColor() {
    return this.color();
  },
  _parseColor(color, multiplier) {
    multiplier = multiplier || 1;

    if (!color) {
      return;
    }
    if (color.indexOf('rgba') === 0) {
      return color.replace(/\d?\.?\d*\s*\)\s*$/, (0.1 * multiplier).toString() + ')');
    }
    if (color.indexOf('rgb') === 0) {
      return rgbToRGBA(color);
    }
    if (color.indexOf('#') === 0) {
      return hexToRGBA(color);
    }

    /**
     * Converts hex value to RGBA string
     * @param color {string}
     * @returns {string}
     */
    function hexToRGBA(color) {
      let hex = color[0] === '#' ? color.substr(1) : color,
        dig = hex.length / 3,
        red = hex.substr(0, dig),
        green = hex.substr(dig, dig),
        blue = hex.substr(dig * 2);
      if (dig === 1) {
        red += red;
        green += green;
        blue += blue;
      }
      return 'rgba(' + parseInt(red, 16) + ',' + parseInt(green, 16) + ',' + parseInt(blue, 16) + ',0.1)';
    }

    /**
     * Converts an RGB color to RGBA
     * @param color {string}
     * @returns {string}
     */
    function rgbToRGBA(color) {
      return color.replace(')', ', 0.1)').replace('(', 'a(');
    }

  },
  bindEvents() {
    this.rippleElement.on('mousedown', this.handleMousedown.bind(this));
    this.rippleElement.on('mouseup touchend', this.handleMouseup.bind(this));
    this.rippleElement.on('mouseleave', this.handleMouseup.bind(this));
    this.rippleElement.on('touchmove', this.handleTouchmove.bind(this));
  },
  handleMousedown(event) {
    if (this.mousedown) {
      return;
    }

    // When jQuery is loaded, we have to get the original event
    if (event.hasOwnProperty('originalEvent')) {
      event = event.originalEvent;
    }
    this.mousedown = true;
    if (this.get('center')) {
      this.createRipple(this._container.prop('clientWidth') / 2, this._container.prop('clientWidth') / 2);
    } else {

      // We need to calculate the relative coordinates if the target is a sublayer of the ripple element
      if (event.srcElement !== this.rippleElement[0]) {
        let layerRect = this.rippleElement[0].getBoundingClientRect();
        let layerX = event.clientX - layerRect.left;
        let layerY = event.clientY - layerRect.top;

        this.createRipple(layerX, layerY);
      } else {
        this.createRipple(event.offsetX, event.offsetY);
      }
    }
  },
  handleMouseup() {
    this.autoCleanup(this, this.clearRipples);
  },
  handleTouchmove() {
    this.autoCleanup(this, this.deleteRipples);
  },
  deleteRipples() {
    for (let i = 0; i < this.ripples.length; i++) {
      this.ripples[i].remove();
    }
  },
  clearRipples() {
    for (let i = 0; i < this.ripples.length; i++) {
      this.fadeInComplete(this.ripples[i]);
    }
  },
  createContainer() {
    let container = $('

');
    this.rippleElement.append(container);
    return container;
  },
  clearTimeout() {
    if (this.timeout) {
      run.cancel(this.timeout);
      this.timeout = null;
    }
  },
  isRippleAllowed() {
    let element = this.rippleElement[0];
    do {
      if (!element.tagName || element.tagName === 'BODY') {
        break;
      }

      if (element && $.isFunction(element.hasAttribute)) {
        if (element.hasAttribute('disabled')) {
          return false;
        }
        if (this.get('rippleInk') === false) {
          return false;
        }
      }

    } while (element = element.parentNode);

    return true;
  },
  createRipple(left, top) {
    if (!this.isRippleAllowed()) {
      return;
    }

    let ctrl = this;
    let ripple = $('

');
    let width = this.rippleElement.prop('clientWidth');
    let height = this.rippleElement.prop('clientHeight');
    let x = Math.max(Math.abs(width - left), left) * 2;
    let y = Math.max(Math.abs(height - top), top) * 2;
    let size = getSize(this.get('fitRipple'), x, y);
    let color = this.calculateColor();

    ripple.css({
      left: left + 'px',
      top: top + 'px',
      background: 'black',
      width: size + 'px',
      height: size + 'px',
      backgroundColor: rgbaToRGB(color),
      borderColor: rgbaToRGB(color)
    });
    this.lastRipple = ripple;

    // we only want one timeout to be running at a time
    this.clearTimeout();
    this.timeout = run.later(this, function () {
      ctrl.clearTimeout();
      if (!ctrl.mousedown) {
        ctrl.fadeInComplete(ripple);
      }
    }, {}, DURATION * 0.35);

    if (this.get('dimBackground')) {
      this._container.css({backgroundColor: color});
    }
    this._container.append(ripple);
    this.ripples.push(ripple);
    ripple.addClass('md-ripple-placed');

    this.get('util').nextTick(function () {

      ripple.addClass('md-ripple-scaled md-ripple-active');
      run.later(this, function () {
        ctrl.clearRipples();
      }, {}, DURATION);

    }, false);

    function rgbaToRGB(color) {
      return color ? color.replace('rgba', 'rgb').replace(/,[^\),]+\)/, ')') : 'rgb(0,0,0)';
    }

    function getSize(fit, x, y) {
      return fit ? Math.max(x, y) : Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
    }
  },
  fadeInComplete(ripple) {
    if (this.lastRipple === ripple) {
      if (!this.timeout && !this.mousedown) {
        this.removeRipple(ripple);
      }
    } else {
      this.removeRipple(ripple);
    }
  },
  removeRipple(ripple) {
    let ctrl = this;
    let index = this.ripples.indexOf(ripple);
    if (index < 0) {
      return;
    }
    this.ripples.splice(this.ripples.indexOf(ripple), 1);
    ripple.removeClass('md-ripple-active');
    if (this.ripples.length === 0) {
      this._container.css({backgroundColor: ''});
    }
    // use a 2-second timeout in order to allow for the animation to finish
    // we don't actually care how long the animation takes
    run.later(this, function () {
      ctrl.fadeOutComplete(ripple);
    }, {}, DURATION);
  },
  fadeOutComplete(ripple) {
    ripple.remove();
    this.lastRipple = null;
  }
});
<|endoftext|>