<commit_before>"use strict";

/* actionResponder
 *
 * Written by Michael Albinson, 2/8/17
 *
 * A middleman for all actions that do not include the addition of true content to the database
 * and do not require information to be returned from the server, other than if the action was
 * successful or not.
 *
 * Implementations for each action can be found in the actions directory.
 *
 * i.e. votes, subscriptions, saves, reports etc.
 */

var log = require('./log');
var saver = require('./actions/Saver');
var subscriber = require('./actions/Subscriptions');
var voter = require('./actions/Voter');
var tagger = require('./actions/Tagger');
var commenter = require('./actions/Commenter');
var rater = require('./actions/Rater');
// var getter = require('./actions/Getter');
var DBRow = require('./DBRow').DBRow;

exports.respond = function(request) {
    return new Promise(function(resolve, reject) {
        var use = undefined;
        var action = request.body.action;
        switch(action) {
            case("vote"):
               use = vote;
               break;
            case("subscribe"):
               use = subscribe;
               break;
            case("save"):
               use = save;
               break;
            case("report"):
                use = report;
                break;
            case("tag"):
                use = tag;
                break;
            case('comment'):
                use = comment;
                break;
            case('rate'):
                use = rate;
                break;
            case('getMore'):
                use = getMore;
                break;
           default:
                log.error("Attempt access an invalid action: '" + action + "'");
               break;
        }

        if (use)
            use(request).then(function(res) {
                resolve(res);
            }, function(res) {
                reject(res);
            });
    });
};

function save(request) {
    return new Promise(function(resolve, reject) {
        if (request.body.saved)
            saver.removeSave(request)
                .then(function() {resolve(true)}).catch(function() {reject(false)});
        else
            saver.save(request)
                .then(function() {resolve(true)}).catch(function() {reject('failure')});
    });
}

function subscribe(request) {
    return new Promise(function(resolve, reject) {
        if (request.body.subscribed)
            subscriber.cancelSubscription(request.signedCookies.usercookie.userID, request.body.itemId)
                .then(function() {resolve(true)}, function() {reject(false)});
        else
            subscriber.onSubscribed(request.body.itemId, request.signedCookies.usercookie.userID, request.body.contentType)
                .then(function() {resolve(true)}, function() {reject(false)});
    });
}

function vote(request) {
    return new Promise(function(resolve, reject) {
        if (request.body.voted)
            voter.updateVoteAndItem(request.signedCookies.usercookie.userID, request.body.itemId, request.body.value, request.body.type)
                .then(function() {resolve(true)}, function() {reject(false)});
        else
            voter.voteAndUpdateItem(request.signedCookies.usercookie.userID, request.body.itemId, request.body.value, request.body.type)
                .then(function() {resolve(true)}, function() {reject(false)});
    });
}

function report(request) {
    return new Promise(function(resolve, reject) {
        if (request.body.sub == 'enter')
            reporter.sendReport(request.signedCookies.usercookie.userID, request.body.itemId, request.body.reportReason, request.body.content)
                .then(function() {resolve(true)}, function() {reject(false)});
        else
            reporter.sendReport(request.body.reportId)
                .then(function() {resolve(true)}, function() {reject(false)});
    });
}

function tag(request) {
    return new Promise(function(resolve, reject){
        if (request.body.sub == "getArray")
            tagger.getArray().then(function(tags) {resolve(tags)}, function() {reject(false)});
        else if (request.body.sub == "getTag")
            tagger.getTag(request.body.id).then(function(tag) {resolve(tag)}, function() {reject(false)});
        else if (request.body.sub == "add")
            tagger.add(request.body.tagName).then(function() {resolve(true)}, function() {reject(false)});
        else
            log.error("Invalid request for tags");
    });
}

function comment(request) {
    return new Promise(function(resolve, reject){
        if (request.body.sub == "add")
            commenter.addComment(request).then(function(res) {resolve(res)}, function() {reject(false)});
        else if (request.body.sub == "edit")
            commenter.editComment(request).then(function() {resolve(true)}, function() {reject(false)});
        else if (request.body.sub == "delete")
            commenter.deleteComment(request).then(function() {resolve(true)}, function() {reject(false)});
        else
            log.error("Invalid request for commenting");
    });
}

function rate(request) {
    return new Promise(function(resolve, reject){
        if (request.body.sub == "add")
            rater.addRating(request).then(function(res) {resolve(res)}, function() {reject(false)});
        else if (request.body.sub == "edit")
            rater.editRating(request).then(function() {resolve(true)}, function() {reject(false)});
        else if (request.body.sub == "delete")
            rater.deleteRating(request).then(function() {resolve(true)}, function() {reject(false)});
        else
            log.error("Invalid request for rating");
    });
}

