<commit_before>"use strict";

var db = require('../models');
var generate = require('../lib/generate');
var requestHelper = require('../lib/request-helper');
var verify = require('../lib/verify');
var config = require('../config');
var messages = require('../lib/messages');
var authHelper = require('../lib/auth-helper');

var registerPairingCode = function(req, res) {
  var clientId = req.body.client_id;
  var serviceProviderName = req.body.service_provider;

  // TODO: validate clientId
  if (!clientId) {
    res.json(400, { error: 'invalid_request' });
    return;
  }
  else if (!clientId.match(/^\d+$/)) {
    res.json(400, { error: 'invalid_client' });
    return;
  }

  if (!serviceProviderName) {
    res.json(400, { error: 'invalid_request' });
    return;
  }

  db.Client.find({ where: { id: clientId } }).success(function(client) {
    if (!client) {
      res.json(400, { error: 'invalid_client' });
      return;
    }

    db.ServiceProvider.find({ where: { name: serviceProviderName }})
      .success(function(serviceProvider) {
        if (!serviceProvider) {
          res.json(400, { error: 'invalid_request' });
          return;
        }

        var pairingCode = {
          client_id: clientId,
          service_provider_id: serviceProvider.id,
          device_code: generate.deviceCode(),
          user_code: generate.userCode(),
          verification_uri: config.uris.verification_uri
        };

        db.PairingCode.create(pairingCode).then(function() {
          res.json(200, {
            device_code: pairingCode.device_code,
            user_code: pairingCode.user_code,
            verification_uri: pairingCode.verification_uri
          });
        },
        function(error) {
          res.send(500);
        });
      });
  },
  function(error) {
    res.send(500);
  });
};

var requestStandAloneAccessToken = function(res, clientId, clientSecret, serviceProvider, scope) {
  db.ServiceProvider.find({ where: {name: serviceProvider}})
    .then(function(serviceProvider) {
      if (!serviceProvider) {
        // SPEC : define correct error message
        res.json(400, { error: 'invalid_request' });
        return;
      }

      var accessToken = {
        token:               generate.accessToken(),
        user_id:             null,
        client_id:           clientId,
        service_provider_id: serviceProvider.id
      };

      // TODO: Handle duplicated tokens
      db.ServiceAccessToken
        .create(accessToken)
        .success(function(dbAccessToken) {
          res.set('Cache-Control', 'no-store');
          res.set('Pragma', 'no-cache');
          res.json({
            token:      dbAccessToken.token,
            token_type: 'bearer'
          });
        }).error(function(error){
          res.send(500);
        });

    }, function(err){
      res.send(500);
    });
};

var validateDeviceCode = function(res, clientId, deviceCode) {

  db.PairingCode
    .find({ where: { client_id: clientId, device_code: deviceCode } })
    .success(function(pairingCode) {
      if (!pairingCode) {
        res.json(400, { error: 'invalid_client' });
        return;
      }

      if (!pairingCode.verified) {
        res.json(400, { error: 'authorization_pending' });
        return;
      }

      db.sequelize.transaction(function(transaction) {
        var accessToken = {
          token:               generate.accessToken(),
          user_id:             pairingCode.user_id,
          client_id:           pairingCode.client_id,
          service_provider_id: pairingCode.service_provider_id
        };

        db.ServiceAccessToken
          .create(accessToken)
          .then(function() {
            return pairingCode.destroy();
          })
          .then(function() {
            return transaction.commit();
          })
          .then(function() {
            res.set('Cache-Control', 'no-store');
            res.set('Pragma', 'no-cache');
            res.json({
              token:      accessToken.token,
              token_type: 'bearer'
            });
          },
          function(error) {
            transaction.rollback().complete(function(err) {
              res.send(500);
            });
          });
      });
    });
};

var requestAccessToken = function(req, res) {
  var clientId = req.body.client_id;
  var clientSecret = req.body.client_secret;
  var serviceProvider = req.body.service_provider;
  var scope = req.body.scope;

  // TODO: validate clientId
  if (!clientId) {
    res.json(400, { error: 'invalid_request' });
    return;
  }

  //Stand-alone mode
  if(clientSecret) {
    verify.clientSecret(clientId, clientSecret, function(err, client) {
      if(err || !client) {
        res.json(400, {error: 'invalid_client'});
        return;
      }

      if(!serviceProvider) {
        res.json(400, { error: 'invalid_request' });
        return;
      }

      requestStandAloneAccessToken(res, clientId, clientSecret, serviceProvider, scope);
    });
  } else {

    // TODO: RFC6749 section 4.1.3 describes the 'code' parameter as "The
    // authorization code received from the authorization server." Assume this
    // is the device_code from draft-recordon-oauth-v2-device-00 section 1.4

    var deviceCode = req.body.code;

    if (!deviceCode) {
      res.json(400, { error: 'invalid_request' });
      return;
    }

    validateDeviceCode(res, clientId, deviceCode);
  }
};

var routes = function(app) {
  var logger = app.get('logger');

  // Client Registration Endpoint

  app.post('/token', function(req, res) {
    if (!requestHelper.isContentType(req, 'application/x-www-form-urlencoded')) {
      logger.error("Invalid content type:", req.get('Content-Type'));
      res.json(400, { error: 'invalid_request' });
      return;
    }

    // TODO: https://github.com/ebu/cpa-spec/issues/1
    if (req.body.response_type === 'device_code') {
      // http://tools.ietf.org/html/draft-ietf-oauth-dyn-reg-14#section-3
      registerPairingCode(req, res);
    }
    else if (!req.body.grant_type) {
      logger.error("Missing grant_type parameter");
      res.json(400, { error: 'invalid_request' });
    }
    else if (req.body.grant_type === 'authorization_code') {
      // RFC6749 section 4.1.3
      requestAccessToken(req, res);
    }
    else {
      logger.error("Unsupported grant_type");
      res.json(400, { error: 'unsupported_grant_type' });
    }
  });

  var renderVerificationPage = function(req, res, error_message) {
    if (typeof error_message === 'string') {
      res.status(400);
      res.render('verify.ejs', { 'values': req.body, 'error': error_message });
    } else {
      res.render('verify.ejs', { 'values': req.body, 'error': null });
    }
  };

  app.get('/verify', authHelper.ensureAuthenticated, renderVerificationPage);

  app.post('/verify', authHelper.ensureAuthenticated, function(req, res) {
    if (req.headers['content-type'] === 'application/x-www-form-urlencoded' && req.body.user_code) {

      var postedUserCode = req.body.user_code;

      verify.userCode(postedUserCode, function(err, pairingCode) {
        if (err || !pairingCode) {
          renderVerificationPage(req, res, messages.INVALID_USERCODE);
        } else {
          if (pairingCode.verified) {
            res.status(400);
            res.render('verify-info.ejs', { message: messages.OBSOLETE_USERCODE, status: 'warning' });
          } else {
            var attributes = {
              user_id:  req.user.id,
              verified: true
            };

            pairingCode.updateAttributes(attributes).success(function() {
              res.render('verify-info.ejs', { message: messages.SUCCESSFUL_PAIRING, status: 'success' });
            }).error(function() {
              res.send(500);
            });
          }
        }
      });

    } else {
      res.json(400, { error: 'invalid_request' });
    }
  });
};

module.exports = routes;
<commit_msg>Corrected content-type check in POST /verify<commit_after>"use strict";

var db = require('../models');
var generate = require('../lib/generate');
var requestHelper = require('../lib/request-helper');
var verify = require('../lib/verify');
var config = require('../config');
var messages = require('../lib/messages');
var authHelper = require('../lib/auth-helper');

var registerPairingCode = function(req, res) {
  var clientId = req.body.client_id;
  var serviceProviderName = req.body.service_provider;

  // TODO: validate clientId
  if (!clientId) {
    res.json(400, { error: 'invalid_request' });
    return;
  }
  else if (!clientId.match(/^\d+$/)) {
    res.json(400, { error: 'invalid_client' });
    return;
  }

  if (!serviceProviderName) {
    res.json(400, { error: 'invalid_request' });
    return;
  }

  db.Client.find({ where: { id: clientId } }).success(function(client) {
    if (!client) {
      res.json(400, { error: 'invalid_client' });
      return;
    }

    db.ServiceProvider.find({ where: { name: serviceProviderName }})
      .success(function(serviceProvider) {
        if (!serviceProvider) {
          res.json(400, { error: 'invalid_request' });
          return;
        }

        var pairingCode = {
          client_id: clientId,
          service_provider_id: serviceProvider.id,
          device_code: generate.deviceCode(),
          user_code: generate.userCode(),
          verification_uri: config.uris.verification_uri
        };

        db.PairingCode.create(pairingCode).then(function() {
          res.json(200, {
            device_code: pairingCode.device_code,
            user_code: pairingCode.user_code,
            verification_uri: pairingCode.verification_uri
          });
        },
        function(error) {
          res.send(500);
        });
      });
  },
  function(error) {
    res.send(500);
  });
};

var requestStandAloneAccessToken = function(res, clientId, clientSecret, serviceProvider, scope) {
  db.ServiceProvider.find({ where: {name: serviceProvider}})
    .then(function(serviceProvider) {
      if (!serviceProvider) {
        // SPEC : define correct error message
        res.json(400, { error: 'invalid_request' });
        return;
      }

      var accessToken = {
        token:               generate.accessToken(),
        user_id:             null,
        client_id:           clientId,
        service_provider_id: serviceProvider.id
      };

      // TODO: Handle duplicated tokens
      db.ServiceAccessToken
        .create(accessToken)
        .success(function(dbAccessToken) {
          res.set('Cache-Control', 'no-store');
          res.set('Pragma', 'no-cache');
          res.json({
            token:      dbAccessToken.token,
            token_type: 'bearer'
          });
        }).error(function(error){
          res.send(500);
        });

    }, function(err){
      res.send(500);
    });
};

var validateDeviceCode = function(res, clientId, deviceCode) {

  db.PairingCode
    .find({ where: { client_id: clientId, device_code: deviceCode } })
    .success(function(pairingCode) {
      if (!pairingCode) {
        res.json(400, { error: 'invalid_client' });
        return;
      }

      if (!pairingCode.verified) {
        res.json(400, { error: 'authorization_pending' });
        return;
      }

      db.sequelize.transaction(function(transaction) {
        var accessToken = {
          token:               generate.accessToken(),
          user_id:             pairingCode.user_id,
          client_id:           pairingCode.client_id,
          service_provider_id: pairingCode.service_provider_id
        };

        db.ServiceAccessToken
          .create(accessToken)
          .then(function() {
            return pairingCode.destroy();
          })
          .then(function() {
            return transaction.commit();
          })
          .then(function() {
            res.set('Cache-Control', 'no-store');
            res.set('Pragma', 'no-cache');
            res.json({
              token:      accessToken.token,
              token_type: 'bearer'
            });
          },
          function(error) {
            transaction.rollback().complete(function(err) {
              res.send(500);
            });
          });
      });
    });
};

var requestAccessToken = function(req, res) {
  var clientId = req.body.client_id;
  var clientSecret = req.body.client_secret;
  var serviceProvider = req.body.service_provider;
  var scope = req.body.scope;

  // TODO: validate clientId
  if (!clientId) {
    res.json(400, { error: 'invalid_request' });
    return;
  }

  //Stand-alone mode
  if(clientSecret) {
    verify.clientSecret(clientId, clientSecret, function(err, client) {
      if(err || !client) {
        res.json(400, {error: 'invalid_client'});
        return;
      }

      if(!serviceProvider) {
        res.json(400, { error: 'invalid_request' });
        return;
      }

      requestStandAloneAccessToken(res, clientId, clientSecret, serviceProvider, scope);
    });
  } else {

    // TODO: RFC6749 section 4.1.3 describes the 'code' parameter as "The
    // authorization code received from the authorization server." Assume this
    // is the device_code from draft-recordon-oauth-v2-device-00 section 1.4

    var deviceCode = req.body.code;

    if (!deviceCode) {
      res.json(400, { error: 'invalid_request' });
      return;
    }

    validateDeviceCode(res, clientId, deviceCode);
  }
};

var routes = function(app) {
  var logger = app.get('logger');

  // Client Registration Endpoint

  app.post('/token', function(req, res) {
    if (!requestHelper.isContentType(req, 'application/x-www-form-urlencoded')) {
      logger.error("Invalid content type:", req.get('Content-Type'));
      res.json(400, { error: 'invalid_request' });
      return;
    }

    // TODO: https://github.com/ebu/cpa-spec/issues/1
    if (req.body.response_type === 'device_code') {
      // http://tools.ietf.org/html/draft-ietf-oauth-dyn-reg-14#section-3
      registerPairingCode(req, res);
    }
    else if (!req.body.grant_type) {
      logger.error("Missing grant_type parameter");
      res.json(400, { error: 'invalid_request' });
    }
    else if (req.body.grant_type === 'authorization_code') {
      // RFC6749 section 4.1.3
      requestAccessToken(req, res);
    }
    else {
      logger.error("Unsupported grant_type");
      res.json(400, { error: 'unsupported_grant_type' });
    }
  });

  var renderVerificationPage = function(req, res, error_message) {
    if (typeof error_message === 'string') {
      res.status(400);
      res.render('verify.ejs', { 'values': req.body, 'error': error_message });
    } else {
      res.render('verify.ejs', { 'values': req.body, 'error': null });
    }
  };

  app.get('/verify', authHelper.ensureAuthenticated, renderVerificationPage);

  app.post('/verify', authHelper.ensureAuthenticated, function(req, res) {
    if (requestHelper.isContentType(req, 'application/x-www-form-urlencoded') && req.body.user_code) {
      var postedUserCode = req.body.user_code;

      verify.userCode(postedUserCode, function(err, pairingCode) {
        if (err || !pairingCode) {
          renderVerificationPage(req, res, messages.INVALID_USERCODE);
        } else {
          if (pairingCode.verified) {
            res.status(400);
            res.render('verify-info.ejs', { message: messages.OBSOLETE_USERCODE, status: 'warning' });
          } else {
            var attributes = {
              user_id:  req.user.id,
              verified: true
            };

            pairingCode.updateAttributes(attributes).success(function() {
              res.render('verify-info.ejs', { message: messages.SUCCESSFUL_PAIRING, status: 'success' });
            }).error(function() {
              res.send(500);
            });
          }
        }
      });
    }
    else {
      res.json(400, { error: 'invalid_request' });
    }
  });
};

module.exports = routes;
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

 /*
LOG ERRORS:
01 - servicio - getVersion ajax retorno diferente de 1 - validar del lado del servicio
02 - servicio - getVersion no pudo comunicarse con servicio.
03 - servicio - loginAuth no pudo comunicarse con servicio.
04 - servicio - getChanges no pudo comunicarse con servicio.
 */
 /*login = {};
 login.user = "test";

 localStorage.setItem('login', JSON.stringify(login));
*/

var utiles = {

    alerta: function(params) {
        titulo = params.titulo;
        mensaje = params.mensaje;
        btnOk = params.btnOk;

        configFancy = {
            padding:0,
            fitToView:false,
            wrapCSS : 'alertaSitio',
            autoCenter: false,
            closeEffect: 'none',
            closeSpeed:0,
            closeBtn:false,
            helpers: {
                    overlay: { closeClick: false,locked: true } //Disable click outside event
            }
        }
        if (params.close)
            configFancy.afterClose = params.close;

        $contenedorAlerta = $(document.createElement('div')).addClass('alertaCont');
        $titulo = $(document.createElement('div')).addClass('titulo').html(titulo);
        $textoMensaje = $(document.createElement('div')).addClass('textoMensaje').html(mensaje);
        $accionesConfirm = $(document.createElement('div')).addClass('accionesConfirmLayer');

        if(btnOk!=false){
            $boton = $(document.createElement('a')).addClass('cierreFancy').text(btnOk);
            $accionesConfirm.append($boton);
        }

        if(params.preload){
            $imgLoader = $(document.createElement('img')).addClass('preloader').attr('src','img/loading.gif');
            $accionesConfirm.append($imgLoader);
        }

        $contenedorAlerta
                .append($titulo)
                .append($textoMensaje)
                .append($accionesConfirm);

        configFancy.content = $contenedorAlerta;

        $.fancybox(configFancy);
    }

};

var source = document.URL.indexOf( 'http://' ) === -1 && document.URL.indexOf( 'https://' ) === -1;
if ( source ) {
    // PhoneGap application
    source_route = 'http://proyectosphp.codice.com/ferrepat_git/';
} else {
    // Web page
    source_route = 'http://localhost:81/ferrepat_git/';
}