function getMore(request) {
    return new Promise(function(resolve, reject) {
        getter.getMore(request).then(function(res){resolve(res)}, function() {reject(false)})
            .catch(function(err) {console.error('Getter getMore error: ' + err)})
    })
}<commit_msg>Yellow Stickies :(<commit_after>"use strict";

/* actionResponder
 *
 * Written by Michael Albinson, 2/8/17
 *
 * A middleman for all actions that do not include the addition of true content to the database
 * and do not require information to be returned from the server, other than if the action was
 * successful or not.
 *
 * Implementations for each action can be found in the actions directory.
 *
 * i.e. votes, subscriptions, saves, reports etc.
 */

var log = require('./log');
var saver = require('./actions/Saver');
var subscriber = require('./actions/Subscriptions');
var voter = require('./actions/Voter');
var tagger = require('./actions/Tagger');
var commenter = require('./actions/Commenter');
var rater = require('./actions/Rater');

exports.respond = function(request) {
    return new Promise(function(resolve, reject) {
        var use = undefined;
        var action = request.body.action;
        switch(action) {
            case("vote"):
               use = vote;
               break;
            case("subscribe"):
               use = subscribe;
               break;
            case("save"):
               use = save;
               break;
            case("report"):
                use = report;
                break;
            case("tag"):
                use = tag;
                break;
            case('comment'):
                use = comment;
                break;
            case('rate'):
                use = rate;
                break;
            case('getMore'):
                use = getMore;
                break;
           default:
                log.error("Attempt access an invalid action: '" + action + "'");
               break;
        }

        if (use)
            use(request).then(function(res) {
                resolve(res);
            }, function(res) {
                reject(res);
            });
    });
};

function save(request) {
    return new Promise(function(resolve, reject) {
        if (request.body.saved)
            saver.removeSave(request)
                .then(function() {resolve(true)}).catch(function() {reject(false)});
        else
            saver.save(request)
                .then(function() {resolve(true)}).catch(function() {reject('failure')});
    });
}

function subscribe(request) {
    return new Promise(function(resolve, reject) {
        if (request.body.subscribed)
            subscriber.cancelSubscription(request.signedCookies.usercookie.userID, request.body.itemId)
                .then(function() {resolve(true)}, function() {reject(false)});
        else
            subscriber.onSubscribed(request.body.itemId, request.signedCookies.usercookie.userID, request.body.contentType)
                .then(function() {resolve(true)}, function() {reject(false)});
    });
}

function vote(request) {
    return new Promise(function(resolve, reject) {
        if (request.body.voted)
            voter.updateVoteAndItem(request.signedCookies.usercookie.userID, request.body.itemId, request.body.value, request.body.type)
                .then(function() {resolve(true)}, function() {reject(false)});
        else
            voter.voteAndUpdateItem(request.signedCookies.usercookie.userID, request.body.itemId, request.body.value, request.body.type)
                .then(function() {resolve(true)}, function() {reject(false)});
    });
}

function report(request) {
    return new Promise(function(resolve, reject) {
        if (request.body.sub === 'enter')
            reporter.sendReport(request.signedCookies.usercookie.userID, request.body.itemId, request.body.reportReason, request.body.content)
                .then(function() {resolve(true)}, function() {reject(false)});
        else
            reporter.sendReport(request.body.reportId)
                .then(function() {resolve(true)}, function() {reject(false)});
    });
}

function tag(request) {
    return new Promise(function(resolve, reject){
        if (request.body.sub === "getArray")
            tagger.getArray().then(function(tags) {resolve(tags)}, function() {reject(false)});
        else if (request.body.sub === "getTag")
            tagger.getTag(request.body.id).then(function(tag) {resolve(tag)}, function() {reject(false)});
        else if (request.body.sub === "add")
            tagger.add(request.body.tagName).then(function() {resolve(true)}, function() {reject(false)});
        else
            log.error("Invalid request for tags");
    });
}

function comment(request) {
    return new Promise(function(resolve, reject){
        if (request.body.sub === "add")
            commenter.addComment(request).then(function(res) {resolve(res)}, function() {reject(false)});
        else if (request.body.sub === "edit")
            commenter.editComment(request).then(function() {resolve(true)}, function() {reject(false)});
        else if (request.body.sub === "delete")
            commenter.deleteComment(request).then(function() {resolve(true)}, function() {reject(false)});
        else
            log.error("Invalid request for commenting");
    });
}

function rate(request) {
    return new Promise(function(resolve, reject){
        if (request.body.sub == "add")
            rater.addRating(request).then(function(res) {resolve(res)}, function() {reject(false)});
        else if (request.body.sub == "edit")
            rater.editRating(request).then(function() {resolve(true)}, function() {reject(false)});
        else if (request.body.sub == "delete")
            rater.deleteRating(request).then(function() {resolve(true)}, function() {reject(false)});
        else
            log.error("Invalid request for rating");
    });
}

function getMore(request) {
    return new Promise(function(resolve, reject) {
        getter.getMore(request).then(function(res){resolve(res)}, function() {reject(false)})
            .catch(function(err) {console.error('Getter getMore error: ' + err)})
    })
}<|endoftext|>


<commit_before>(function() {
    var originalFn = Ext.app.Application.prototype.onDependenciesLoaded;

    // @private
    Ext.define('Mba.ux.incubator.service.OverrideApplication', {
        override: 'Ext.app.Application',

        onDependenciesLoaded: function() {
            var controllers = this.getControllerInstances(),
                controller,
                controllerServices,
                services = [], name,
                me = this, callback;

            for (name in controllers) {
                controller = controllers[name];
                controllerServices = controller.getServices();
                services = services.concat(controllerServices);
            }

            callback = function() {
                var servicesIntances = me.instantiateServices(services);
                originalFn.apply(me);
                for (var i = 0, length = servicesIntances.length; i < length; i++) {
                    servicesIntances[i].init();
                }
            };

            Ext.require(services, callback);
        },

        instantiateServices: function(services) {
            var length  = services.length,
                service, serviceClass, serviceName,
                splits, i, servicesIntances = [];

            for (i = 0; i < length; i++) {
                service = services[i];

                if (Mba.ux.incubator.service && Mba.ux.incubator.service.Service &&
                    !(service instanceof BRBRelatorioVisita.service.Service)) {
                    if (Ext.isString(service)) {
                        serviceName = service;
                        serviceClass = Ext.ClassManager.classes[service];

                        service = {
                            xclass: service
                        };

                        if (serviceClass.prototype.defaultConfig.serviceId === undefined) {
                            splits = serviceName.split('.');
                            service.id = splits[splits.length - 1];
                        }
                    }

                    servicesIntances.push(Ext.factory(service, Mba.ux.incubator.service.Service));
                }
            }

            return servicesIntances;
        }
    });
})();
<commit_msg>- fix typo instanceof<commit_after>(function() {
    var originalFn = Ext.app.Application.prototype.onDependenciesLoaded;

    // @private
    Ext.define('Mba.ux.incubator.service.OverrideApplication', {
        override: 'Ext.app.Application',

        onDependenciesLoaded: function() {
            var controllers = this.getControllerInstances(),
                controller,
                controllerServices,
                services = [], name,
                me = this, callback;

            for (name in controllers) {
                controller = controllers[name];
                controllerServices = controller.getServices();
                services = services.concat(controllerServices);
            }

            callback = function() {
                var servicesIntances = me.instantiateServices(services);
                originalFn.apply(me);
                for (var i = 0, length = servicesIntances.length; i < length; i++) {
                    servicesIntances[i].init();
                }
            };

            Ext.require(services, callback);
        },

        instantiateServices: function(services) {
            var length  = services.length,
                service, serviceClass, serviceName,
                splits, i, servicesIntances = [];

            for (i = 0; i < length; i++) {
                service = services[i];

                if (Mba.ux.incubator.service && Mba.ux.incubator.service.Service &&
                    !(service instanceof Mba.ux.incubator.service.Service)) {
                    if (Ext.isString(service)) {
                        serviceName = service;
                        serviceClass = Ext.ClassManager.classes[service];

                        service = {
                            xclass: service
                        };

                        if (serviceClass.prototype.defaultConfig.serviceId === undefined) {
                            splits = serviceName.split('.');
                            service.id = splits[splits.length - 1];
                        }
                    }

                    servicesIntances.push(Ext.factory(service, Mba.ux.incubator.service.Service));
                }
            }

            return servicesIntances;
        }
    });
})();
<|endoftext|>


<commit_before>
// Based on Resig's pretty date
function prettyDate(time) {
  var diff = (Date.now() - time) / 1000;
  var day_diff = Math.floor(diff / 86400);

  if (isNaN(day_diff) || day_diff < 0 || day_diff >= 31)
    return '';

  return day_diff == 0 && (
          diff < 60 && 'just now' ||
          diff < 120 && '1 minute ago' ||
          diff < 3600 && Math.floor(diff / 60) + ' minutes ago' ||
          diff < 7200 && '1 hour ago' ||
          diff < 86400 && Math.floor(diff / 3600) + ' hours ago') ||
          day_diff == 1 && 'Yesterday' ||
          day_diff < 7 && day_diff + ' days ago' ||
          day_diff < 31 && Math.ceil(day_diff / 7) + ' weeks ago';
}

var MessageManager = {
  getMessages: function mm_getMessages(callback, filter, invert) {
    // XXX Bug 712809
    // Until there is a database for mozSms, use a fake GetMessages
    if (true) {
      GetMessagesHack(callback, filter, invert);
      return;
    }

    var request = navigator.mozSms.getMessages(filter, !invert);

    var messages = [];
    request.onsuccess = function() {
      var result = request.result;
      if (!result) {
        callback(messages);
        return;
      }

      var message = result.message;
      messages.push(message);
      result.next();
    };

    request.onerror = function() {
      alert('Error reading the database. Error code: ' + request.errorCode);
    }
  },

  send: function mm_send(number, text, callback) {
    var result = navigator.mozSms.send(number, text);
    result.onsuccess = function (event) { callback(event.message); };
    result.onerror = function (event) {
      console.log("Error sending SMS!");
      callback(null);
    };
  },

  delete: function mm_delete(id) {
    navigator.mozSms.delete(id);
  }
};

// Until there is a database to store messages on the device, return
// a fake list of messages.
var messagesHack = [];
(function() {
  var messages = [
    {
      sender: null,
      receiver: '1-977-743-6797',
      body: 'Nothing :)',
      timestamp: Date.now() - 44000000
    },
    {
      sender: '1-977-743-6797',
      body: 'Hey! What\s up?',
      timestamp: Date.now() - 50000000
    }
  ];

  for (var i = 0; i < 40; i++) {
    messages.push({
      sender: '1-488-678-3487',
      body: 'Hello world!',
      timestamp: Date.now() - 60000000
    });
  }

  messagesHack = messages;
})();
var GetMessagesHack = function(callback, filter, invert) {
  function applyFilter(msgs) {
    if (!filter)
      return msgs;

    if (filter.number) {
      msgs = msgs.filter(function(element, index, array) {
          return (filter.number && (filter.number == element.sender ||
                  filter.number == element.receiver));
      });
    }

    return msgs;
  }

  var msg = messagesHack.slice();
  if (invert)
    msg.reverse();
  callback(applyFilter(msg));
};

// Use a fake send if mozSms is not present
if (!navigator.mozSms) {
  MessageManager.send = function(number, text, callback) {
    var message = {
      sender: null,
      receiver: number,
      body: text,
      timestamp: Date.now()
    };
    window.setTimeout(function() { callback(message); }, 0);
  };
}

var MessageView = {
  init: function init() {
    if (navigator.mozSms)
      navigator.mozSms.addEventListener('received', this);
    this.showConversations();

    var readyEvent = document.createEvent('CustomEvent');
    readyEvent.initCustomEvent('appready', true, true, null);
    window.dispatchEvent(readyEvent);
  },

  get conversationView() {
    delete this.conversationView;
    return this.conversationView = document.getElementById('conversation');
  },

  openConversationView: function openConversationView(num) {
    if (!num)
      return;

    ConversationView.showConversation(num == '*' ? '' : num);

    var conversationView = document.getElementById('conversationView');
    conversationView.hidden = false;

    window.setTimeout(function conversationSlideIn() {
      conversationView.classList.remove('slideOut');
      conversationView.classList.add('slideIn');
    }, 100);
  },

  get view() {
    delete this.view;
    return this.view = document.getElementById('messages');
  },

  showConversations: function showConversations() {
    var self = this;
    MessageManager.getMessages(function(messages) {
      var conversations = {};
      for (var i = 0; i < messages.length; i++) {
        var message = messages[i];
        var sender = message.sender || message.receiver;
        if (conversations[sender]) {
          conversations[sender].count++;
          continue;
        }

        conversations[sender] = {
          sender: message.sender,
          receiver: message.receiver,
          body: message.body,
          timestamp: prettyDate(message.timestamp),
          count: 1
        };
      }

      var fragment = '
' +
                     '
New Message
' +
                     '
Write a message
' +
                     '
';
      for (var conversation in conversations) {
        var msg = self.createNewMessage(conversations[conversation]);
        fragment += msg;
      }
      self.view.innerHTML = fragment;
    }, null);
  },

  createNewMessage: function createNewMessage(msg) {
    var className = 'class="message ' +
                    (msg.sender ? 'sender' : 'receiver') + '"';

    var num = (msg.sender || msg.receiver);
    var dataNum = 'data-num="' + num + '"';

    var contacts = window.navigator.mozContacts.contacts;
    contacts.forEach(function(contact) {
      if (contact.phones[0] == num)
        num = contact.displayName;
    });
    var title = num + ' (' + msg.count + ')';

    return '
' +
           '
' +
           '
' + title + '
' +
           '
' +
           '      ' + msg.body + '' +
           '      ' + msg.timestamp + '' +
           '
' +
           '
' +
           '
';
  },

  handleEvent: function handleEvent(evt) {
    switch (evt.type) {
      case 'received':
        window.setTimeout(function() { MessageView.showConversations(); }, 0);
        break;
    }
  }
};

var ConversationView = {
  get view() {
    delete this.view;
    return this.view = document.getElementById('conversation');
  },

  init: function cv_init() {
    window.addEventListener('keypress', this, true);
    if (navigator.mozSms)
      navigator.mozSms.addEventListener('received', this);
  },

  showConversation: function cv_showConversation(num) {
    var contact = document.getElementById('contact');
    contact.value = num;

    this.filter = num;
    if (!this.filter) {
      contact.classList.remove('filtered');
      this.view.innerHTML = '';
      return;
    }
    contact.classList.add('filtered');

    var view = this.view;
    var filter = ('SmsFilter' in window) ? new SmsFilter() : {};
    filter.number = this.filter;

    view.innerHTML = '';
    MessageManager.getMessages(function mm_getMessages(messages) {
      var fragment = '';
      for (var i = 0; i < messages.length; i++) {
        var msg = messages[i];
        var uuid = msg.hasOwnProperty('uuid') ? msg.uuid : '';
        var dataId = 'data-id="' + uuid + '"';

        var dataNum = 'data-num="' + (msg.sender || msg.receiver) + '"';

        var className = 'class="message ' +
                        (msg.sender ? 'sender' : 'receiver') + '"';

        var time = prettyDate(msg.timestamp);
        fragment += '
' +
                    '

' +
                    '
' +
                    '    ' + msg.body + '' +
                    '    ' + time + '' +
                    '
' +
                    '
';
      }

      view.innerHTML = fragment;
      setTimeout(function() {
        view.scrollTop = view.scrollHeight;
      }, 0);
    }, filter, true);
  },

  deleteMessage: function deleteMessage(evt) {
    var uuid = evt.target.getAttribute('data-id');
    if (!uuid)
      return;

    MessageManager.delete(uuid);
    this.showConversation(this.filter);
  },

  handleEvent: function handleEvent(evt) {
    switch (evt.type) {
      case 'keypress':
        if (evt.keyCode != evt.DOM_VK_ESCAPE)
          return;

        if (this.close())
          evt.preventDefault();
      case 'received':
        var message = evt.message;
        console.log('Received message from ' + message.sender + ': ' +
                    message.body);
        messagesHack.push(message);
        window.setTimeout(function () {
          ConversationView.showConversation(ConversationView.filter);
        }, 0);
        break;
    }
  },
  close: function cv_close() {
    var view = document.getElementById('conversationView');
    if (view.hidden)
      return false;

    view.classList.remove('slideIn');
    view.classList.add('slideOut');

    view.addEventListener('transitionend', function slideOut(evt) {
      view.removeEventListener('transitionend', slideOut);
      var text = document.getElementById('text');
      text.value = text.style.height = '';

      view.hidden = true;
    });
    return true;
  },
  sendMessage: function cv_sendMessage() {
    var contact = document.getElementById('contact');
    var text = document.getElementById('text');
    if (contact.value == '' || text.value == '')
      return;

    MessageManager.send(contact.value, text.value, function(msg) {
      // There was an error. We should really do some error handling here.
      // or in send() or wherever.
      if (!msg)
        return;

      // Copy all the information from the actual message object to the
      // preliminary message object. Then update the view.
      for (var key in msg)
        message[msg] = msg[key];

      if (ConversationView.filter)
        ConversationView.showConversation(ConversationView.filter);
    });

    // Create a preliminary message object and update the view right away.
    var message = {
      sender: null,
      receiver: contact.value,
      body: text.value
    };
    messagesHack.push(message);

    text.value = "";
    if (ConversationView.filter) {
      ConversationView.showConversation(ConversationView.filter);
      return;
    }
    ConversationView.close();
    MessageView.showConversations();
  }
};

ConversationView.init();

function onKeyPress(evt) {
  var target = evt.originalTarget;
  setTimeout(function() {
    target.style.height = '';
    target.style.height = '-moz-calc(' + target.scrollHeight + 'px + 32px)';
  }, 0);
}
<commit_msg>Address nits.<commit_after>
// Based on Resig's pretty date
function prettyDate(time) {
  var diff = (Date.now() - time) / 1000;
  var day_diff = Math.floor(diff / 86400);

  if (isNaN(day_diff) || day_diff < 0 || day_diff >= 31)
    return '';

  return day_diff == 0 && (
          diff < 60 && 'just now' ||
          diff < 120 && '1 minute ago' ||
          diff < 3600 && Math.floor(diff / 60) + ' minutes ago' ||
          diff < 7200 && '1 hour ago' ||
          diff < 86400 && Math.floor(diff / 3600) + ' hours ago') ||
          day_diff == 1 && 'Yesterday' ||
          day_diff < 7 && day_diff + ' days ago' ||
          day_diff < 31 && Math.ceil(day_diff / 7) + ' weeks ago';
}

var MessageManager = {
  getMessages: function mm_getMessages(callback, filter, invert) {
    // XXX Bug 712809
    // Until there is a database for mozSms, use a fake GetMessages
    if (true) {
      GetMessagesHack(callback, filter, invert);
      return;
    }

    var request = navigator.mozSms.getMessages(filter, !invert);

    var messages = [];
    request.onsuccess = function() {
      var result = request.result;
      if (!result) {
        callback(messages);
        return;
      }

      var message = result.message;
      messages.push(message);
      result.next();
    };

    request.onerror = function() {
      alert('Error reading the database. Error code: ' + request.errorCode);
    }
  },

  send: function mm_send(number, text, callback) {
    var result = navigator.mozSms.send(number, text);
    result.onsuccess = function onsuccess(event) {
      callback(event.message);
    };
    result.onerror = function onerror(event) {
      console.log("Error sending SMS!");
      callback(null);
    };
  },

  delete: function mm_delete(id) {
    navigator.mozSms.delete(id);
  }
};

// Until there is a database to store messages on the device, return
// a fake list of messages.
var messagesHack = [];
(function() {
  var messages = [
    {
      sender: null,
      receiver: '1-977-743-6797',
      body: 'Nothing :)',
      timestamp: Date.now() - 44000000
    },
    {
      sender: '1-977-743-6797',
      body: 'Hey! What\s up?',
      timestamp: Date.now() - 50000000
    }
  ];

  for (var i = 0; i < 40; i++) {
    messages.push({
      sender: '1-488-678-3487',
      body: 'Hello world!',
      timestamp: Date.now() - 60000000
    });
  }

  messagesHack = messages;
})();
var GetMessagesHack = function(callback, filter, invert) {
  function applyFilter(msgs) {
    if (!filter)
      return msgs;

    if (filter.number) {
      msgs = msgs.filter(function(element, index, array) {
          return (filter.number && (filter.number == element.sender ||
                  filter.number == element.receiver));
      });
    }

    return msgs;
  }

  var msg = messagesHack.slice();
  if (invert)
    msg.reverse();
  callback(applyFilter(msg));
};

// Use a fake send if mozSms is not present
if (!navigator.mozSms) {
  MessageManager.send = function mm_send(number, text, callback) {
    var message = {
      sender: null,
      receiver: number,
      body: text,
      timestamp: Date.now()
    };
    window.setTimeout(function() {
      callback(message);
    }, 0);
  };
}

var MessageView = {
  init: function init() {
    if (navigator.mozSms)
      navigator.mozSms.addEventListener('received', this);
    this.showConversations();

    var readyEvent = document.createEvent('CustomEvent');
    readyEvent.initCustomEvent('appready', true, true, null);
    window.dispatchEvent(readyEvent);
  },

  get conversationView() {
    delete this.conversationView;
    return this.conversationView = document.getElementById('conversation');
  },

  openConversationView: function openConversationView(num) {
    if (!num)
      return;

    ConversationView.showConversation(num == '*' ? '' : num);

    var conversationView = document.getElementById('conversationView');
    conversationView.hidden = false;

    window.setTimeout(function conversationSlideIn() {
      conversationView.classList.remove('slideOut');
      conversationView.classList.add('slideIn');
    }, 100);
  },

  get view() {
    delete this.view;
    return this.view = document.getElementById('messages');
  },

  showConversations: function showConversations() {
    var self = this;
    MessageManager.getMessages(function(messages) {
      var conversations = {};
      for (var i = 0; i < messages.length; i++) {
        var message = messages[i];
        var sender = message.sender || message.receiver;
        if (conversations[sender]) {
          conversations[sender].count++;
          continue;
        }

        conversations[sender] = {
          sender: message.sender,
          receiver: message.receiver,
          body: message.body,
          timestamp: prettyDate(message.timestamp),
          count: 1
        };
      }

      var fragment = '
' +
                     '
New Message
' +
                     '
Write a message
' +
                     '
';
      for (var conversation in conversations) {
        var msg = self.createNewMessage(conversations[conversation]);
        fragment += msg;
      }
      self.view.innerHTML = fragment;
    }, null);
  },

  createNewMessage: function createNewMessage(msg) {
    var className = 'class="message ' +
                    (msg.sender ? 'sender' : 'receiver') + '"';

    var num = (msg.sender || msg.receiver);
    var dataNum = 'data-num="' + num + '"';

    var contacts = window.navigator.mozContacts.contacts;
    contacts.forEach(function(contact) {
      if (contact.phones[0] == num)
        num = contact.displayName;
    });
    var title = num + ' (' + msg.count + ')';

    return '
' +
           '
' +
           '
' + title + '
' +
           '
' +
           '      ' + msg.body + '' +
           '      ' + msg.timestamp + '' +
           '
' +
           '
' +
           '
';
  },

  handleEvent: function handleEvent(evt) {
    switch (evt.type) {
      case 'received':
        window.setTimeout(function() {
          MessageView.showConversations();
        }, 0);
        break;
    }
  }
};

var ConversationView = {
  get view() {
    delete this.view;
    return this.view = document.getElementById('conversation');
  },

  init: function cv_init() {
    window.addEventListener('keypress', this, true);
    if (navigator.mozSms)
      navigator.mozSms.addEventListener('received', this);
  },

  showConversation: function cv_showConversation(num) {
    var contact = document.getElementById('contact');
    contact.value = num;

    this.filter = num;
    if (!this.filter) {
      contact.classList.remove('filtered');
      this.view.innerHTML = '';
      return;
    }
    contact.classList.add('filtered');

    var view = this.view;
    var filter = ('SmsFilter' in window) ? new SmsFilter() : {};
    filter.number = this.filter;

    view.innerHTML = '';
    MessageManager.getMessages(function mm_getMessages(messages) {
      var fragment = '';
      for (var i = 0; i < messages.length; i++) {
        var msg = messages[i];
        var uuid = msg.hasOwnProperty('uuid') ? msg.uuid : '';
        var dataId = 'data-id="' + uuid + '"';

        var dataNum = 'data-num="' + (msg.sender || msg.receiver) + '"';

        var className = 'class="message ' +
                        (msg.sender ? 'sender' : 'receiver') + '"';

        var time = prettyDate(msg.timestamp);
        fragment += '
' +
                    '

' +
                    '
' +
                    '    ' + msg.body + '' +
                    '    ' + time + '' +
                    '
' +
                    '
';
      }

      view.innerHTML = fragment;
      setTimeout(function() {
        view.scrollTop = view.scrollHeight;
      }, 0);
    }, filter, true);
  },

  deleteMessage: function deleteMessage(evt) {
    var uuid = evt.target.getAttribute('data-id');
    if (!uuid)
      return;

    MessageManager.delete(uuid);
    this.showConversation(this.filter);
  },

  handleEvent: function handleEvent(evt) {
    switch (evt.type) {
      case 'keypress':
        if (evt.keyCode != evt.DOM_VK_ESCAPE)
          return;

        if (this.close())
          evt.preventDefault();
      case 'received':
        var message = evt.message;
        console.log('Received message from ' + message.sender + ': ' +
                    message.body);
        messagesHack.push(message);
        window.setTimeout(function () {
          ConversationView.showConversation(ConversationView.filter);
        }, 0);
        break;
    }
  },
  close: function cv_close() {
    var view = document.getElementById('conversationView');
    if (view.hidden)
      return false;

    view.classList.remove('slideIn');
    view.classList.add('slideOut');

    view.addEventListener('transitionend', function slideOut(evt) {
      view.removeEventListener('transitionend', slideOut);
      var text = document.getElementById('text');
      text.value = text.style.height = '';

      view.hidden = true;
    });
    return true;
  },
  sendMessage: function cv_sendMessage() {
    var contact = document.getElementById('contact');
    var text = document.getElementById('text');
    if (contact.value == '' || text.value == '')
      return;

    MessageManager.send(contact.value, text.value, function onsent(msg) {
      // There was an error. We should really do some error handling here.
      // or in send() or wherever.
      if (!msg)
        return;

      // Copy all the information from the actual message object to the
      // preliminary message object. Then update the view.
      for (var key in msg)
        message[msg] = msg[key];

      if (ConversationView.filter)
        ConversationView.showConversation(ConversationView.filter);
    });

    // Create a preliminary message object and update the view right away.
    var message = {
      sender: null,
      receiver: contact.value,
      body: text.value
    };
    messagesHack.push(message);

    text.value = "";
    if (ConversationView.filter) {
      ConversationView.showConversation(ConversationView.filter);
      return;
    }
    ConversationView.close();
    MessageView.showConversations();
  }
};

ConversationView.init();

function onKeyPress(evt) {
  var target = evt.originalTarget;
  setTimeout(function() {
    target.style.height = '';
    target.style.height = '-moz-calc(' + target.scrollHeight + 'px + 32px)';
  }, 0);
}
<|endoftext|>


<commit_before>// @flow
import type {
  AuthClient,
  CustomClient,
  ClientPool,
  EntityClient,
  SessionClient,
} from './decls/client.js.flow'
import type {
  CommandResult,
  CommandResultOrError,
  CustomCommandResult,
  CustomCommandResultOrError,
  FetchCommandResult,
  FetchCommandResultOrError,
  GetCommandResult,
  GetCommandResultOrError,
  LoginCommandResult,
  LoginCommandResultOrError,
  LogoutCommandResult,
  LogoutCommandResultOrError,
} from './decls/command-result.js.flow'
import type {
  CustomCommand,
  DeleteCommand,
  IdDeleteCommand,
  IdUpdateCommand,
  InsertCommand,
  LoginCommand,
  LogoutCommand,
  MultiDeleteCommand,
  MultiInsertCommand,
  MultiUpdateCommand,
  SingleInsertCommand,
  UpdateCommand,
} from './decls/command.js.flow'
import type {
  CustomCommandDefinition,
  CustomCommandDefinitions,
} from './decls/custom-command-definition.js.flow'
import type {
  CustomQueryDefinition,
  CustomQueryDefinitions,
} from './decls/custom-query-definition.js.flow'
import type { DotNotationString } from './decls/dot-notation-string.js.flow'
import type { EntityDefinition, EntityDefinitions } from './decls/entity-definition.js.flow'
import type {
  ErrorResult,
  ErrorResultType,
} from './decls/error-result.js.flow'
import type { FunctionalGroup } from './decls/functional-group.js.flow'
import type {
  AclHandler,
  AuthenticationHandler,
  AuthenticationResult,
  CustomCommandHandler,
  CoreExecution,
  CustomQueryHandler,
  ExecutionWrapper,
  NGAuthenticationResult,
  OKAuthenticationResult,
  ValidationHandler,
} from './decls/handler.js.flow'
import type {
  EncodedHttpRequest,
  EncodedHttpResponse,
  HttpMethod,
  QueryStringParams,
} from './decls/http.js.flow'
import type { Id } from './decls/id.js.flow'
import type { KvsClient } from './decls/kvs-client.js.flow'
import { PhenylRunner } from './decls/phenyl-runner.js.flow'
import type {
  BSONTypeNumber,
  BSONTypeString,
  EqCondition,
  QueryCondition,
} from './decls/query-condition.js.flow'
import type {
  CustomQueryResult,
  CustomQueryResultOrError,
  QueryResult,
  QueryResultOrError,
  SingleQueryResult,
  SingleQueryResultOrError,
} from './decls/query-result.js.flow'
import type {
  CustomQuery,
  IdQuery,
  IdsQuery,
  WhereQuery,
} from './decls/query.js.flow'
import type {
  RequestData,
  RequestMethodName,
  FindRequestData,
  FindOneRequestData,
  GetRequestData,
  GetByIdsRequestData,
  InsertRequestData,
  InsertAndGetRequestData,
  InsertAndFetchRequestData,
  UpdateRequestData,
  UpdateAndGetRequestData,
  UpdateAndFetchRequestData,
  DeleteRequestData,
  RunCustomQueryRequestData,
  RunCustomCommandRequestData,
  LoginRequestData,
  LogoutRequestData,
} from './decls/request-data.js.flow'
import type {
  ResponseData,
  FindResponseData,
  FindOneResponseData,
  GetResponseData,
  GetByIdsResponseData,
  InsertResponseData,
  InsertAndGetResponseData,
  InsertAndFetchResponseData,
  UpdateResponseData,
  UpdateAndGetResponseData,
  UpdateAndFetchResponseData,
  DeleteResponseData,
  RunCustomQueryResponseData,
  RunCustomCommandResponseData,
  LoginResponseData,
  LogoutResponseData,

} from './decls/response-data.js.flow'
import type {
  Restorable,
  RestorableEntity,
} from './decls/restorable.js.flow'
import type { PreSession, Session } from './decls/session.js.flow'
import type { SortNotation } from './decls/sort-notation.js.flow'
import type {
  AddToSetOperator,
  BitOperator,
  CurrentDateOperator,
  IncOperator,
  MaxOperator,
  MinOperator,
  MulOperator,
  PopOperator,
  PullOperator,
  PushModifier,
  PushOperator,
  SetOperator,
  UpdateOperators,
} from './decls/update-operators.js.flow'
import type {
  UserDefinition,
  UserDefinitions,
} from './decls/user-definition.js.flow'
import type { WhereConditions } from './decls/where-conditions.js.flow'

export type {
  AclHandler,
  AddToSetOperator,
  AuthenticationHandler,
  AuthenticationResult,
  AuthClient,
  BitOperator,
  BSONTypeNumber,
  BSONTypeString,
  ClientPool,
  CommandResult,
  CommandResultOrError,
  CoreExecution,
  CurrentDateOperator,
  CustomClient,
  CustomCommand,
  CustomCommandHandler,
  CustomCommandResult,
  CustomCommandResultOrError,
  CustomCommandDefinition,
  CustomCommandDefinitions,
  CustomQuery,
  CustomQueryHandler,
  CustomQueryResult,
  CustomQueryResultOrError,
  CustomQueryDefinition,
  CustomQueryDefinitions,
  DeleteCommand,
  DeleteRequestData,
  DeleteResponseData,
  DotNotationString,
  EncodedHttpRequest,
  EncodedHttpResponse,
  EntityClient,
  EntityDefinition,
  EntityDefinitions
  EqCondition,
  ErrorResult,
  ErrorResultType,
  ExecutionWrapper,
  FetchCommandResult,
  FetchCommandResultOrError,
  FindOneRequestData,
  FindOneResponseData,
  FindRequestData,
  FindResponseData,
  FunctionalGroup,
  GetByIdsRequestData,
  GetByIdsResponseData,
  GetCommandResult,
  GetCommandResultOrError,
  GetRequestData,
  GetResponseData,
  HttpMethod,
  Id,
  IdQuery,
  IdsQuery,
  IdDeleteCommand,
  IdUpdateCommand,
  IncOperator,
  InsertAndFetchRequestData,
  InsertAndFetchResponseData,
  InsertAndGetRequestData,
  InsertAndGetResponseData,
  InsertCommand,
  InsertRequestData,
  InsertResponseData,
  KvsClient,
  LoginCommand,
  LoginCommandResult,
  LoginCommandResultOrError,
  LoginRequestData,
  LoginResponseData,
  LogoutCommand,
  LogoutCommandResult,
  LogoutCommandResultOrError,
  LogoutRequestData,
  LogoutResponseData,
  MaxOperator,
  MinOperator,
  MulOperator,
  MultiDeleteCommand,
  MultiInsertCommand,
  MultiUpdateCommand,
  NGAuthenticationResult,
  OKAuthenticationResult,
  PreSession,
  PhenylRunner,
  PopOperator,
  PullOperator,
  PushModifier,
  PushOperator,
  QueryCondition,
  QueryResult,
  QueryResultOrError,
  QueryStringParams,
  RequestData,
  RequestMethodName,
  ResponseData,
  Restorable,
  RestorableEntity,
  RunCustomCommandRequestData,
  RunCustomCommandResponseData,
  RunCustomQueryRequestData,
  RunCustomQueryResponseData,
  SetOperator,
  Session,
  SessionClient,
  SingleInsertCommand,
  SingleQueryResult,
  SingleQueryResultOrError,
  SortNotation,
  UpdateAndFetchRequestData,
  UpdateAndFetchResponseData,
  UpdateAndGetRequestData,
  UpdateAndGetResponseData,
  UpdateCommand,
  UpdateOperators,
  UpdateRequestData,
  UpdateResponseData,
  UserDefinition,
  UserDefinitions,
  ValidationHandler,
  WhereConditions,
  WhereQuery,
}
<commit_msg>[phenyl-interfaces] export WhereConditions subtypes<commit_after>// @flow
import type {
  AuthClient,
  CustomClient,
  ClientPool,
  EntityClient,
  SessionClient,
} from './decls/client.js.flow'
import type {
  CommandResult,
  CommandResultOrError,
  CustomCommandResult,
  CustomCommandResultOrError,
  FetchCommandResult,
  FetchCommandResultOrError,
  GetCommandResult,
  GetCommandResultOrError,
  LoginCommandResult,
  LoginCommandResultOrError,
  LogoutCommandResult,
  LogoutCommandResultOrError,
} from './decls/command-result.js.flow'
import type {
  CustomCommand,
  DeleteCommand,
  IdDeleteCommand,
  IdUpdateCommand,
  InsertCommand,
  LoginCommand,
  LogoutCommand,
  MultiDeleteCommand,
  MultiInsertCommand,
  MultiUpdateCommand,
  SingleInsertCommand,
  UpdateCommand,
} from './decls/command.js.flow'
import type {
  CustomCommandDefinition,
  CustomCommandDefinitions,
} from './decls/custom-command-definition.js.flow'
import type {
  CustomQueryDefinition,
  CustomQueryDefinitions,
} from './decls/custom-query-definition.js.flow'
import type { DotNotationString } from './decls/dot-notation-string.js.flow'
import type { EntityDefinition, EntityDefinitions } from './decls/entity-definition.js.flow'
import type {
  ErrorResult,
  ErrorResultType,
} from './decls/error-result.js.flow'
import type { FunctionalGroup } from './decls/functional-group.js.flow'
import type {
  AclHandler,
  AuthenticationHandler,
  AuthenticationResult,
  CustomCommandHandler,
  CoreExecution,
  CustomQueryHandler,
  ExecutionWrapper,
  NGAuthenticationResult,
  OKAuthenticationResult,
  ValidationHandler,
} from './decls/handler.js.flow'
import type {
  EncodedHttpRequest,
  EncodedHttpResponse,
  HttpMethod,
  QueryStringParams,
} from './decls/http.js.flow'
import type { Id } from './decls/id.js.flow'
import type { KvsClient } from './decls/kvs-client.js.flow'
import { PhenylRunner } from './decls/phenyl-runner.js.flow'
import type {
  BSONTypeNumber,
  BSONTypeString,
  EqCondition,
  QueryCondition,
} from './decls/query-condition.js.flow'
import type {
  CustomQueryResult,
  CustomQueryResultOrError,
  QueryResult,
  QueryResultOrError,
  SingleQueryResult,
  SingleQueryResultOrError,
} from './decls/query-result.js.flow'
import type {
  CustomQuery,
  IdQuery,
  IdsQuery,
  WhereQuery,
} from './decls/query.js.flow'
import type {
  RequestData,
  RequestMethodName,
  FindRequestData,
  FindOneRequestData,
  GetRequestData,
  GetByIdsRequestData,
  InsertRequestData,
  InsertAndGetRequestData,
  InsertAndFetchRequestData,
  UpdateRequestData,
  UpdateAndGetRequestData,
  UpdateAndFetchRequestData,
  DeleteRequestData,
  RunCustomQueryRequestData,
  RunCustomCommandRequestData,
  LoginRequestData,
  LogoutRequestData,
} from './decls/request-data.js.flow'
import type {
  ResponseData,
  FindResponseData,
  FindOneResponseData,
  GetResponseData,
  GetByIdsResponseData,
  InsertResponseData,
  InsertAndGetResponseData,
  InsertAndFetchResponseData,
  UpdateResponseData,
  UpdateAndGetResponseData,
  UpdateAndFetchResponseData,
  DeleteResponseData,
  RunCustomQueryResponseData,
  RunCustomCommandResponseData,
  LoginResponseData,
  LogoutResponseData,

} from './decls/response-data.js.flow'
import type {
  Restorable,
  RestorableEntity,
} from './decls/restorable.js.flow'
import type { PreSession, Session } from './decls/session.js.flow'
import type { SortNotation } from './decls/sort-notation.js.flow'
import type {
  AddToSetOperator,
  BitOperator,
  CurrentDateOperator,
  IncOperator,
  MaxOperator,
  MinOperator,
  MulOperator,
  PopOperator,
  PullOperator,
  PushModifier,
  PushOperator,
  SetOperator,
  UpdateOperators,
} from './decls/update-operators.js.flow'
import type {
  UserDefinition,
  UserDefinitions,
} from './decls/user-definition.js.flow'
import type {
  AndWhereConditions,
  NorWhereConditions,
  OrWhereConditions,
  SimpleWhereConditions,
  WhereConditions,
} from './decls/where-conditions.js.flow'

export type {
  AclHandler,
  AddToSetOperator,
  AndWhereConditions,
  AuthenticationHandler,
  AuthenticationResult,
  AuthClient,
  BitOperator,
  BSONTypeNumber,
  BSONTypeString,
  ClientPool,
  CommandResult,
  CommandResultOrError,
  CoreExecution,
  CurrentDateOperator,
  CustomClient,
  CustomCommand,
  CustomCommandHandler,
  CustomCommandResult,
  CustomCommandResultOrError,
  CustomCommandDefinition,
  CustomCommandDefinitions,
  CustomQuery,
  CustomQueryHandler,
  CustomQueryResult,
  CustomQueryResultOrError,
  CustomQueryDefinition,
  CustomQueryDefinitions,
  DeleteCommand,
  DeleteRequestData,
  DeleteResponseData,
  DotNotationString,
  EncodedHttpRequest,
  EncodedHttpResponse,
  EntityClient,
  EntityDefinition,
  EntityDefinitions
  EqCondition,
  ErrorResult,
  ErrorResultType,
  ExecutionWrapper,
  FetchCommandResult,
  FetchCommandResultOrError,
  FindOneRequestData,
  FindOneResponseData,
  FindRequestData,
  FindResponseData,
  FunctionalGroup,
  GetByIdsRequestData,
  GetByIdsResponseData,
  GetCommandResult,
  GetCommandResultOrError,
  GetRequestData,
  GetResponseData,
  HttpMethod,
  Id,
  IdQuery,
  IdsQuery,
  IdDeleteCommand,
  IdUpdateCommand,
  IncOperator,
  InsertAndFetchRequestData,
  InsertAndFetchResponseData,
  InsertAndGetRequestData,
  InsertAndGetResponseData,
  InsertCommand,
  InsertRequestData,
  InsertResponseData,
  KvsClient,
  LoginCommand,
  LoginCommandResult,
  LoginCommandResultOrError,
  LoginRequestData,
  LoginResponseData,
  LogoutCommand,
  LogoutCommandResult,
  LogoutCommandResultOrError,
  LogoutRequestData,
  LogoutResponseData,
  MaxOperator,
  MinOperator,
  MulOperator,
  MultiDeleteCommand,
  MultiInsertCommand,
  MultiUpdateCommand,
  NGAuthenticationResult,
  NorWhereConditions,
  OKAuthenticationResult,
  OrWhereConditions,
  PreSession,
  PhenylRunner,
  PopOperator,
  PullOperator,
  PushModifier,
  PushOperator,
  QueryCondition,
  QueryResult,
  QueryResultOrError,
  QueryStringParams,
  RequestData,
  RequestMethodName,
  ResponseData,
  Restorable,
  RestorableEntity,
  RunCustomCommandRequestData,
  RunCustomCommandResponseData,
  RunCustomQueryRequestData,
  RunCustomQueryResponseData,
  SetOperator,
  Session,
  SessionClient,
  SimpleWhereConditions,
  SingleInsertCommand,
  SingleQueryResult,
  SingleQueryResultOrError,
  SortNotation,
  UpdateAndFetchRequestData,
  UpdateAndFetchResponseData,
  UpdateAndGetRequestData,
  UpdateAndGetResponseData,
  UpdateCommand,
  UpdateOperators,
  UpdateRequestData,
  UpdateResponseData,
  UserDefinition,
  UserDefinitions,
  ValidationHandler,
  WhereConditions,
  WhereQuery,
}
<|endoftext|>


<commit_before>/* global kirkiPostMessageFields, WebFont */
var kirkiPostMessage = {

	/**
	 * The fields.
	 *
	 * @since 3.0.26
	 */
	fields: {},

	/**
	 * A collection of methods for the <style> tags.
	 *
	 * @since 3.0.26
	 */
	styleTag: {

		/**
		 * Add a <style> tag in <head> if it doesn't already exist.
		 *
		 * @since 3.0.26
		 * @param {string} id - The field-ID.
		 * @returns {void}
		 */
		add: function( id ) {
			if ( null === document.getElementById( 'kirki-postmessage-' + id ) || 'undefined' === typeof document.getElementById( 'kirki-postmessage-' + id ) ) {
				jQuery( 'head' ).append( '<style id="kirki-postmessage-' + id + '"></style>' );
			}
		},

		/**
		 * Add a <style> tag in <head> if it doesn't already exist,
		 * by calling the this.add method, and then add styles inside it.
		 *
		 * @since 3.0.26
		 * @param {string} id - The field-ID.
		 * @param {string} styles - The styles to add.
		 * @returns {void}
		 */
		addData: function( id, styles ) {
			kirkiPostMessage.styleTag.add( id );
			jQuery( '#kirki-postmessage-' + id ).text( styles );
		}
	},

	/**
	 * Common utilities.
	 *
	 * @since 3.0.26
	 */
	util: {

		/**
		 * Processes the value and applies any replacements and/or additions.
		 *
		 * @since 3.0.26
		 * @param {Object} output - The output (js_vars) argument.
		 * @param {mixed}  value - The value.
		 * @param {string} controlType - The control-type.
		 * @returns {string}
		 */
		processValue: function( output, value ) {
			var self     = this,
				settings = window.parent.wp.customize.get();

			if ( 'string' !== typeof value ) {
				_.each( value, function( subValue, key ) {
					value[ key ] = self.processValue( output, subValue );
				} );
				return value;
			}
			output = _.defaults( output, {
				prefix: '',
				units: '',
				suffix: '',
				value_pattern: '$',
				pattern_replace: {}
			} );

			value = output.value_pattern.replace( new RegExp( '\\$', 'g' ), value );
			_.each( output.pattern_replace, function( id, placeholder ) {
				if ( ! _.isUndefined( settings[ id ] ) ) {
					value = value.replace( placeholder, settings[ id ] );
				}
			} );
			return output.prefix + value + output.units + output.suffix;
		},

		/**
		 * Make sure urls are properly formatted for background-image properties.
		 *
		 * @since 3.0.26
		 * @param {string} url - The URL.
		 * @returns {string}
		 */
		backgroundImageValue: function( url ) {
			return ( -1 === url.indexOf( 'url(' ) ) ? 'url(' + url + ')' : url;
		}
	},

	/**
	 * A collection of utilities for CSS generation.
	 *
	 * @since 3.0.26
	 */
	css: {

		/**
		 * Generates the CSS from the output (js_vars) parameter.
		 *
		 * @since 3.0.26
		 * @param {Object} output - The output (js_vars) argument.
		 * @param {mixed}  value - The value.
		 * @param {string} controlType - The control-type.
		 * @returns {string}
		 */
		fromOutput: function( output, value, controlType ) {
			var styles      = '',
				kirkiParent = window.parent.kirki,
				googleFont  = '';

			if ( output.js_callback && 'function' === typeof window[ output.js_callback ] ) {
				value = window[ output.js_callback[0] ]( value, output.js_callback[1] );
			}
			switch ( controlType ) {
				case 'kirki-typography':
					styles += output.element + '{';
					_.each( value, function( val, key ) {
						if ( output.choice && key !== output.choice ) {
							return;
						}
						styles += key + ':' + kirkiPostMessage.util.processValue( output, val ) + ';';
					} );
					styles += '}';

					// Check if this is a googlefont so that we may load it.
					if ( ! _.isUndefined( WebFont ) && value['font-family'] && 'google' === kirkiParent.util.webfonts.getFontType( value['font-family'] ) ) {

						// Calculate the googlefont params.
						googleFont = value['font-family'].replace( /\"/g, '"' );
						if ( value.variant ) {
							if ( 'regular' === value.variant ) {
								googleFont += ':400';
							} else if ( 'italic' === value.variant ) {
								googleFont += ':400i';
							} else {
								googleFont += ':' + value.variant;
							}
						}
						googleFont += ':cyrillic,cyrillic-ext,devanagari,greek,greek-ext,khmer,latin,latin-ext,vietnamese,hebrew,arabic,bengali,gujarati,tamil,telugu,thai';
						WebFont.load( {
							google: {
								families: [ googleFont ]
							}
						} );
					}
					break;
				case 'kirki-background':
				case 'kirki-dimensions':
				case 'kirki-multicolor':
				case 'kirki-sortable':
					styles += output.element + '{';
					_.each( value, function( val, key ) {
						if ( output.choice && key !== output.choice ) {
							return;
						}
						if ( 'background-image' === key ) {
							val = kirkiPostMessage.util.backgroundImageValue( val );
						}

						// Mostly used for padding, margin & position properties.
						if ( output.property && '' !== output.property && ( 'top' === key || 'bottom' === key || 'left' === key || 'right' === key ) ) {
							styles += output.element + '{' + output.property + '-' + key + ':' + kirkiPostMessage.util.processValue( output, val ) + ';';
						} else {
							styles += key + ':' + kirkiPostMessage.util.processValue( output, val ) + ';';
						}
					} );
					styles += '}';
					break;
				default:
					if ( 'kirki-image' === controlType ) {
						value = ( ! _.isUndefined( value.url ) ) ? kirkiPostMessage.util.backgroundImageValue( value.url ) : kirkiPostMessage.util.backgroundImageValue( value );
					}
					styles += output.element + '{' + output.property + ':' + kirkiPostMessage.util.processValue( output, value ) + ';}';
					break;
			}
			return styles;
		}
	},

	/**
	 * A collection of utilities to change the HTML in the document.
	 *
	 * @since 3.0.26
	 */
	html: {

		/**
		 * Modifies the HTML from the output (js_vars) parameter.
		 *
		 * @since 3.0.26
		 * @param {Object} output - The output (js_vars) argument.
		 * @param {mixed}  value - The value.
		 * @returns {string}
		 */
		fromOutput: function( output, value ) {

			if ( output.js_callback && 'function' === typeof window[ output.js_callback ] ) {
				value = window[ output.js_callback[0] ]( value, output.js_callback[1] );
			}

			if ( _.isObject( value ) || _.isArray( value ) ) {
				if ( ! output.choice ) {
					return;
				}
				_.each( value, function( val, key ) {
					if ( output.choice && key !== output.choice ) {
						return;
					}
					value = val;
				} );
			}
			value = kirkiPostMessage.util.processValue( output, value );

			if ( output.attr ) {
				jQuery( output.element ).attr( output.attr, value );
			} else {
				jQuery( output.element ).html( value );
			}
		}
	},

	/**
	 * A collection of utilities to allow toggling a CSS class.
	 *
	 * @since 3.0.26
	 */
	toggleClass: {

		/**
		 * Toggles a CSS class from the output (js_vars) parameter.
		 *
		 * @since 3.0.21
		 * @param {Object} output - The output (js_vars) argument.
		 * @param {mixed}  value - The value.
		 * @returns {string}
		 */
		fromOutput: function( output, value ) {
			if ( 'undefined' === typeof output.class || 'undefined' === typeof output.value ) {
				return;
			}
			if ( value === output.value && ! jQuery( output.element ).hasClass( output.class ) ) {
				jQuery( output.element ).addClass( output.class );
			} else {
				jQuery( output.element ).removeClass( output.class );
			}
		}
	}
};

jQuery( document ).ready( function() {

	_.each( kirkiPostMessageFields, function( field ) {
		wp.customize( field.settings, function( value ) {
			value.bind( function( newVal ) {
				var styles = '';
				_.each( field.js_vars, function( output ) {
					if ( ! output.function || 'undefined' === typeof kirkiPostMessage[ output.function ] ) {
						output.function = 'css';
					}
					if ( 'css' === output.function ) {
						styles += kirkiPostMessage.css.fromOutput( output, newVal, field.type );
					} else {
						kirkiPostMessage[ output.function ].fromOutput( output, newVal, field.type );
					}
				} );
				kirkiPostMessage.styleTag.addData( field.settings, styles );
			} );
		} );
	} );
} );
<commit_msg>possible fix for https://github.com/aristath/kirki/issues/1709#issuecomment-400631510<commit_after>/* global kirkiPostMessageFields, WebFont */
var kirkiPostMessage = {

	/**
	 * The fields.
	 *
	 * @since 3.0.26
	 */
	fields: {},

	/**
	 * A collection of methods for the <style> tags.
	 *
	 * @since 3.0.26
	 */
	styleTag: {

		/**
		 * Add a <style> tag in <head> if it doesn't already exist.
		 *
		 * @since 3.0.26
		 * @param {string} id - The field-ID.
		 * @returns {void}
		 */
		add: function( id ) {
			if ( null === document.getElementById( 'kirki-postmessage-' + id ) || 'undefined' === typeof document.getElementById( 'kirki-postmessage-' + id ) ) {
				jQuery( 'head' ).append( '<style id="kirki-postmessage-' + id + '"></style>' );
			}
		},

		/**
		 * Add a <style> tag in <head> if it doesn't already exist,
		 * by calling the this.add method, and then add styles inside it.
		 *
		 * @since 3.0.26
		 * @param {string} id - The field-ID.
		 * @param {string} styles - The styles to add.
		 * @returns {void}
		 */
		addData: function( id, styles ) {
			kirkiPostMessage.styleTag.add( id );
			jQuery( '#kirki-postmessage-' + id ).text( styles );
		}
	},

	/**
	 * Common utilities.
	 *
	 * @since 3.0.26
	 */
	util: {

		/**
		 * Processes the value and applies any replacements and/or additions.
		 *
		 * @since 3.0.26
		 * @param {Object} output - The output (js_vars) argument.
		 * @param {mixed}  value - The value.
		 * @param {string} controlType - The control-type.
		 * @returns {string}
		 */
		processValue: function( output, value ) {
			var self     = this,
				settings = window.parent.wp.customize.get();

			if ( 'string' !== typeof value ) {
				_.each( value, function( subValue, key ) {
					value[ key ] = self.processValue( output, subValue );
				} );
				return value;
			}
			output = _.defaults( output, {
				prefix: '',
				units: '',
				suffix: '',
				value_pattern: '$',
				pattern_replace: {}
			} );

			value = output.value_pattern.replace( new RegExp( '\\$', 'g' ), value );
			_.each( output.pattern_replace, function( id, placeholder ) {
				if ( ! _.isUndefined( settings[ id ] ) ) {
					value = value.replace( placeholder, settings[ id ] );
				}
			} );
			return output.prefix + value + output.units + output.suffix;
		},

		/**
		 * Make sure urls are properly formatted for background-image properties.
		 *
		 * @since 3.0.26
		 * @param {string} url - The URL.
		 * @returns {string}
		 */
		backgroundImageValue: function( url ) {
			return ( -1 === url.indexOf( 'url(' ) ) ? 'url(' + url + ')' : url;
		}
	},

	/**
	 * A collection of utilities for CSS generation.
	 *
	 * @since 3.0.26
	 */
	css: {

		/**
		 * Generates the CSS from the output (js_vars) parameter.
		 *
		 * @since 3.0.26
		 * @param {Object} output - The output (js_vars) argument.
		 * @param {mixed}  value - The value.
		 * @param {string} controlType - The control-type.
		 * @returns {string}
		 */
		fromOutput: function( output, value, controlType ) {
			var styles      = '',
				kirkiParent = window.parent.kirki,
				googleFont  = '',
				mediaQuery  = false;

			if ( output.js_callback && 'function' === typeof window[ output.js_callback ] ) {
				value = window[ output.js_callback[0] ]( value, output.js_callback[1] );
			}
			switch ( controlType ) {
				case 'kirki-typography':
					styles += output.element + '{';
					_.each( value, function( val, key ) {
						if ( output.choice && key !== output.choice ) {
							return;
						}
						styles += key + ':' + kirkiPostMessage.util.processValue( output, val ) + ';';
					} );
					styles += '}';

					// Check if this is a googlefont so that we may load it.
					if ( ! _.isUndefined( WebFont ) && value['font-family'] && 'google' === kirkiParent.util.webfonts.getFontType( value['font-family'] ) ) {

						// Calculate the googlefont params.
						googleFont = value['font-family'].replace( /\"/g, '"' );
						if ( value.variant ) {
							if ( 'regular' === value.variant ) {
								googleFont += ':400';
							} else if ( 'italic' === value.variant ) {
								googleFont += ':400i';
							} else {
								googleFont += ':' + value.variant;
							}
						}
						googleFont += ':cyrillic,cyrillic-ext,devanagari,greek,greek-ext,khmer,latin,latin-ext,vietnamese,hebrew,arabic,bengali,gujarati,tamil,telugu,thai';
						WebFont.load( {
							google: {
								families: [ googleFont ]
							}
						} );
					}
					break;
				case 'kirki-background':
				case 'kirki-dimensions':
				case 'kirki-multicolor':
				case 'kirki-sortable':
					styles += output.element + '{';
					_.each( value, function( val, key ) {
						if ( output.choice && key !== output.choice ) {
							return;
						}
						if ( 'background-image' === key ) {
							val = kirkiPostMessage.util.backgroundImageValue( val );
						}

						// Mostly used for padding, margin & position properties.
						if ( output.property && '' !== output.property && ( 'top' === key || 'bottom' === key || 'left' === key || 'right' === key ) ) {
							styles += output.element + '{' + output.property + '-' + key + ':' + kirkiPostMessage.util.processValue( output, val ) + ';';
						} else {
							styles += key + ':' + kirkiPostMessage.util.processValue( output, val ) + ';';
						}
					} );
					styles += '}';
					break;
				default:
					if ( 'kirki-image' === controlType ) {
						value = ( ! _.isUndefined( value.url ) ) ? kirkiPostMessage.util.backgroundImageValue( value.url ) : kirkiPostMessage.util.backgroundImageValue( value );
					}
					styles += output.element + '{' + output.property + ':' + kirkiPostMessage.util.processValue( output, value ) + ';}';
					break;
			}

			// Get the media-query.
			if ( output.media_query && 'string' === typeof output.media_query && ! _.isEmpty( output.media_query ) ) {
				mediaQuery = output.media_query;
				if ( -1 === mediaQuery.indexOf( '@media' ) ) {
					mediaQuery = '@media ' + mediaQuery;
				}
			}

			// If we have a media-query, add it and return.
			if ( mediaQuery ) {
				return mediaQuery + '{' + styles + '}';
			}

			// Return the styles.
			return styles;
		}
	},

	/**
	 * A collection of utilities to change the HTML in the document.
	 *
	 * @since 3.0.26
	 */
	html: {

		/**
		 * Modifies the HTML from the output (js_vars) parameter.
		 *
		 * @since 3.0.26
		 * @param {Object} output - The output (js_vars) argument.
		 * @param {mixed}  value - The value.
		 * @returns {string}
		 */
		fromOutput: function( output, value ) {

			if ( output.js_callback && 'function' === typeof window[ output.js_callback ] ) {
				value = window[ output.js_callback[0] ]( value, output.js_callback[1] );
			}

			if ( _.isObject( value ) || _.isArray( value ) ) {
				if ( ! output.choice ) {
					return;
				}
				_.each( value, function( val, key ) {
					if ( output.choice && key !== output.choice ) {
						return;
					}
					value = val;
				} );
			}
			value = kirkiPostMessage.util.processValue( output, value );

			if ( output.attr ) {
				jQuery( output.element ).attr( output.attr, value );
			} else {
				jQuery( output.element ).html( value );
			}
		}
	},

	/**
	 * A collection of utilities to allow toggling a CSS class.
	 *
	 * @since 3.0.26
	 */
	toggleClass: {

		/**
		 * Toggles a CSS class from the output (js_vars) parameter.
		 *
		 * @since 3.0.21
		 * @param {Object} output - The output (js_vars) argument.
		 * @param {mixed}  value - The value.
		 * @returns {string}
		 */
		fromOutput: function( output, value ) {
			if ( 'undefined' === typeof output.class || 'undefined' === typeof output.value ) {
				return;
			}
			if ( value === output.value && ! jQuery( output.element ).hasClass( output.class ) ) {
				jQuery( output.element ).addClass( output.class );
			} else {
				jQuery( output.element ).removeClass( output.class );
			}
		}
	}
};

jQuery( document ).ready( function() {

	_.each( kirkiPostMessageFields, function( field ) {
		wp.customize( field.settings, function( value ) {
			value.bind( function( newVal ) {
				var styles = '';
				_.each( field.js_vars, function( output ) {
					if ( ! output.function || 'undefined' === typeof kirkiPostMessage[ output.function ] ) {
						output.function = 'css';
					}
					if ( 'css' === output.function ) {
						styles += kirkiPostMessage.css.fromOutput( output, newVal, field.type );
					} else {
						kirkiPostMessage[ output.function ].fromOutput( output, newVal, field.type );
					}
				} );
				kirkiPostMessage.styleTag.addData( field.settings, styles );
			} );
		} );
	} );
} );
<|endoftext|>


<commit_before>import { createSelector } from 'reselect';
import { getColorByIndex, createLegendBuckets } from 'utils/map';
import { deburrUpper } from 'app/utils';
import uniqBy from 'lodash/uniqBy';
import sortBy from 'lodash/sortBy';
import { generateLinkToDataExplorer } from 'utils/data-explorer';
import worldPaths from 'app/data/world-50m-paths';
import { europeSlug, europeanCountries } from 'app/data/european-countries';
import { PATH_LAYERS } from 'app/data/constants';

const getSearch = state => state.search || null;
const getCountries = state => state.countries || null;
const getCategories = state => state.categories || null;
const getIndicatorsData = state => state.indicators || null;

export const getISOCountries = createSelector([getCountries], countries =>
  countries.map(country => country.iso_code3)
);

export const getIndicatorsParsed = createSelector(
  [getCategories, getIndicatorsData, getISOCountries],
  (categories, indicators, isos) => {
    if (!categories || !indicators || !indicators.length) return null;
    const categoryId = Object.keys(categories).find(
      id => categories[id].slug === 'ndc_enhancement'
    );
    return sortBy(
      uniqBy(
        indicators.map(i => {
          const legendBuckets = createLegendBuckets(
            i.locations,
            i.labels,
            isos
          );
          return {
            label: i.name,
            value: i.slug,
            categoryIds: i.category_ids,
            locations: i.locations,
            legendBuckets
          };
        }),
        'value'
      ),
      'label'
    ).filter(ind => ind.categoryIds.indexOf(parseInt(categoryId, 10)) > -1);
  }
);

export const getMapIndicator = createSelector(
  [getIndicatorsParsed, getISOCountries],
  (indicators, isos) => {
    if (!indicators || !indicators.length) return null;
    const mapIndicator = indicators.find(
      ind => ind.value === 'ndce_status_2020'
    );

    if (mapIndicator) {
      const noInfoId = Object.keys(mapIndicator.legendBuckets).find(
        id => mapIndicator.legendBuckets[id].slug === 'no_info_2020'
      );
      // Set all countries without values to "No Information" by default
      if (noInfoId) {
        isos.forEach(iso => {
          if (!mapIndicator.locations[iso]) {
            mapIndicator.locations[iso] = {
              value: mapIndicator.legendBuckets[noInfoId].name,
              label_id: noInfoId,
              label_slug: mapIndicator.legendBuckets[noInfoId].slug
            };
          }
        });
      }
    }
    return mapIndicator;
  }
);

const countryStyles = {
  default: {
    fill: '#e9e9e9',
    fillOpacity: 1,
    stroke: '#f5f6f7',
    strokeWidth: 1,
    outline: 'none'
  },
  hover: {
    fill: '#e9e9e9',
    fillOpacity: 1,
    stroke: '#f5f6f7',
    strokeWidth: 1,
    outline: 'none'
  },
  pressed: {
    fill: '#e9e9e9',
    fillOpacity: 1,
    stroke: '#f5f6f7',
    strokeWidth: 1,
    outline: 'none'
  }
};

export const MAP_COLORS = [
  [
    'rgb(254, 224, 141)',
    'rgb(30, 79, 116)',
    'rgb(132, 181, 218)',
    'rgb(0, 0, 0)',
    'rgb(204, 204, 204)'
  ],
  [
    'rgb(254, 224, 141)',
    'rgb(30, 79, 116)',
    'rgb(132, 181, 218)',
    'rgb(0, 0, 0)',
    'rgb(204, 204, 204)'
  ],
  [
    'rgb(254, 224, 141)',
    'rgb(30, 79, 116)',
    'rgb(132, 181, 218)',
    'rgb(0, 0, 0)',
    'rgb(204, 204, 204)'
  ],
  [
    'rgb(254, 224, 141)',
    'rgb(30, 79, 116)',
    'rgb(132, 181, 218)',
    'rgb(0, 0, 0)',
    'rgb(204, 204, 204)'
  ],
  [
    'rgb(254, 224, 141)',
    'rgb(30, 79, 116)',
    'rgb(132, 181, 218)',
    'rgb(0, 0, 0)',
    'rgb(204, 204, 204)'
  ]
];

export const getPathsWithStyles = createSelector(
  [getMapIndicator, getISOCountries],
  indicator => {
    if (!indicator) return [];
    const paths = [];
    worldPaths.forEach(path => {
      if (path.properties.layer !== PATH_LAYERS.ISLANDS) {
        const { locations, legendBuckets } = indicator;

        if (!locations) {
          paths.push({
            ...path,
            countryStyles
          });
          return null;
        }

        const iso = path.properties && path.properties.id;
        const isEuropeanCountry = europeanCountries.includes(iso);
        const countryData = isEuropeanCountry
          ? locations[europeSlug]
          : locations[iso];

        let style = countryStyles;
        if (countryData && countryData.label_id) {
          const legendIndex = legendBuckets[countryData.label_id].index;
          const color = countryData.label_slug == 'ambg_2020' ? 'url(#pattern_gJ2H6)' : getColorByIndex(legendBuckets, legendIndex, MAP_COLORS);
          style = {
            ...countryStyles,
            default: {
              ...countryStyles.default,
              fill: color ,
              fillOpacity: 1
            },
            hover: {
              ...countryStyles.hover,
              fill: color,
              fillOpacity: 1
            }
          };
        }

        paths.push({
          ...path,
          style
        });
      }
      return null;
    });
    return paths;
  }
);

export const getLinkToDataExplorer = createSelector([getSearch], search => {
  const section = 'ndc-content';
  return generateLinkToDataExplorer(search, section);
});

// Chart data methods

export const summarizeIndicators = createSelector(
  [getIndicatorsParsed, getMapIndicator],
  (indicators, indicator) => {
    if (!indicator || !indicators) return null;
    const summaryData = {};
    const labels = Object.keys(indicator.legendBuckets).map(id => ({
      ...indicator.legendBuckets[id],
      id
    }));

    // Retain functionality for showing submitted 2020 NDCs in case this becomes useful to display later
    // Retain functionality for showing emissions percentage in case this becomes useful to display later
    // ONLY "intent to submit" and "intent to enhance" 2020 NDCs currently displayed in component
    // ONLY country totals currently displayed in component
    labels.forEach(label => {
      summaryData[label.slug] = {
        countries: {
          value: 0,
          max: Object.keys(indicator.locations).length,
          opts: {
            color: getColorByIndex(
              indicator.legendBuckets,
              label.index,
              MAP_COLORS
            ),
            label: (() => {
              switch (label.slug) {
                case 'enhance_2020':
                  return 'countries have stated their intention to enhance ambition or action in their NDCs by 2020';
                  break;
                case 'intend_2020':
                  return 'countries have stated their intention to update their NDCs by 2020';
                  break;
                default:
                  return 'countries';
                  break;
              }
            })()
          }
        },
        emissions: {
          value: 0,
          max: 100,
          opts: {
            color: getColorByIndex(
              indicator.legendBuckets,
              label.index,
              MAP_COLORS
            ),
            suffix: '%',
            label:
              'of global emissions are represented by these countries (2014 emissions data)'
          }
        }
      };
    });
    const emissionsIndicator = indicators.find(ind => ind.value === 'ndce_ghg');
    Object.keys(indicator.locations).forEach(l => {
      const location = indicator.locations[l];
      const type = location.label_slug;
      if (type) {
        summaryData[type].countries.value++;
        if (emissionsIndicator.locations[l]) {
          summaryData[type].emissions.value += parseFloat(
            emissionsIndicator.locations[l].value
          );
        }
      }
    });
    Object.keys(summaryData).forEach(type => {
      summaryData[type].emissions.value = parseFloat(
        summaryData[type].emissions.value.toFixed(1)
      );
      summaryData[type].countries.opts.label += `, representing ${summaryData[
        type
      ].emissions.value}% of global emissions`;
    });
    return summaryData;
  }
);

export default {
  getMapIndicator,
  getIndicatorsParsed,
  summarizeIndicators,
  getPathsWithStyles
};
<commit_msg>Changing map color for 'Submitted' countries<commit_after>import { createSelector } from 'reselect';
import { getColorByIndex, createLegendBuckets } from 'utils/map';
import { deburrUpper } from 'app/utils';
import uniqBy from 'lodash/uniqBy';
import sortBy from 'lodash/sortBy';
import { generateLinkToDataExplorer } from 'utils/data-explorer';
import worldPaths from 'app/data/world-50m-paths';
import { europeSlug, europeanCountries } from 'app/data/european-countries';
import { PATH_LAYERS } from 'app/data/constants';

const getSearch = state => state.search || null;
const getCountries = state => state.countries || null;
const getCategories = state => state.categories || null;
const getIndicatorsData = state => state.indicators || null;

export const getISOCountries = createSelector([getCountries], countries =>
  countries.map(country => country.iso_code3)
);

export const getIndicatorsParsed = createSelector(
  [getCategories, getIndicatorsData, getISOCountries],
  (categories, indicators, isos) => {
    if (!categories || !indicators || !indicators.length) return null;
    const categoryId = Object.keys(categories).find(
      id => categories[id].slug === 'ndc_enhancement'
    );
    return sortBy(
      uniqBy(
        indicators.map(i => {
          const legendBuckets = createLegendBuckets(
            i.locations,
            i.labels,
            isos
          );
          return {
            label: i.name,
            value: i.slug,
            categoryIds: i.category_ids,
            locations: i.locations,
            legendBuckets
          };
        }),
        'value'
      ),
      'label'
    ).filter(ind => ind.categoryIds.indexOf(parseInt(categoryId, 10)) > -1);
  }
);

export const getMapIndicator = createSelector(
  [getIndicatorsParsed, getISOCountries],
  (indicators, isos) => {
    if (!indicators || !indicators.length) return null;
    const mapIndicator = indicators.find(
      ind => ind.value === 'ndce_status_2020'
    );

    if (mapIndicator) {
      const noInfoId = Object.keys(mapIndicator.legendBuckets).find(
        id => mapIndicator.legendBuckets[id].slug === 'no_info_2020'
      );
      // Set all countries without values to "No Information" by default
      if (noInfoId) {
        isos.forEach(iso => {
          if (!mapIndicator.locations[iso]) {
            mapIndicator.locations[iso] = {
              value: mapIndicator.legendBuckets[noInfoId].name,
              label_id: noInfoId,
              label_slug: mapIndicator.legendBuckets[noInfoId].slug
            };
          }
        });
      }
    }
    return mapIndicator;
  }
);

const countryStyles = {
  default: {
    fill: '#e9e9e9',
    fillOpacity: 1,
    stroke: '#f5f6f7',
    strokeWidth: 1,
    outline: 'none'
  },
  hover: {
    fill: '#e9e9e9',
    fillOpacity: 1,
    stroke: '#f5f6f7',
    strokeWidth: 1,
    outline: 'none'
  },
  pressed: {
    fill: '#e9e9e9',
    fillOpacity: 1,
    stroke: '#f5f6f7',
    strokeWidth: 1,
    outline: 'none'
  }
};

export const MAP_COLORS = [
  [
    'rgb(255, 108, 47)',
    'rgb(30, 79, 116)',
    'rgb(132, 181, 218)',
    'rgb(0, 0, 0)',
    'rgb(204, 204, 204)'
  ],
  [
    'rgb(255, 108, 47)',
    'rgb(30, 79, 116)',
    'rgb(132, 181, 218)',
    'rgb(0, 0, 0)',
    'rgb(204, 204, 204)'
  ],
  [
    'rgb(255, 108, 47)',
    'rgb(30, 79, 116)',
    'rgb(132, 181, 218)',
    'rgb(0, 0, 0)',
    'rgb(204, 204, 204)'
  ],
  [
    'rgb(255, 108, 47)',
    'rgb(30, 79, 116)',
    'rgb(132, 181, 218)',
    'rgb(0, 0, 0)',
    'rgb(204, 204, 204)'
  ],
  [
    'rgb(255, 108, 47)',
    'rgb(30, 79, 116)',
    'rgb(132, 181, 218)',
    'rgb(0, 0, 0)',
    'rgb(204, 204, 204)'
  ]
];

export const getPathsWithStyles = createSelector(
  [getMapIndicator, getISOCountries],
  indicator => {
    if (!indicator) return [];
    const paths = [];
    worldPaths.forEach(path => {
      if (path.properties.layer !== PATH_LAYERS.ISLANDS) {
        const { locations, legendBuckets } = indicator;

        if (!locations) {
          paths.push({
            ...path,
            countryStyles
          });
          return null;
        }

        const iso = path.properties && path.properties.id;
        const isEuropeanCountry = europeanCountries.includes(iso);
        const countryData = isEuropeanCountry
          ? locations[europeSlug]
          : locations[iso];

        let style = countryStyles;
        if (countryData && countryData.label_id) {
          const legendIndex = legendBuckets[countryData.label_id].index;
          const color = countryData.label_slug == 'ambg_2020' ? 'url(#pattern_gJ2H6)' : getColorByIndex(legendBuckets, legendIndex, MAP_COLORS);
          style = {
            ...countryStyles,
            default: {
              ...countryStyles.default,
              fill: color ,
              fillOpacity: 1
            },
            hover: {
              ...countryStyles.hover,
              fill: color,
              fillOpacity: 1
            }
          };
        }

        paths.push({
          ...path,
          style
        });
      }
      return null;
    });
    return paths;
  }
);

export const getLinkToDataExplorer = createSelector([getSearch], search => {
  const section = 'ndc-content';
  return generateLinkToDataExplorer(search, section);
});

// Chart data methods

export const summarizeIndicators = createSelector(
  [getIndicatorsParsed, getMapIndicator],
  (indicators, indicator) => {
    if (!indicator || !indicators) return null;
    const summaryData = {};
    const labels = Object.keys(indicator.legendBuckets).map(id => ({
      ...indicator.legendBuckets[id],
      id
    }));

    // Retain functionality for showing submitted 2020 NDCs in case this becomes useful to display later
    // Retain functionality for showing emissions percentage in case this becomes useful to display later
    // ONLY "intent to submit" and "intent to enhance" 2020 NDCs currently displayed in component
    // ONLY country totals currently displayed in component
    labels.forEach(label => {
      summaryData[label.slug] = {
        countries: {
          value: 0,
          max: Object.keys(indicator.locations).length,
          opts: {
            color: getColorByIndex(
              indicator.legendBuckets,
              label.index,
              MAP_COLORS
            ),
            label: (() => {
              switch (label.slug) {
                case 'enhance_2020':
                  return 'countries have stated their intention to enhance ambition or action in their NDCs by 2020';
                  break;
                case 'intend_2020':
                  return 'countries have stated their intention to update their NDCs by 2020';
                  break;
                default:
                  return 'countries';
                  break;
              }
            })()
          }
        },
        emissions: {
          value: 0,
          max: 100,
          opts: {
            color: getColorByIndex(
              indicator.legendBuckets,
              label.index,
              MAP_COLORS
            ),
            suffix: '%',
            label:
              'of global emissions are represented by these countries (2014 emissions data)'
          }
        }
      };
    });
    const emissionsIndicator = indicators.find(ind => ind.value === 'ndce_ghg');
    Object.keys(indicator.locations).forEach(l => {
      const location = indicator.locations[l];
      const type = location.label_slug;
      if (type) {
        summaryData[type].countries.value++;
        if (emissionsIndicator.locations[l]) {
          summaryData[type].emissions.value += parseFloat(
            emissionsIndicator.locations[l].value
          );
        }
      }
    });
    Object.keys(summaryData).forEach(type => {
      summaryData[type].emissions.value = parseFloat(
        summaryData[type].emissions.value.toFixed(1)
      );
      summaryData[type].countries.opts.label += `, representing ${summaryData[
        type
      ].emissions.value}% of global emissions`;
    });
    return summaryData;
  }
);

export default {
  getMapIndicator,
  getIndicatorsParsed,
  summarizeIndicators,
  getPathsWithStyles
};
<|endoftext|>


<commit_before>'use strict';

// @ngInject
var MessageController = function ($scope, $route, $http, $location, $mdToast, npolarApiConfig, NpolarApiUser, NpolarApiSecurity, NpolarApiMessage) {

  var flashError = function(message) {

    let explanation = "";

    if (message.body && message.body.error && message.body.error.explanation ) {
      explanation = message.body.error.explanation;
    } else if (message.body && message.body.reason) {
      explanation = message.body.reason;
    } else {
      explanation = message;
    }

    $mdToast.show({
      controller: 'ToastCtrl',
      templateUrl: 'angular-npolar/src/ui/message/_message_toast.html',
      hideDelay: 30000,
      action: "OK",
      locals: { message: message, explanation: explanation },
      position: "top left"
    }).then(function() {
      //$route.reload();
    });
  };

  var flashInfo = function(message) {

    let explanation = "";

    explanation = message;

    $mdToast.show({
      controller: 'ToastCtrl',
      templateUrl: 'angular-npolar/src/ui/message/_message_toast.html',
      hideDelay: 5000,
      action: "OK",
      locals: { message: message, explanation: explanation },
      position: "bottom right"
    }).then(function() {
      // noop
    });
  };

  NpolarApiMessage.on("npolar-api-info", function(response) {
    console.log("<- npolar-api-info", response);
    if ("POST" === response.method || "PUT" === response.method) {
      let time = new Date(response.time);
      flashInfo(`Saved at ${ time.toISOString() }`);
    } else if ("DELETE" === response.method) {
      flashInfo(`Deleted document ${ response.uri } at ${ response.time }`);
    }
  });

  NpolarApiMessage.on("npolar-login", function(user) {
    flashInfo(`${user.name} logged in`);
  });

  NpolarApiMessage.on("npolar-logout", function(user) {
    flashInfo(`${user.name} logged out`);
  });

  NpolarApiMessage.on("npolar-api-error", function(message) {
    console.log("<- npolar-api-error", message);
    if (404 === message.status) {
      flashError("Document does not exist: "+$location.absUrl());
    } else {
      flashError(message, message.body);
    }
  });

};

module.exports = MessageController;
<commit_msg>Good messages to the left<commit_after>'use strict';

// @ngInject
var MessageController = function ($scope, $route, $http, $location, $mdToast, npolarApiConfig, NpolarApiUser, NpolarApiSecurity, NpolarApiMessage) {

  var flashError = function(message) {

    let explanation = "";

    if (message.body && message.body.error && message.body.error.explanation ) {
      explanation = message.body.error.explanation;
    } else if (message.body && message.body.reason) {
      explanation = message.body.reason;
    } else {
      explanation = message;
    }

    $mdToast.show({
      controller: 'ToastCtrl',
      templateUrl: 'angular-npolar/src/ui/message/_message_toast.html',
      hideDelay: 30000,
      action: "OK",
      locals: { message: message, explanation: explanation },
      position: "top left"
    }).then(function() {
      //$route.reload();
    });
  };

  var flashInfo = function(message) {

    let explanation = "";

    explanation = message;

    $mdToast.show({
      controller: 'ToastCtrl',
      templateUrl: 'angular-npolar/src/ui/message/_message_toast.html',
      hideDelay: 5000,
      action: "OK",
      locals: { message: message, explanation: explanation },
      position: "bottom left"
    }).then(function() {
      // noop
    });
  };

  NpolarApiMessage.on("npolar-api-info", function(response) {
    console.log("<- npolar-api-info", response);
    if ("POST" === response.method || "PUT" === response.method) {
      let time = new Date(response.time);
      flashInfo(`Saved at ${ time.toISOString() }`);
    } else if ("DELETE" === response.method) {
      flashInfo(`Deleted document ${ response.uri } at ${ response.time }`);
    }
  });

  NpolarApiMessage.on("npolar-login", function(user) {
    flashInfo(`${user.name} logged in`);
  });

  NpolarApiMessage.on("npolar-logout", function(user) {
    flashInfo(`${user.name} logged out`);
  });

  NpolarApiMessage.on("npolar-api-error", function(message) {
    console.log("<- npolar-api-error", message);
    if (404 === message.status) {
      flashError("Document does not exist: "+$location.absUrl());
    } else {
      flashError(message, message.body);
    }
  });

};

module.exports = MessageController;
<|endoftext|>


<commit_before>'use strict';

const { assignArray } = require('./reduce');

// Similar to Lodash get(), but do not mutate, and faster
const get = function (obj, keys) {
  if (keys.length === 0) { return obj; }

  const [childKey, ...keysA] = keys;
  const child = obj[childKey];
  return get(child, keysA);
};

// Similar to Lodash set(), but do not mutate, and faster
const set = function (obj = {}, keys, val) {
  if (keys.length === 0) {
    return typeof val === 'function' ? val(obj, keys) : val;
  }

  const [childKey, ...keysA] = keys;
  const child = obj[childKey];
  const childA = set(child, keysA, val);

  if (Array.isArray(obj)) {
    return [
      ...obj.slice(0, childKey),
      childA,
      ...obj.slice(childKey + 1),
    ];
  }

  return { ...obj, [childKey]: childA };
};

// Apply several set() at once, using an array of `paths`
const setAll = function (obj, paths, val) {
  return paths.reduce(
    (objA, inlineFuncPath) => set(objA, inlineFuncPath, val),
    obj,
  );
};

// Retrieves all recursive values (i.e. leaves) of an object,
// as a single array of [value, key] elements
const getAll = function (value, key = []) {
  if (value && value.constructor === Object) {
    return Object.entries(value)
      .map(([childKey, child]) => getAll(child, [...key, childKey]))
      .reduce(assignArray, []);
  }

  if (Array.isArray(value)) {
    return value
      .map((child, childKey) => getAll(child, [...key, childKey]))
      .reduce(assignArray, []);
  }

  return [[value, key]];
};

module.exports = {
  get,
  set,
  getAll,
  setAll,
};
<commit_msg>Improve set() utility<commit_after>'use strict';

const { assignArray } = require('./reduce');

// Similar to Lodash get(), but do not mutate, and faster
const get = function (obj, keys) {
  if (keys.length === 0) { return obj; }

  const [childKey, ...keysA] = keys;
  const child = obj[childKey];
  return get(child, keysA);
};

// Similar to Lodash set(), but do not mutate, and faster
const set = function (objArr, keys, val) {
  if (keys.length === 0) {
    return typeof val === 'function' ? val(objArr, keys) : val;
  }

  if (typeof keys[0] === 'number') {
    return setArray(objArr, keys, val);
  }

  return setObject(objArr, keys, val);
};

const setObject = function (obj = {}, keys, val) {
  const { child, childKey } = setVal({ objArr: obj, keys, val });

  return { ...obj, [childKey]: child };
};

const setArray = function (arr = [], keys, val) {
  const { child, childKey } = setVal({ objArr: arr, keys, val });

  return [
    ...arr.slice(0, childKey),
    child,
    ...arr.slice(childKey + 1),
  ];
};

const setVal = function ({ objArr, keys, val }) {
  const [childKey, ...keysA] = keys;
  const child = objArr[childKey];
  const childA = set(child, keysA, val);

  return { child: childA, childKey };
};

// Apply several set() at once, using an array of `paths`
const setAll = function (obj, paths, val) {
  return paths.reduce(
    (objA, inlineFuncPath) => set(objA, inlineFuncPath, val),
    obj,
  );
};

// Retrieves all recursive values (i.e. leaves) of an object,
// as a single array of [value, key] elements
const getAll = function (value, key = []) {
  if (value && value.constructor === Object) {
    return Object.entries(value)
      .map(([childKey, child]) => getAll(child, [...key, childKey]))
      .reduce(assignArray, []);
  }

  if (Array.isArray(value)) {
    return value
      .map((child, childKey) => getAll(child, [...key, childKey]))
      .reduce(assignArray, []);
  }

  return [[value, key]];
};

module.exports = {
  get,
  set,
  getAll,
  setAll,
};
<|endoftext|>


<commit_before>/* @flow */

import React from 'react';
import { connect } from 'react-redux';
import { actions } from 'react-redux-form';
import _get from 'lodash.get';
import {
  TextInputComponent,
  CheckboxComponent,
  RadioGroupComponent,
} from '.';

const fieldComponents = {
  textinput: TextInputComponent,
  check: CheckboxComponent,
  radio: RadioGroupComponent,
};

function checkVisibility(state: Object, showProp: ?string|boolean) {
  if (showProp === false) {
    return false;
  }

  if (typeof showProp === 'string') {
    const [referPath, containedInArray] = showProp.split(':');
    const referent = _get(state, referPath, false);

    if (!referent) { return false; }

    if (containedInArray) {
      return referent.indexOf(containedInArray) > -1;
    }
  }

  return true;
}

type FormFieldDefinition = {
  field: string,
  class: string,
  label?: string,
  primary?: boolean,
  show?: boolean | string,
}

const RowComponent = ({
  value,
  fields,
  onChange,
  onRemoveItemRequested,
}: {
  value: Object | string,
  fields: Array<FormFieldDefinition>,
  onChange: (newValue: Object) => void,
  onRemoveItemRequested?: () => void,
}) => {
  let _value = {};
  if (typeof value === 'string') {
    // MultiInputからの移行．primary=trueのfieldに当てはめる
    const primaryField = fields.find(f => f.primary);
    if (primaryField) {
      _value[primaryField.field] = value;
    }
  } else {
    _value = value;
  }

  return (

          {fields.map((field, i) => {
            if (!checkVisibility(_value, field.show)) {
              return null;
            }

            const component = fieldComponents[field.class];

            return React.createElement(component, {
              key: i,
              ...field,
              value: _value[field.field],
              onChange: (v => {
                const updated = {};
                updated[field.field] = v;

                onChange({
                  ..._value,
                  ...updated,
                });
              }),
            });
          })}

        {onRemoveItemRequested &&

             {
                e.preventDefault();
                if (onRemoveItemRequested) onRemoveItemRequested();
              }}
            >

        }

  );
};

export const SubformListComponent = ({
  label,
  values,
  fields,
  onChange,
  onAddItemRequested,
  onRemoveItemRequested,
}: {
  label?: ?string,
  values: ?Array<Object | string>,
  fields: Array<FormFieldDefinition>,
  onChange: (index: number, newValue: Object) => void,
  onAddItemRequested: (value: Object) => void,
  onRemoveItemRequested: (index: number) => void,
}) => {
  const _values = values || [];

  return (

      {label && {label}}

      {_values.map((value, i) =>
        <RowComponent
          key={i}
          value={value}
          fields={fields}
          onChange={v => onChange(i, v)}
          onRemoveItemRequested={() => onRemoveItemRequested(i)}
        />
      ).concat(
        <RowComponent
          key={_values.length}
          value={{}}
          fields={fields}
          onChange={v => onAddItemRequested(v)}
        />
      )}

  );
};

// TODO: MultiInput のconnectと共通化
const mapStateToProps = (state, ownProps) => ({
  values: _get(state, ownProps.model),
});
const mapDispatchToProps = (dispatch, ownProps) => ({
  onChange: (index, value) => dispatch(actions.change(`${ownProps.model}[${index}]`, value)),
  onAddItemRequested: (value) => dispatch(actions.push(ownProps.model, value)),
  onRemoveItemRequested: (index) => dispatch(actions.remove(ownProps.model, index)),
});

export const SubformList = connect(
  mapStateToProps, mapDispatchToProps
)(SubformListComponent);
<commit_msg>(refs #258) Set size=small to child components of SubformList<commit_after>/* @flow */

import React from 'react';
import { connect } from 'react-redux';
import { actions } from 'react-redux-form';
import _get from 'lodash.get';
import {
  TextInputComponent,
  CheckboxComponent,
  RadioGroupComponent,
} from '.';

const fieldComponents = {
  textinput: TextInputComponent,
  check: CheckboxComponent,
  radio: RadioGroupComponent,
};

function checkVisibility(state: Object, showProp: ?string|boolean) {
  if (showProp === false) {
    return false;
  }

  if (typeof showProp === 'string') {
    const [referPath, containedInArray] = showProp.split(':');
    const referent = _get(state, referPath, false);

    if (!referent) { return false; }

    if (containedInArray) {
      return referent.indexOf(containedInArray) > -1;
    }
  }

  return true;
}

type FormFieldDefinition = {
  field: string,
  class: string,
  label?: string,
  primary?: boolean,
  show?: boolean | string,
}

const RowComponent = ({
  value,
  fields,
  onChange,
  onRemoveItemRequested,
}: {
  value: Object | string,
  fields: Array<FormFieldDefinition>,
  onChange: (newValue: Object) => void,
  onRemoveItemRequested?: () => void,
}) => {
  let _value = {};
  if (typeof value === 'string') {
    // MultiInputからの移行．primary=trueのfieldに当てはめる
    const primaryField = fields.find(f => f.primary);
    if (primaryField) {
      _value[primaryField.field] = value;
    }
  } else {
    _value = value;
  }

  return (

          {fields.map((field, i) => {
            if (!checkVisibility(_value, field.show)) {
              return null;
            }

            const component = fieldComponents[field.class];

            return React.createElement(component, {
              key: i,
              ...field,
              size: 'small',
              value: _value[field.field],
              onChange: (v => {
                const updated = {};
                updated[field.field] = v;

                onChange({
                  ..._value,
                  ...updated,
                });
              }),
            });
          })}

        {onRemoveItemRequested &&

             {
                e.preventDefault();
                if (onRemoveItemRequested) onRemoveItemRequested();
              }}
            >

        }

  );
};

export const SubformListComponent = ({
  label,
  values,
  fields,
  onChange,
  onAddItemRequested,
  onRemoveItemRequested,
}: {
  label?: ?string,
  values: ?Array<Object | string>,
  fields: Array<FormFieldDefinition>,
  onChange: (index: number, newValue: Object) => void,
  onAddItemRequested: (value: Object) => void,
  onRemoveItemRequested: (index: number) => void,
}) => {
  const _values = values || [];

  return (

      {label && {label}}

      {_values.map((value, i) =>
        <RowComponent
          key={i}
          value={value}
          fields={fields}
          onChange={v => onChange(i, v)}
          onRemoveItemRequested={() => onRemoveItemRequested(i)}
        />
      ).concat(
        <RowComponent
          key={_values.length}
          value={{}}
          fields={fields}
          onChange={v => onAddItemRequested(v)}
        />
      )}

  );
};

// TODO: MultiInput のconnectと共通化
const mapStateToProps = (state, ownProps) => ({
  values: _get(state, ownProps.model),
});
const mapDispatchToProps = (dispatch, ownProps) => ({
  onChange: (index, value) => dispatch(actions.change(`${ownProps.model}[${index}]`, value)),
  onAddItemRequested: (value) => dispatch(actions.push(ownProps.model, value)),
  onRemoveItemRequested: (index) => dispatch(actions.remove(ownProps.model, index)),
});

export const SubformList = connect(
  mapStateToProps, mapDispatchToProps
)(SubformListComponent);
<|endoftext|>