intentos = 0,
internetIntentos=0,
linkIntentos=0;
var app = {
    version: 0,
    servicio : source_route+'webapp_service/index.php',
    urlsitio : source_route+'index.php?app=true',
    // Application Constructor
    initialize: function() {
        this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
        document.addEventListener('online', this.checkConnection('online'), false);
        document.addEventListener("offline", this.checkConnection('offline'), false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicitly call 'app.receivedEvent(...);'
    checkConnection: function(caller){
        if(caller == 'offline') console.log('Se cayó la red');
        else{
            console.log('Caller:' + caller);
            var networkState = navigator.connection.type;
            var states = {};
            states[Connection.UNKNOWN]  = {tipo:-1,lbl:'Conexión desconocida'};
            states[Connection.ETHERNET] = {tipo:1,lbl:'Conexión ethernet'};
            states[Connection.WIFI]     = {tipo:2,lbl:'Conexión Wifi'};
            states[Connection.CELL_2G]  = {tipo:3,lbl:'2G'};
            states[Connection.CELL_3G]  = {tipo:4,lbl:'3G'};
            states[Connection.CELL_4G]  = {tipo:5,lbl:'4G'};
            states[Connection.CELL]     = {tipo:6,lbl:'Celular Conexión Baja'};
            states[Connection.NONE]     = {tipo:0,lbl:'Verifique su conexión a internet por favor!'};

            return states[networkState];
        }
    },
    onDeviceReady: function() {
        var version = JSON.parse(localStorage.getItem('version'));

        if(version==null){

            localStorage.setItem('version', app.version);
            app.version = JSON.parse(localStorage.getItem('version'));

        } else {

            app.version = version;

        }

        var cssLocal = JSON.parse(localStorage.getItem('cssLocal'));
        var jsLocal = JSON.parse(localStorage.getItem('jsLocal'));

        if(cssLocal!=null)
            $('#injectedCSS').html(cssLocal);
        if(jsLocal!=null)
            $('#injectedJS').html(jsLocal);

        internet = app.checkConnection('onDeviceReady');

        if (internet.tipo!=0)
            app.checkForUpdates();
        else
        {
            utiles.alerta(
                        {
                            titulo:'Conexión',
                            mensaje:internet.lbl,
                            btnOk:(intentos<2)?"Reintentar":'Cerrar',
                            close:function(){

                                    if(internetIntentos < 3)
                                        setTimeout(function(){app.onDeviceReady();},1000);
                                    else
                                        navigator.app.exitApp();

                                    internetIntentos++;

                                }
                        }
                    )

        }
    },
    // Update DOM on a Received Event
    receivedEvent: function(id) {
        var parentElement = document.getElementById(id);
        var listeningElement = parentElement.querySelector('.listening');
        var receivedElement = parentElement.querySelector('.received');

        listeningElement.setAttribute('style', 'display:none;');
        receivedElement.setAttribute('style', 'display:block;');

        console.log('Received Event: ' + id);
    },
    checkForUpdates: function(callback){
        utiles.alerta({
                    titulo:'Actualizaciones',
                    mensaje:'Revisando actualizaciones',
                    btnOk:false,
                    preload:true
                    });
        //Codigo para validar si hay estilos, js, recursos y demás por inyectar
        $.ajax({
            url: app.servicio+'?act=getVersion',
            dataType: 'JSON',
            success: function(data, status) {
                console.log(data);
                if(data.codigo==1){
                    //validar data.version contra la version local, si es diferente inyectar lo correspondiente
                    if(app.version != data.version)
                        setTimeout(function(){$.fancybox.close();app.actualizarApp();},1000);
                    else
                        setTimeout(function(){$.fancybox.close();app.toMain();},1000);

                } else {
                    utiles.alerta({
                                    titulo:'Error',
                                    mensaje:'Ocurrió un error, favor de volver a intentar (01)
'+data.codigo,
                                    btnOk:"Ok"
                                });
                }

            },
            error: function() {
                //handle the error
                utiles.alerta({
                                titulo:'Error',
                                mensaje:'Ocurrió un error en la comunicación, favor de volver a intentar (02)',
                                btnOk:(intentos<3)?"Ok":'Cerrar',
                                close:function(){

                                    if(intentos < 3)
                                        setTimeout(function(){app.checkForUpdates();},1000);
                                    else
                                        navigator.app.exitApp();

                                    intentos++;
                                }
                            });
            }
        });
    },
    actualizarApp: function(){
        utiles.alerta({
                    titulo:'Actualizando',
                    mensaje:'Actualizando contenido',
                    btnOk:false,
                    preload:true
                    });
        //Codigo para validar si hay estilos, js, recursos y demás por inyectar
        $.ajax({
            url: app.servicio+'?act=getChanges',
            dataType: 'JSON',
            success: function(data, status) {
                if(data.codigo==1){
                    console.log(data);
                    if(data.css){
                        $('#injectedCSS').html(data.css);
                        localStorage.setItem('cssLocal', JSON.stringify(data.css));
                    }
                    if(data.js){
                        $('#injectedJS').html(data.js);
                        localStorage.setItem('jsLocal', JSON.stringify(data.js));
                    }

                    if(data.css == $('#injectedCSS').text() && data.js == $('#injectedJS').text()){
                        localStorage.setItem('version', data.version);
                        setTimeout(function(){
                            $.fancybox.close();
                            utiles.alerta({
                                    titulo:'Actualizado',
                                    mensaje:data.mensaje,
                                    btnOk:"Ok",
                                    close: function(){app.toMain();}
                                });
                        },1000);
                    } else {
                        setTimeout(function(){
                            $.fancybox.close();
                            utiles.alerta({
                                    titulo:'Error',
                                    mensaje:'Ha ocurrido un error durante la actualización, favor de reiniciar la aplicación.',
                                    btnOk:"Cerrar",
                                    close: function(){navigator.app.exitApp();}
                                });
                        },1000);
                    }

                } else {
                    utiles.alerta({
                                    titulo:'Error',
                                    mensaje:'Ocurrió un error, favor de volver a intentar
'+data.codigo,
                                    btnOk:"Ok"
                                });
                }

            },
            error: function() {
                //handle the error
                utiles.alerta({
                                titulo:'Error',
                                mensaje:'Ocurrió un error en la comunicación, favor de volver a intentar (04)',
                                btnOk:"Ok"
                            });
            }
        });
    },
    toMain: function(){

        $('#mainPage').show();
        $('#contenidoSitio').attr('src',app.urlsitio);

    },
    actualizarCarrito: function(){
        //Actualizar numero de productos en el carrito
    },
    validarInteraccion: function(msg){

        if (msg.data.type == "abrirMosaico")
        {
            app.abrirMosaico(false);
        }
        else if (msg.data.type == "putLogin")
        {
            app.putLogin(msg.data.nombreUsuario);
        }
        else if (msg.data.type == "popLogin")
        {
            app.popLogin();
        }
        else if (msg.data.type == "updateCart")
        {
            app.updateCart(msg.data.items);
        }
        else if (msg.data.type == "shareProduct" )
        {
            app.shareProduct(msg.data.info);
        }
        else if (msg.data.type == "shareProductFB" )
        {
            app.shareProductFB(msg.data.info);
        }
        else if (msg.data.type == "openLink" )
        {
            app.openLink(msg.data.url);
        }

    },
    abrirMosaico:function(soloicono){

        $('.toolsHeader a.logoInt').hide('slide',{direction:'left'});
        $('.toolsHeader a.abrirMosaico').show('slide',{direction:'right'});
        if(!soloicono){
            if($('#menuApp').hasClass('open')) $('#menuApp').removeClass('open');
            $('#menuArticulos').addClass('open');
        }
    },
    putLogin:function(nombreUsuario){
        $('#loginUsuario').css('display','block').find('#nombreUsuario').text(nombreUsuario);
    },
    popLogin:function(){
        $('#loginUsuario').css('display','none');
        $('#menuApp').removeClass('open');
    },
    updateCart:function(items){
        if(items>0){
            $('#itemsCont').css('display','table').children('span').text(items);
        } else {
            $('#itemsCont').css('display','none').children('span').text(0);
        }
    },
    shareProduct:function(info){
        console.log(info);
        //Limitaciones
        //http://www.joshmorony.com/posting-to-a-facebook-wall-with-phonegap-the-javascript-sdk/
        window.plugins.socialsharing.share(
            info.mensaje,
            null,
            info.imagen,
            info.link);
    },
    shareProductFB:function(info){
        console.log(info);
        window.plugins.socialsharing.shareViaFacebook(
            'Mensaje vía Facebook',
            null /* img */,
            info.link /* url */,
            function() {console.log('share ok')},
            function(errormsg){alert(errormsg)})
    },
    openLink:function(url){
        console.log(url);
        $('#contenidoSitio').attr('src',url);
    }
};
/*JQUERY*/
$(document).on('click','.cierreFancy',function(event){
    event.preventDefault();
    $.fancybox.close();
});
$(document).on('click','.buscarHeader',function(){
    (!$('#buscadorDesplegable').is(':visible'))?$('#buscadorDesplegable').show('slide',{direction:'up'}):$('#buscadorDesplegable').hide('slide',{direction:'up'});
});
$(document).on('click','#desplegarMenu',function(){
    if($('#menuArticulos').hasClass('open')) $('#menuArticulos').removeClass('open');
    $('#menuApp').addClass('open');
});
$(document).on('click','#menuOverlayClick',function(){
    $('#menuApp').removeClass('open');
});

$(document).on('click','.contenidoMenu ul.opcionesMenu > li > a',function(event){
    if($(this).next('ul.subMenu').length>0){
        event.preventDefault();
        if($(this).hasClass('openSubMenu'))
            $(this).removeClass('openSubMenu');
        else {
            $('.openSubMenu').removeClass('openSubMenu');
            $(this).addClass('openSubMenu');
        }
    }
});

$('.buscadorSubmit').submit(function(){

    if ( $(this).attr('id') )
    {

        $('#buscadorDesplegable').hide('slide',{direction:'up'});

    } else
    {

        $('#menuApp').removeClass('open');

    }

});

//$(document).on('click',"a[href^='https://www.ferrepat.com']",function(event){
$(document).on('click',"a[href^='http://localhost:81/ferrepat_git'],a[href^='http://proyectosphp.codice.com/ferrepat_git'],a[href^='https://www.ferrepat.com']",function(event){
    event.preventDefault();
    internet = app.checkConnection('link');

    if (internet.tipo!=0) {
        linkIntentos = 0;
        $('#contenidoSitio').attr('src',$(this).attr('href')+'?app=true');
    }
    else
    {
        utiles.alerta(
                    {
                        titulo:'Conexión',
                        mensaje:internet.lbl,
                        btnOk:"Ok"
                    }
                )
        $('#contenidoSitio').attr('src','404.html');

    }

});

$(document).on('click','.showMosaico,.noMosaico',function(){
    if ($(this).hasClass('showMosaico'))
    {
        $('.toolsHeader a.logoInt').hide('slide',{direction:'left'});
        $('.toolsHeader a.abrirMosaico').show('slide',{direction:'right'});

    } else
    {

        $('.toolsHeader a.logoInt').show('slide',{direction:'left'});
        $('.toolsHeader a.abrirMosaico').hide('slide',{direction:'right'});

    }

    if ($('#menuApp').hasClass('open')) $('#menuApp').removeClass('open');
});

$(document).on('click','a.abrirMosaico',function(event){
    event.preventDefault();
    if ($('#menuApp').hasClass('open')) $('#menuApp').removeClass('open');
    $('#menuArticulos').toggleClass('open');
});

$(document).on('click','.cerrarMenuArticulos,.cerrarMenuArticulos img',function(){
   $('#menuArticulos').removeClass('open');
});

//Comunicacion entre el iframe y esta app
window.addEventListener("message", function(msg) {

    app.validarInteraccion(msg);

});<commit_msg>No status bar overlay<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

 /*
LOG ERRORS:
01 - servicio - getVersion ajax retorno diferente de 1 - validar del lado del servicio
02 - servicio - getVersion no pudo comunicarse con servicio.
03 - servicio - loginAuth no pudo comunicarse con servicio.
04 - servicio - getChanges no pudo comunicarse con servicio.
 */
 /*login = {};
 login.user = "test";

 localStorage.setItem('login', JSON.stringify(login));
*/

var utiles = {

    alerta: function(params) {
        titulo = params.titulo;
        mensaje = params.mensaje;
        btnOk = params.btnOk;

        configFancy = {
            padding:0,
            fitToView:false,
            wrapCSS : 'alertaSitio',
            autoCenter: false,
            closeEffect: 'none',
            closeSpeed:0,
            closeBtn:false,
            helpers: {
                    overlay: { closeClick: false,locked: true } //Disable click outside event
            }
        }
        if (params.close)
            configFancy.afterClose = params.close;

        $contenedorAlerta = $(document.createElement('div')).addClass('alertaCont');
        $titulo = $(document.createElement('div')).addClass('titulo').html(titulo);
        $textoMensaje = $(document.createElement('div')).addClass('textoMensaje').html(mensaje);
        $accionesConfirm = $(document.createElement('div')).addClass('accionesConfirmLayer');

        if(btnOk!=false){
            $boton = $(document.createElement('a')).addClass('cierreFancy').text(btnOk);
            $accionesConfirm.append($boton);
        }

        if(params.preload){
            $imgLoader = $(document.createElement('img')).addClass('preloader').attr('src','img/loading.gif');
            $accionesConfirm.append($imgLoader);
        }

        $contenedorAlerta
                .append($titulo)
                .append($textoMensaje)
                .append($accionesConfirm);

        configFancy.content = $contenedorAlerta;

        $.fancybox(configFancy);
    }

};

var source = document.URL.indexOf( 'http://' ) === -1 && document.URL.indexOf( 'https://' ) === -1;
if ( source ) {
    // PhoneGap application
    source_route = 'http://proyectosphp.codice.com/ferrepat_git/';
} else {
    // Web page
    source_route = 'http://localhost:81/ferrepat_git/';
}

intentos = 0,
internetIntentos=0,
linkIntentos=0;
var app = {
    version: 0,
    servicio : source_route+'webapp_service/index.php',
    urlsitio : source_route+'index.php?app=true',
    // Application Constructor
    initialize: function() {
        this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
        document.addEventListener('online', this.checkConnection('online'), false);
        document.addEventListener("offline", this.checkConnection('offline'), false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicitly call 'app.receivedEvent(...);'
    checkConnection: function(caller){
        if(caller == 'offline') console.log('Se cayó la red');
        else{
            console.log('Caller:' + caller);
            var networkState = navigator.connection.type;
            var states = {};
            states[Connection.UNKNOWN]  = {tipo:-1,lbl:'Conexión desconocida'};
            states[Connection.ETHERNET] = {tipo:1,lbl:'Conexión ethernet'};
            states[Connection.WIFI]     = {tipo:2,lbl:'Conexión Wifi'};
            states[Connection.CELL_2G]  = {tipo:3,lbl:'2G'};
            states[Connection.CELL_3G]  = {tipo:4,lbl:'3G'};
            states[Connection.CELL_4G]  = {tipo:5,lbl:'4G'};
            states[Connection.CELL]     = {tipo:6,lbl:'Celular Conexión Baja'};
            states[Connection.NONE]     = {tipo:0,lbl:'Verifique su conexión a internet por favor!'};

            return states[networkState];
        }
    },
    onDeviceReady: function() {
        StatusBar.overlaysWebView(false);
        var version = JSON.parse(localStorage.getItem('version'));

        if(version==null){

            localStorage.setItem('version', app.version);
            app.version = JSON.parse(localStorage.getItem('version'));

        } else {

            app.version = version;

        }

        var cssLocal = JSON.parse(localStorage.getItem('cssLocal'));
        var jsLocal = JSON.parse(localStorage.getItem('jsLocal'));

        if(cssLocal!=null)
            $('#injectedCSS').html(cssLocal);
        if(jsLocal!=null)
            $('#injectedJS').html(jsLocal);

        internet = app.checkConnection('onDeviceReady');

        if (internet.tipo!=0)
            app.checkForUpdates();
        else
        {
            utiles.alerta(
                        {
                            titulo:'Conexión',
                            mensaje:internet.lbl,
                            btnOk:(intentos<2)?"Reintentar":'Cerrar',
                            close:function(){

                                    if(internetIntentos < 3)
                                        setTimeout(function(){app.onDeviceReady();},1000);
                                    else
                                        navigator.app.exitApp();

                                    internetIntentos++;

                                }
                        }
                    )

        }
    },
    // Update DOM on a Received Event
    receivedEvent: function(id) {
        var parentElement = document.getElementById(id);
        var listeningElement = parentElement.querySelector('.listening');
        var receivedElement = parentElement.querySelector('.received');

        listeningElement.setAttribute('style', 'display:none;');
        receivedElement.setAttribute('style', 'display:block;');

        console.log('Received Event: ' + id);
    },
    checkForUpdates: function(callback){
        utiles.alerta({
                    titulo:'Actualizaciones',
                    mensaje:'Revisando actualizaciones',
                    btnOk:false,
                    preload:true
                    });
        //Codigo para validar si hay estilos, js, recursos y demás por inyectar
        $.ajax({
            url: app.servicio+'?act=getVersion',
            dataType: 'JSON',
            success: function(data, status) {
                console.log(data);
                if(data.codigo==1){
                    //validar data.version contra la version local, si es diferente inyectar lo correspondiente
                    if(app.version != data.version)
                        setTimeout(function(){$.fancybox.close();app.actualizarApp();},1000);
                    else
                        setTimeout(function(){$.fancybox.close();app.toMain();},1000);

                } else {
                    utiles.alerta({
                                    titulo:'Error',
                                    mensaje:'Ocurrió un error, favor de volver a intentar (01)
'+data.codigo,
                                    btnOk:"Ok"
                                });
                }

            },
            error: function() {
                //handle the error
                utiles.alerta({
                                titulo:'Error',
                                mensaje:'Ocurrió un error en la comunicación, favor de volver a intentar (02)',
                                btnOk:(intentos<3)?"Ok":'Cerrar',
                                close:function(){

                                    if(intentos < 3)
                                        setTimeout(function(){app.checkForUpdates();},1000);
                                    else
                                        navigator.app.exitApp();

                                    intentos++;
                                }
                            });
            }
        });
    },
    actualizarApp: function(){
        utiles.alerta({
                    titulo:'Actualizando',
                    mensaje:'Actualizando contenido',
                    btnOk:false,
                    preload:true
                    });
        //Codigo para validar si hay estilos, js, recursos y demás por inyectar
        $.ajax({
            url: app.servicio+'?act=getChanges',
            dataType: 'JSON',
            success: function(data, status) {
                if(data.codigo==1){
                    console.log(data);
                    if(data.css){
                        $('#injectedCSS').html(data.css);
                        localStorage.setItem('cssLocal', JSON.stringify(data.css));
                    }
                    if(data.js){
                        $('#injectedJS').html(data.js);
                        localStorage.setItem('jsLocal', JSON.stringify(data.js));
                    }

                    if(data.css == $('#injectedCSS').text() && data.js == $('#injectedJS').text()){
                        localStorage.setItem('version', data.version);
                        setTimeout(function(){
                            $.fancybox.close();
                            utiles.alerta({
                                    titulo:'Actualizado',
                                    mensaje:data.mensaje,
                                    btnOk:"Ok",
                                    close: function(){app.toMain();}
                                });
                        },1000);
                    } else {
                        setTimeout(function(){
                            $.fancybox.close();
                            utiles.alerta({
                                    titulo:'Error',
                                    mensaje:'Ha ocurrido un error durante la actualización, favor de reiniciar la aplicación.',
                                    btnOk:"Cerrar",
                                    close: function(){navigator.app.exitApp();}
                                });
                        },1000);
                    }

                } else {
                    utiles.alerta({
                                    titulo:'Error',
                                    mensaje:'Ocurrió un error, favor de volver a intentar
'+data.codigo,
                                    btnOk:"Ok"
                                });
                }

            },
            error: function() {
                //handle the error
                utiles.alerta({
                                titulo:'Error',
                                mensaje:'Ocurrió un error en la comunicación, favor de volver a intentar (04)',
                                btnOk:"Ok"
                            });
            }
        });
    },
    toMain: function(){

        $('#mainPage').show();
        $('#contenidoSitio').attr('src',app.urlsitio);

    },
    actualizarCarrito: function(){
        //Actualizar numero de productos en el carrito
    },
    validarInteraccion: function(msg){

        if (msg.data.type == "abrirMosaico")
        {
            app.abrirMosaico(false);
        }
        else if (msg.data.type == "putLogin")
        {
            app.putLogin(msg.data.nombreUsuario);
        }
        else if (msg.data.type == "popLogin")
        {
            app.popLogin();
        }
        else if (msg.data.type == "updateCart")
        {
            app.updateCart(msg.data.items);
        }
        else if (msg.data.type == "shareProduct" )
        {
            app.shareProduct(msg.data.info);
        }
        else if (msg.data.type == "shareProductFB" )
        {
            app.shareProductFB(msg.data.info);
        }
        else if (msg.data.type == "openLink" )
        {
            app.openLink(msg.data.url);
        }

    },
    abrirMosaico:function(soloicono){

        $('.toolsHeader a.logoInt').hide('slide',{direction:'left'});
        $('.toolsHeader a.abrirMosaico').show('slide',{direction:'right'});
        if(!soloicono){
            if($('#menuApp').hasClass('open')) $('#menuApp').removeClass('open');
            $('#menuArticulos').addClass('open');
        }
    },
    putLogin:function(nombreUsuario){
        $('#loginUsuario').css('display','block').find('#nombreUsuario').text(nombreUsuario);
    },
    popLogin:function(){
        $('#loginUsuario').css('display','none');
        $('#menuApp').removeClass('open');
    },
    updateCart:function(items){
        if(items>0){
            $('#itemsCont').css('display','table').children('span').text(items);
        } else {
            $('#itemsCont').css('display','none').children('span').text(0);
        }
    },
    shareProduct:function(info){
        console.log(info);
        //Limitaciones
        //http://www.joshmorony.com/posting-to-a-facebook-wall-with-phonegap-the-javascript-sdk/
        window.plugins.socialsharing.share(
            info.mensaje,
            null,
            info.imagen,
            info.link);
    },
    shareProductFB:function(info){
        console.log(info);
        window.plugins.socialsharing.shareViaFacebook(
            'Mensaje vía Facebook',
            null /* img */,
            info.link /* url */,
            function() {console.log('share ok')},
            function(errormsg){alert(errormsg)})
    },
    openLink:function(url){
        console.log(url);
        $('#contenidoSitio').attr('src',url);
    }
};
/*JQUERY*/
$(document).on('click','.cierreFancy',function(event){
    event.preventDefault();
    $.fancybox.close();
});
$(document).on('click','.buscarHeader',function(){
    (!$('#buscadorDesplegable').is(':visible'))?$('#buscadorDesplegable').show('slide',{direction:'up'}):$('#buscadorDesplegable').hide('slide',{direction:'up'});
});
$(document).on('click','#desplegarMenu',function(){
    if($('#menuArticulos').hasClass('open')) $('#menuArticulos').removeClass('open');
    $('#menuApp').addClass('open');
});
$(document).on('click','#menuOverlayClick',function(){
    $('#menuApp').removeClass('open');
});

$(document).on('click','.contenidoMenu ul.opcionesMenu > li > a',function(event){
    if($(this).next('ul.subMenu').length>0){
        event.preventDefault();
        if($(this).hasClass('openSubMenu'))
            $(this).removeClass('openSubMenu');
        else {
            $('.openSubMenu').removeClass('openSubMenu');
            $(this).addClass('openSubMenu');
        }
    }
});

$('.buscadorSubmit').submit(function(){

    if ( $(this).attr('id') )
    {

        $('#buscadorDesplegable').hide('slide',{direction:'up'});

    } else
    {

        $('#menuApp').removeClass('open');

    }

});

//$(document).on('click',"a[href^='https://www.ferrepat.com']",function(event){
$(document).on('click',"a[href^='http://localhost:81/ferrepat_git'],a[href^='http://proyectosphp.codice.com/ferrepat_git'],a[href^='https://www.ferrepat.com']",function(event){
    event.preventDefault();
    internet = app.checkConnection('link');

    if (internet.tipo!=0) {
        linkIntentos = 0;
        $('#contenidoSitio').attr('src',$(this).attr('href')+'?app=true');
    }
    else
    {
        utiles.alerta(
                    {
                        titulo:'Conexión',
                        mensaje:internet.lbl,
                        btnOk:"Ok"
                    }
                )
        $('#contenidoSitio').attr('src','404.html');

    }

});

$(document).on('click','.showMosaico,.noMosaico',function(){
    if ($(this).hasClass('showMosaico'))
    {
        $('.toolsHeader a.logoInt').hide('slide',{direction:'left'});
        $('.toolsHeader a.abrirMosaico').show('slide',{direction:'right'});

    } else
    {

        $('.toolsHeader a.logoInt').show('slide',{direction:'left'});
        $('.toolsHeader a.abrirMosaico').hide('slide',{direction:'right'});

    }

    if ($('#menuApp').hasClass('open')) $('#menuApp').removeClass('open');
});

$(document).on('click','a.abrirMosaico',function(event){
    event.preventDefault();
    if ($('#menuApp').hasClass('open')) $('#menuApp').removeClass('open');
    $('#menuArticulos').toggleClass('open');
});

$(document).on('click','.cerrarMenuArticulos,.cerrarMenuArticulos img',function(){
   $('#menuArticulos').removeClass('open');
});

//Comunicacion entre el iframe y esta app
window.addEventListener("message", function(msg) {

    app.validarInteraccion(msg);

});<|endoftext|>


<commit_before>'use strict';

define(['app/rest', 'app/gui', 'app/client', 'app/user'], function () {

    // Controllers

    function GridCtrl($scope, orders, orderStates, $timeout, messages, Response) {
        $scope.orders = [];
        $scope.filters = {};
        $scope.states = [];

        var sort = {};
        var timeoutPromise;

        $scope.$watch('filters', filtersChanged, true);

        var firstTime = true;
        function filtersChanged () {
            if (firstTime) {
                firstTime = false;
                return;
            }
            $scope.loading = true;
            $timeout.cancel(timeoutPromise);
            timeoutPromise = $timeout(loadGrid, 1000);
        }

        function loadGrid() {
            $scope.loading = true;
            var query = filtersToQuery();
            query.sort = sortToString();
            orders.readAll(query).success(function (data) {
                $scope.orders = data;
                $scope.loading = false;
            });
        }

        loadGrid();

        messages.readAll({fetch: true}).success(function () {
                loadGrid();
        }).error(function (_, code) {
            if (code != 304) { // HTTP 304 Not Modified
                Response.defaultErrorHandler();
            }
        });

        orderStates.readAll().success(function (data) {
            $scope.states = data;
        });

        function sortToString() {
            return sort.by ? (sort.dir === 'asc' ? '' : '-') + sort.by : null;
        }

        function filtersToQuery() {
            function recursive(filters, params, fullName) {
                fullName = fullName || '';
                if (typeof filters == 'string') {
                    params['filters[' + fullName + ']'] = filters;
                } else {
                    for (var name in filters) {
                        recursive(filters[name], params, fullName === '' ? name : fullName + '.' + name);
                    }
                }
            }
            var params = {};
            recursive($scope.filters, params);
            return params;
        }

        $scope.searchClicked = function () {
            if ($scope.search) {
                $scope.filters = {};
            }
            $scope.search = !$scope.search;
        };

        $scope.openDatePicker = function($event, name) {
            $event.preventDefault();
            $event.stopPropagation();
            $scope[name + 'Opened'] = true;
        };

        $scope.sort = function (name) {
            if (sort.by === name) {
                if (sort.dir === 'asc') {
                    sort.dir = 'desc';
                } else {
                    sort = {};
                }
            } else {
                sort = {by: name, dir: 'asc'};
            }
            loadGrid();
        };

        $scope.indicator = function (name) {
            if (sort.by === name) {
                return sort.dir === 'asc' ? '▼' : '▲';
            }
        };

        var rowColors = {
            processing: 'success',
            waiting: 'info',
            postponed: 'warning',
            cancelled: '',
            completed: ''
        };

        $scope.rowColor = function (state) {
            return rowColors[state.slug]
        }
    }

    function DetailCtrl($scope, $state, orders) {
        $scope.order = {};

        orders.read($state.params.id).success(function (data) {
            $scope.order = data;
        });
    }

    function FormCtrl($scope, $state, orders, alerts, clients, $q, users, orderDraft) {
        $scope.order = orderDraft.order
            ? orderDraft.order
            : {
                event: {},
                state: {
                    name: 'Zpracovává se',
                    slug: 'processing'
                }
            };
        $scope.sending = false;
        $scope.editation = $state.current.name === 'app.order.edit';
        $scope.loading = $scope.editation;
        $scope.originalState = 'processing';

        if ($scope.editation) {
            orders.read($state.params.id).success(function (data) {
                $scope.order = data;
                $scope.originalState = data.state.slug;
                $scope.loading = false;
            });
        }

        $scope.openDatePicker = function($event) {
            $event.preventDefault();
            $event.stopPropagation();
            $scope.opened = true;
        };

        $scope.loadClients = function (value) {
            return $q(function (resolve) {
                clients.readAll({'filters[fullName]': value})
                    .success(function (data) {
                        resolve(data);
                    });
            });
        };

        $scope.loadUsers = function (value) {
            return $q(function (resolve) {
                users.readAll({'filters[fullName]': value})
                    .success(function (data) {
                        resolve(data);
                    });
            });
        };

        $scope.addClient = function () {
            orderDraft.order = $scope.order;
            $state.go('app.client.add');
        };

        $scope.save = function () {
            if (!$scope.order.client) {
                alert('Vyberte prosím klienta ze seznamu.');
                document.getElementById('client').focus();
                return;
            }

            $scope.sending = true;

            if ($scope.order.event.date instanceof Date) {
                $scope.order.event.date.setHours(2); // fixes https://github.com/angular-ui/bootstrap/issues/2072
                $scope.order.event.date = $scope.order.event.date.toString();
            }

            if ($scope.editation) {
                orders.update($state.params.id, $scope.order)
                    .success(function () {
                        alerts.prepareSuccess('Změny byly úspěšně uloženy.');
                        $state.go('app.order.grid');
                    })
                    .finally(function () {
                        $scope.sending = false;
                    });

            } else {
                orders.create($scope.order)
                    .success(function () {
                        alerts.prepareSuccess('Nová objednávka byla úspěšně vytvořena.');
                        $state.go('app.order.grid');
                    })
                    .finally(function () {
                        $scope.sending = false;
                    });
            }
        };
    }

    function TabsCtrl($scope, $state) {
        $scope.$state = $state;
        $scope.id = $state.params.id;
    }

    function MessagesCtrl($scope, $state, messages, $sce, documents, $timeout, Response) {
        $scope.messages = [];

        function show(data) {
            for (var i = 0; i < data.length; i++) {
                data[i].content = $sce.trustAsHtml(data[i].content.replace(/\n/g, '
'));
            }
            $scope.messages = data;
        }

        $scope.fetch = function () {
            $scope.fetching = true;
            messages.readAll({'order[id]': $state.params.id}).success(function (data) {
                show(data);
                messages.readAll({'order[id]': $state.params.id, fetch: true}).success(function (data) {
                    show(data);
                }).error(function (_, code) {
                    if (code != 304) { // HTTP 304 Not Modified
                        Response.defaultErrorHandler();
                    }
                }).finally(function () {
                    $scope.fetching = false;
                });
            });
        };

        $scope.fetch();

        $scope.download = function (id) {
            document.body.className = 'downloading';
            documents.read(id, {download: true}).success(function (data) {
                window.location.href = 'api/documents/' + id + '?download=true&token[key]=' + data.token.key;
                $timeout(function () {
                    document.body.className = '';
                }, 100);
            });
        };
    }

    function SendMessageCtrl($scope, $state, messages, alerts, documents, Upload, Response) {
        $scope.message = {
            documents: []
        };
        $scope.availableDocuments = [];

        function loadAvailableDocuments() {
            documents.readAll({'order[id]': $state.params.id}).success(function (data) {
                $scope.availableDocuments = data;
            });
        }
        loadAvailableDocuments();

        $scope.send = function () {
            $scope.sending = true;
            messages.create($scope.message, {'order[id]': $state.params.id}).success(function () {
                alerts.prepareSuccess('Zpráva byla úspěšně odeslána.');
                $state.go('app.order.tabs.message.grid');
            }).finally(function () {
                $scope.sending = false;
            });
        };

        $scope.select = function () {
            $scope.message.documents.push({});
        };

        $scope.remove = function (index) {
            $scope.message.documents.splice(index, 1);
        };

        $scope.upload = function (files) {
            if (files && files.length) {
                for (var i = 0; i < files.length; i++) {
                    var file = files[i];
                    $scope.uploading = true;
                    Upload.upload({
                        url: 'api/documents',
                        file: file,
                        params: {
                            'order[id]': $state.params.id
                        }
                    }).success(function (data) {
                        loadAvailableDocuments();
                        $scope.message.documents.push(data);
                    }).error(Response.defaultErrorHandler)
                        .finally(function () {
                            $scope.uploading= false;
                        });
                }
            }
        };
    }

    function DocumentCtrl($scope, $state, documents, Upload, Response, $timeout, alerts) {
        $scope.documents = [];
        $scope.deleting = {};

        function loadGrid() {
            documents.readAll({'order[id]': $state.params.id}).success(function (data) {
                $scope.documents = data;
            });
        }
        loadGrid();

        $scope.download = function (id) {
            document.body.className = 'downloading';
            documents.read(id, {download: true}).success(function (data) {
                window.location.href = 'api/documents/' + id + '?download=true&token[key]=' + data.token.key;
                $timeout(function () {
                    document.body.className = '';
                }, 100);
            });
        };

        $scope.upload = function (files) {
            $scope.uploading = true;
            if (files && files.length) {
                for (var i = 0; i < files.length; i++) {
                    var file = files[i];
                    Upload.upload({
                        url: 'api/documents',
                        file: file,
                        params: {
                            'order[id]': $state.params.id
                        }
                    }).success(function () {
                        loadGrid();
                    }).error(Response.defaultErrorHandler)
                    .finally(function () {
                        $scope.uploading= false;
                    });
                }
            }
        };

        $scope.generate = function (type) {
            $scope.generating = true;
            documents.create('', {
                'order[id]': $state.params.id,
                generate: type
            }).success(function () {
                alerts.clear();
                alerts.showSuccess('Dokument byl úspěšně vygenerován. Překontrolujte prosím údaje, které do něj byly doplněny.');
                loadGrid();
            }).error(function (error) {
                if (error.type === 'missingMandatoryItems') {
                    alerts.clear();
                    alerts.showError('Dokument nemůže být vygenerován. Doplňte prosím nejdříve tyto údaje: ' + error.items.join(', ') + '.');
                } else {
                    Response.defaultErrorHandler();
                }
            }).finally(function () {
                $scope.generating = false;
            })
        };

        $scope.delete = function (id) {
            if (!confirm('Opravdu chcete smazat dokument?')) {
                return;
            }
            $scope.deleting[id] = true;
            documents.delete(id).success(function () {
                alerts.clear();
                alerts.showSuccess('Dokument byl úspěšně smazán.');
                loadGrid();
            }).error(function (error) {
                if (error.type === 'integrityViolation') {
                    alerts.clear();
                    alerts.showError('Dokument nemůže být smazán, protože je přiřazen k již proběhlé komunikaci.');
                } else {
                    Response.defaultErrorHandler();
                }
            }).finally(function () {
                delete $scope.deleting[id];
            });
        };
    }

    // Configuration

    angular.module('app.order', ['ui.router', 'ui.bootstrap', 'app.rest', 'app.gui', 'app.client', 'app.user', 'ngFileUpload'])

        .config(function ($stateProvider) {
            $stateProvider
                .state('app.order', {
                    abstract: true,
                    url: '/order',
                    template: '

'
                })

                .state('app.order.tabs', {
                    abstract: true,
                    url: '/{id}',
                    templateUrl: 'app/order/tabs.html',
                    controller: TabsCtrl
                })

                .state('app.order.tabs.detail', {
                    url: '/detail',
                    templateUrl: 'app/order/detail.html',
                    controller: DetailCtrl
                })

                .state('app.order.tabs.message', {
                    abstract: true,
                    template: '

'
                })

                .state('app.order.tabs.message.grid', {
                    url: '/message',
                    templateUrl: 'app/order/messages.html',
                    controller: MessagesCtrl
                })

                .state('app.order.tabs.message.send', {
                    url: '/message/send',
                    templateUrl: 'app/order/sendMessage.html',
                    controller: SendMessageCtrl
                })

                .state('app.order.tabs.documents', {
                    url: '/document',
                    templateUrl: 'app/order/documents.html',
                    controller: DocumentCtrl
                })

                .state('app.order.grid', {
                    url: '/grid',
                    templateUrl: 'app/order/grid.html',
                    controller: GridCtrl
                })

                .state('app.order.edit', {
                    url: '/edit/{id}',
                    templateUrl: 'app/order/form.html',
                    controller: FormCtrl
                })

                .state('app.order.add', {
                    url: '/add',
                    templateUrl: 'app/order/form.html',
                    controller: FormCtrl
                });
        })

        .factory('orders', function (resourceFactory) {
            return resourceFactory.create('api/orders');
        })

        .factory('orderStates', function (resourceFactory) {
            return resourceFactory.create('api/order-states');
        })

        .factory('messages', function (resourceFactory) {
            return resourceFactory.create('api/messages');
        })

        .factory('documents', function (resourceFactory) {
            return resourceFactory.create('api/documents');
        })

        .value('orderDraft', {order: null})

        .run(function ($rootScope, orderDraft) {
            $rootScope.$on('$stateChangeSuccess', function (event, toState) { // draft saved only between order form and client addition
                if (toState.name !== 'app.order.add' && toState.name !== 'app.order.edit' && toState.name !== 'app.client.add') {
                    orderDraft.order = null;
                }
            });
        });

});
<commit_msg>fix: filter orders by date (closes #31)<commit_after>'use strict';

define(['app/rest', 'app/gui', 'app/client', 'app/user'], function () {

    // Controllers

    function GridCtrl($scope, orders, orderStates, $timeout, messages, Response) {
        $scope.orders = [];
        $scope.filters = {};
        $scope.states = [];

        var sort = {};
        var timeoutPromise;

        $scope.$watch('filters', filtersChanged, true);

        var firstTime = true;
        function filtersChanged () {
            if (firstTime) {
                firstTime = false;
                return;
            }
            $scope.loading = true;
            $timeout.cancel(timeoutPromise);
            timeoutPromise = $timeout(loadGrid, 1000);
        }

        function loadGrid() {
            $scope.loading = true;
            var query = filtersToQuery();
            query.sort = sortToString();
            orders.readAll(query).success(function (data) {
                $scope.orders = data;
                $scope.loading = false;
            });
        }

        loadGrid();

        messages.readAll({fetch: true}).success(function () {
                loadGrid();
        }).error(function (_, code) {
            if (code != 304) { // HTTP 304 Not Modified
                Response.defaultErrorHandler();
            }
        });

        orderStates.readAll().success(function (data) {
            $scope.states = data;
        });

        function sortToString() {
            return sort.by ? (sort.dir === 'asc' ? '' : '-') + sort.by : null;
        }

        function filtersToQuery() {
            function recursive(filters, params, fullName) {
                fullName = fullName || '';
                if (filters instanceof Date) {
                    filters = filters.toString();
                }
                if (typeof filters == 'string') {
                    params['filters[' + fullName + ']'] = filters;
                } else {
                    for (var name in filters) {
                        recursive(filters[name], params, fullName === '' ? name : fullName + '.' + name);
                    }
                }
            }
            var params = {};
            recursive($scope.filters, params);
            return params;
        }

        $scope.searchClicked = function () {
            if ($scope.search) {
                $scope.filters = {};
            }
            $scope.search = !$scope.search;
        };

        $scope.openDatePicker = function($event, name) {
            $event.preventDefault();
            $event.stopPropagation();
            $scope[name + 'Opened'] = true;
        };

        $scope.sort = function (name) {
            if (sort.by === name) {
                if (sort.dir === 'asc') {
                    sort.dir = 'desc';
                } else {
                    sort = {};
                }
            } else {
                sort = {by: name, dir: 'asc'};
            }
            loadGrid();
        };

        $scope.indicator = function (name) {
            if (sort.by === name) {
                return sort.dir === 'asc' ? '▼' : '▲';
            }
        };

        var rowColors = {
            processing: 'success',
            waiting: 'info',
            postponed: 'warning',
            cancelled: '',
            completed: ''
        };

        $scope.rowColor = function (state) {
            return rowColors[state.slug]
        }
    }

    function DetailCtrl($scope, $state, orders) {
        $scope.order = {};

        orders.read($state.params.id).success(function (data) {
            $scope.order = data;
        });
    }

    function FormCtrl($scope, $state, orders, alerts, clients, $q, users, orderDraft) {
        $scope.order = orderDraft.order
            ? orderDraft.order
            : {
                event: {},
                state: {
                    name: 'Zpracovává se',
                    slug: 'processing'
                }
            };
        $scope.sending = false;
        $scope.editation = $state.current.name === 'app.order.edit';
        $scope.loading = $scope.editation;
        $scope.originalState = 'processing';

        if ($scope.editation) {
            orders.read($state.params.id).success(function (data) {
                $scope.order = data;
                $scope.originalState = data.state.slug;
                $scope.loading = false;
            });
        }

        $scope.openDatePicker = function($event) {
            $event.preventDefault();
            $event.stopPropagation();
            $scope.opened = true;
        };

        $scope.loadClients = function (value) {
            return $q(function (resolve) {
                clients.readAll({'filters[fullName]': value})
                    .success(function (data) {
                        resolve(data);
                    });
            });
        };

        $scope.loadUsers = function (value) {
            return $q(function (resolve) {
                users.readAll({'filters[fullName]': value})
                    .success(function (data) {
                        resolve(data);
                    });
            });
        };

        $scope.addClient = function () {
            orderDraft.order = $scope.order;
            $state.go('app.client.add');
        };

        $scope.save = function () {
            if (!$scope.order.client) {
                alert('Vyberte prosím klienta ze seznamu.');
                document.getElementById('client').focus();
                return;
            }

            $scope.sending = true;

            if ($scope.order.event.date instanceof Date) {
                $scope.order.event.date.setHours(2); // fixes https://github.com/angular-ui/bootstrap/issues/2072
                $scope.order.event.date = $scope.order.event.date.toString();
            }

            if ($scope.editation) {
                orders.update($state.params.id, $scope.order)
                    .success(function () {
                        alerts.prepareSuccess('Změny byly úspěšně uloženy.');
                        $state.go('app.order.grid');
                    })
                    .finally(function () {
                        $scope.sending = false;
                    });

            } else {
                orders.create($scope.order)
                    .success(function () {
                        alerts.prepareSuccess('Nová objednávka byla úspěšně vytvořena.');
                        $state.go('app.order.grid');
                    })
                    .finally(function () {
                        $scope.sending = false;
                    });
            }
        };
    }

    function TabsCtrl($scope, $state) {
        $scope.$state = $state;
        $scope.id = $state.params.id;
    }

    function MessagesCtrl($scope, $state, messages, $sce, documents, $timeout, Response) {
        $scope.messages = [];

        function show(data) {
            for (var i = 0; i < data.length; i++) {
                data[i].content = $sce.trustAsHtml(data[i].content.replace(/\n/g, '
'));
            }
            $scope.messages = data;
        }

        $scope.fetch = function () {
            $scope.fetching = true;
            messages.readAll({'order[id]': $state.params.id}).success(function (data) {
                show(data);
                messages.readAll({'order[id]': $state.params.id, fetch: true}).success(function (data) {
                    show(data);
                }).error(function (_, code) {
                    if (code != 304) { // HTTP 304 Not Modified
                        Response.defaultErrorHandler();
                    }
                }).finally(function () {
                    $scope.fetching = false;
                });
            });
        };

        $scope.fetch();

        $scope.download = function (id) {
            document.body.className = 'downloading';
            documents.read(id, {download: true}).success(function (data) {
                window.location.href = 'api/documents/' + id + '?download=true&token[key]=' + data.token.key;
                $timeout(function () {
                    document.body.className = '';
                }, 100);
            });
        };
    }

    function SendMessageCtrl($scope, $state, messages, alerts, documents, Upload, Response) {
        $scope.message = {
            documents: []
        };
        $scope.availableDocuments = [];

        function loadAvailableDocuments() {
            documents.readAll({'order[id]': $state.params.id}).success(function (data) {
                $scope.availableDocuments = data;
            });
        }
        loadAvailableDocuments();

        $scope.send = function () {
            $scope.sending = true;
            messages.create($scope.message, {'order[id]': $state.params.id}).success(function () {
                alerts.prepareSuccess('Zpráva byla úspěšně odeslána.');
                $state.go('app.order.tabs.message.grid');
            }).finally(function () {
                $scope.sending = false;
            });
        };

        $scope.select = function () {
            $scope.message.documents.push({});
        };

        $scope.remove = function (index) {
            $scope.message.documents.splice(index, 1);
        };

        $scope.upload = function (files) {
            if (files && files.length) {
                for (var i = 0; i < files.length; i++) {
                    var file = files[i];
                    $scope.uploading = true;
                    Upload.upload({
                        url: 'api/documents',
                        file: file,
                        params: {
                            'order[id]': $state.params.id
                        }
                    }).success(function (data) {
                        loadAvailableDocuments();
                        $scope.message.documents.push(data);
                    }).error(Response.defaultErrorHandler)
                        .finally(function () {
                            $scope.uploading= false;
                        });
                }
            }
        };
    }

    function DocumentCtrl($scope, $state, documents, Upload, Response, $timeout, alerts) {
        $scope.documents = [];
        $scope.deleting = {};

        function loadGrid() {
            documents.readAll({'order[id]': $state.params.id}).success(function (data) {
                $scope.documents = data;
            });
        }
        loadGrid();

        $scope.download = function (id) {
            document.body.className = 'downloading';
            documents.read(id, {download: true}).success(function (data) {
                window.location.href = 'api/documents/' + id + '?download=true&token[key]=' + data.token.key;
                $timeout(function () {
                    document.body.className = '';
                }, 100);
            });
        };

        $scope.upload = function (files) {
            $scope.uploading = true;
            if (files && files.length) {
                for (var i = 0; i < files.length; i++) {
                    var file = files[i];
                    Upload.upload({
                        url: 'api/documents',
                        file: file,
                        params: {
                            'order[id]': $state.params.id
                        }
                    }).success(function () {
                        loadGrid();
                    }).error(Response.defaultErrorHandler)
                    .finally(function () {
                        $scope.uploading= false;
                    });
                }
            }
        };

        $scope.generate = function (type) {
            $scope.generating = true;
            documents.create('', {
                'order[id]': $state.params.id,
                generate: type
            }).success(function () {
                alerts.clear();
                alerts.showSuccess('Dokument byl úspěšně vygenerován. Překontrolujte prosím údaje, které do něj byly doplněny.');
                loadGrid();
            }).error(function (error) {
                if (error.type === 'missingMandatoryItems') {
                    alerts.clear();
                    alerts.showError('Dokument nemůže být vygenerován. Doplňte prosím nejdříve tyto údaje: ' + error.items.join(', ') + '.');
                } else {
                    Response.defaultErrorHandler();
                }
            }).finally(function () {
                $scope.generating = false;
            })
        };

        $scope.delete = function (id) {
            if (!confirm('Opravdu chcete smazat dokument?')) {
                return;
            }
            $scope.deleting[id] = true;
            documents.delete(id).success(function () {
                alerts.clear();
                alerts.showSuccess('Dokument byl úspěšně smazán.');
                loadGrid();
            }).error(function (error) {
                if (error.type === 'integrityViolation') {
                    alerts.clear();
                    alerts.showError('Dokument nemůže být smazán, protože je přiřazen k již proběhlé komunikaci.');
                } else {
                    Response.defaultErrorHandler();
                }
            }).finally(function () {
                delete $scope.deleting[id];
            });
        };
    }

    // Configuration

    angular.module('app.order', ['ui.router', 'ui.bootstrap', 'app.rest', 'app.gui', 'app.client', 'app.user', 'ngFileUpload'])

        .config(function ($stateProvider) {
            $stateProvider
                .state('app.order', {
                    abstract: true,
                    url: '/order',
                    template: '

'
                })

                .state('app.order.tabs', {
                    abstract: true,
                    url: '/{id}',
                    templateUrl: 'app/order/tabs.html',
                    controller: TabsCtrl
                })

                .state('app.order.tabs.detail', {
                    url: '/detail',
                    templateUrl: 'app/order/detail.html',
                    controller: DetailCtrl
                })

                .state('app.order.tabs.message', {
                    abstract: true,
                    template: '

'
                })

                .state('app.order.tabs.message.grid', {
                    url: '/message',
                    templateUrl: 'app/order/messages.html',
                    controller: MessagesCtrl
                })

                .state('app.order.tabs.message.send', {
                    url: '/message/send',
                    templateUrl: 'app/order/sendMessage.html',
                    controller: SendMessageCtrl
                })

                .state('app.order.tabs.documents', {
                    url: '/document',
                    templateUrl: 'app/order/documents.html',
                    controller: DocumentCtrl
                })

                .state('app.order.grid', {
                    url: '/grid',
                    templateUrl: 'app/order/grid.html',
                    controller: GridCtrl
                })

                .state('app.order.edit', {
                    url: '/edit/{id}',
                    templateUrl: 'app/order/form.html',
                    controller: FormCtrl
                })

                .state('app.order.add', {
                    url: '/add',
                    templateUrl: 'app/order/form.html',
                    controller: FormCtrl
                });
        })

        .factory('orders', function (resourceFactory) {
            return resourceFactory.create('api/orders');
        })

        .factory('orderStates', function (resourceFactory) {
            return resourceFactory.create('api/order-states');
        })

        .factory('messages', function (resourceFactory) {
            return resourceFactory.create('api/messages');
        })

        .factory('documents', function (resourceFactory) {
            return resourceFactory.create('api/documents');
        })

        .value('orderDraft', {order: null})

        .run(function ($rootScope, orderDraft) {
            $rootScope.$on('$stateChangeSuccess', function (event, toState) { // draft saved only between order form and client addition
                if (toState.name !== 'app.order.add' && toState.name !== 'app.order.edit' && toState.name !== 'app.client.add') {
                    orderDraft.order = null;
                }
            });
        });

});
<|endoftext|>


<commit_before>var Datastore = require('nedb');
var db = new Datastore({ filename: 'latest2.txt' });

module.exports = {};
module.exports.connect = function () { // connect to database
	return new Promise(function (resolve, reject) {
		db.loadDatabase(function (err) {
			if (err) {
				reject(err);
			} else {
				resolve();
			}
		});
	});
};

module.exports.pushData = function (values) { // add new data, as JSON
	return new Promise(function (resolve, reject) {
		db.insert(values, function (err, newDoc) {
			if (err) {
				reject(err);
			} else {
				resolve(newDoc);
			}
		});
	});
};

module.exports.getGraphs = function () { // get graphs TODO: specify time/date
	return new Promise(function (resolve, reject) {
		db.find({}).sort({ time: -1 }).limit(100).exec(function (err, docs) { // query 100 newest entries, newest first
			if (err) {
				reject(err);
			} else {
				resolve(docs.reverse());
			}
		});
	});
};

module.exports.getExportAll = function () { // get all data in database
	return new Promise(function (resolve, reject) {
		db.find({}).sort({ time: 1 }).exec(function (err, docs) { // find all records and sort
			if (err) {
				reject(err);
			} else {
				resolve();
			}
		});
	});
};

module.exports.getExportBetweenDates = function (dateStart, dateEnd) { // get data between dates
	return new Promise(function (resolve, reject) { // TODO filter dates
		db.find({}).sort({ time: 1 }).exec(function (err, docs) { // find all records and sort
			if (err) {
				reject(err);
			} else {
				resolve();
			}
		});
	});
};<commit_msg>actually resolve the docs<commit_after>var Datastore = require('nedb');
var db = new Datastore({ filename: 'latest2.txt' });

module.exports = {};
module.exports.connect = function () { // connect to database
	return new Promise(function (resolve, reject) {
		db.loadDatabase(function (err) {
			if (err) {
				reject(err);
			} else {
				resolve();
			}
		});
	});
};

module.exports.pushData = function (values) { // add new data, as JSON
	return new Promise(function (resolve, reject) {
		db.insert(values, function (err, newDoc) {
			if (err) {
				reject(err);
			} else {
				resolve(newDoc);
			}
		});
	});
};

module.exports.getGraphs = function () { // get graphs TODO: specify time/date
	return new Promise(function (resolve, reject) {
		db.find({}).sort({ time: -1 }).limit(100).exec(function (err, docs) { // query 100 newest entries, newest first
			if (err) {
				reject(err);
			} else {
				resolve(docs.reverse());
			}
		});
	});
};

module.exports.getExportAll = function () { // get all data in database
	return new Promise(function (resolve, reject) {
		db.find({}).sort({ time: 1 }).exec(function (err, docs) { // find all records and sort
			if (err) {
				reject(err);
			} else {
				resolve(docs);
			}
		});
	});
};

module.exports.getExportBetweenDates = function (dateStart, dateEnd) { // get data between dates
	return new Promise(function (resolve, reject) { // TODO filter dates
		db.find({}).sort({ time: 1 }).exec(function (err, docs) { // find all records and sort
			if (err) {
				reject(err);
			} else {
				resolve(docs);
			}
		});
	});
};<|endoftext|>


<commit_before>import traverser from '../traverser';
import TemplateLiteral from './../syntax/TemplateLiteral';
import TemplateElement from './../syntax/TemplateElement';
import isString from './../utils/isString';
import _ from 'lodash';

export default function(ast) {
  traverser.replace(ast, {
    enter(node) {
      if (isPlusExpression(node)) {
        this.skip();

        const [operands, isStringConcatenation, comments] = flattenPlusExpression(node);

        if (isStringConcatenation && !operands.every(isString)) {
          const literal = new TemplateLiteral(splitQuasisAndExpressions(operands));
          literal.comments = comments;
          return literal;
        }
      }
    }
  });
}

// Returns three items:
// - flat array of all the plus operation sub-expressions
// - true when the result of the plus operation is a string
// - array of comments
function flattenPlusExpression(node) {
  if (isPlusExpression(node)) {
    const [left, leftIsString, leftComments] = flattenPlusExpression(node.left);
    const [right, rightIsString, rightComments] = flattenPlusExpression(node.right);

    if (leftIsString || rightIsString) {
      const operands = _.flatten([left, right]);
      const comments = _.flatten([
        node.comments || [],
        leftComments,
        rightComments
      ]);
      return [operands, true, comments];
    }
    else {
      return [node, false, node.comments || []];
    }
  }
  else {
    return [node, isString(node), node.comments || []];
  }
}

function isPlusExpression(node) {
  return node.type === 'BinaryExpression' && node.operator === '+';
}

function splitQuasisAndExpressions(operands) {
  const quasis = [];
  const expressions = [];

  for (let i = 0; i < operands.length; i++) {
    const curr = operands[i];

    if (isString(curr)) {
      let currVal = curr.value;
      let currRaw = escapeForTemplate(curr.raw);

      while (isString(operands[i + 1] || {})) {
        i++;
        currVal += operands[i].value;
        currRaw += escapeForTemplate(operands[i].raw);
      }

      quasis.push(new TemplateElement({
        raw: currRaw,
        cooked: currVal
      }));
    }
    else {
      if (i === 0) {
        quasis.push(new TemplateElement({}));
      }

      if (!isString(operands[i + 1] || {})) {
        quasis.push(new TemplateElement({
          tail: operands[i + 1] === undefined
        }));
      }

      expressions.push(curr);
    }
  }

  return {quasis, expressions};
}

// Strip surrounding quotes, escape backticks and unescape escaped quotes
function escapeForTemplate(raw) {
  return raw
    .replace(/^['"]|['"]$/g, '')
    .replace(/`/g, '\\`')
    .replace(/\\(['"])/g, '$1');
}
<commit_msg>Use object instead of array<commit_after>import traverser from '../traverser';
import TemplateLiteral from './../syntax/TemplateLiteral';
import TemplateElement from './../syntax/TemplateElement';
import isString from './../utils/isString';
import _ from 'lodash';

export default function(ast) {
  traverser.replace(ast, {
    enter(node) {
      if (isPlusExpression(node)) {
        this.skip();

        const plusExpr = flattenPlusExpression(node);

        if (plusExpr.isString && !plusExpr.operands.every(isString)) {
          const literal = new TemplateLiteral(splitQuasisAndExpressions(plusExpr.operands));
          literal.comments = plusExpr.comments;
          return literal;
        }
      }
    }
  });
}

// Returns object of three fields:
// - operands: flat array of all the plus operation sub-expressions
// - comments: array of comments
// - isString: true when the result of the whole plus operation is a string
function flattenPlusExpression(node) {
  if (isPlusExpression(node)) {
    const left = flattenPlusExpression(node.left);
    const right = flattenPlusExpression(node.right);

    if (left.isString || right.isString) {
      return {
        operands: _.flatten([left.operands, right.operands]),
        comments: _.flatten([
          node.comments || [],
          left.comments,
          right.comments
        ]),
        isString: true,
      };
    }
    else {
      return {
        operands: [node],
        comments: node.comments || [],
        isString: false,
      };
    }
  }
  else {
    return {
      operands: [node],
      comments: node.comments || [],
      isString: isString(node),
    };
  }
}

function isPlusExpression(node) {
  return node.type === 'BinaryExpression' && node.operator === '+';
}

function splitQuasisAndExpressions(operands) {
  const quasis = [];
  const expressions = [];

  for (let i = 0; i < operands.length; i++) {
    const curr = operands[i];

    if (isString(curr)) {
      let currVal = curr.value;
      let currRaw = escapeForTemplate(curr.raw);

      while (isString(operands[i + 1] || {})) {
        i++;
        currVal += operands[i].value;
        currRaw += escapeForTemplate(operands[i].raw);
      }

      quasis.push(new TemplateElement({
        raw: currRaw,
        cooked: currVal
      }));
    }
    else {
      if (i === 0) {
        quasis.push(new TemplateElement({}));
      }

      if (!isString(operands[i + 1] || {})) {
        quasis.push(new TemplateElement({
          tail: operands[i + 1] === undefined
        }));
      }

      expressions.push(curr);
    }
  }

  return {quasis, expressions};
}

// Strip surrounding quotes, escape backticks and unescape escaped quotes
function escapeForTemplate(raw) {
  return raw
    .replace(/^['"]|['"]$/g, '')
    .replace(/`/g, '\\`')
    .replace(/\\(['"])/g, '$1');
}
<|endoftext|>