<commit_before>'use strict';

angular.module('myApp')
  .config(function ($stateProvider) {
    $stateProvider
      .state('home', {
        url: '/',
        templateUrl: 'home/home.html',
        controller: 'homeController'
      });
  })
  .controller('homeController', function($scope, $http){
  	$scope.code = "000001"
    $scope.codex = "000001.SS";
    $scope.analyst = '赵晓光';
    //52.198.111.194
    $scope.serverurl = 'http://10.254.145.220:3000/';

    $scope.itemArray = [
        {uid: 1, name: '赵晓光',domain:'电子'},
        {uid: 2, name: '武超则',domain:'通讯'},
    ];

    if(localStorage.getItem('anas')){
        $scope.itemArray = JSON.parse(localStorage.getItem('anas'));
    }else{
        $http.get($scope.serverurl+"ana").success(function(data) {
            localStorage.setItem('anas',JSON.stringify(data));
            $scope.itemArray = data;
        }).error(function(data){
          console.log("get analysts failed")
        });
    }

    $scope.selected = { analyst: $scope.itemArray[0] };

    $scope.queryfromYahoo = function(){
        if($scope.code.startsWith("000001")){
            $scope.codex = "sh000001";
        }else if($scope.code.startsWith("60")){
            $scope.codex = "sh"+$scope.code;
        }else{
            $scope.codex = "sz"+$scope.code;
        }

        var url = 'http://web.ifzq.gtimg.cn/appstock/app/fqkline/get?_var=kline_dayqfq&param='+$scope.codex+',day,,,320,qfq';
        var encodedURI = encodeURI(url);

         $http.get(encodedURI).success(function(data) {
            try{
                var p = /kline_dayqfq=(.*)/
                var m = p.exec(data)
                var payload = m[1]
                var jload = JSON.parse(payload)
                var  datat = []
                for(var item in jload.data){
                    if("sh000001"==item || item.startsWith("sz399")){
                        for(var i = 0; i<jload.data[item].day.length;i++){
                            datat.push([
                                ( new Date( jload.data[item].day[ i ][0]) ).getTime(),
                                parseFloat( jload.data[item].day[ i ][2] )
                            ]);
                        }
                    }else{
                         for(var i = 0; i<jload.data[item].qfqday.length;i++){
                            datat.push([
                                ( new Date( jload.data[item].qfqday[ i ][0]) ).getTime(),
                                parseFloat( jload.data[item].qfqday[ i ][2] )
                            ]);
                        }
                    }

                }

                $scope.stockdata = datat;
                $scope.chartConfig.series = [];
                $scope.chartConfig.series.push({
                id: 'dataseries',
                data: datat
                });

                $scope.queryReport();

                return datat;

            }catch(e){
                $scope.response = JSON.stringify({error:"error from server side"});
                console.log("get data error")
            }

        }).error(function(data){

              $scope.response = JSON.stringify({"error":"error from server side","data":data},null,Number(2));
              console.log("parse data error")
        });

    }

    $scope.constructLink = function(item){
        var t = item['datetime'].split('T');
        var d = t[0];
        var dd = d.replace(/-/g,'');
        var url = "http://data.eastmoney.com/report/"+dd+"/"+item['infoCode']+".html";
        return url;
    };

    $scope.queryReport = function(){
        // var reporturl = "http://52.198.111.194:3000/author/"+$scope.selected.analyst.name;
        var reporturl = $scope.serverurl+"author/"+$scope.selected.analyst.name;
        $http.get(reporturl).success(function(data) {
            try{
                // console.log(data);
                var tdata = [];
                for (var i = 0; i <data.length; i++) {
                  var d = data[i];
                  if("000001" ==  $scope.code || $scope.code.startsWith("399")){
                    tdata.push({'x': new Date(d['datetime']).getTime(),'title': d['title'],'url':d['pdfurl']});
                  }else if(d['secuFullCode'].indexOf($scope.code)>-1){
                    tdata.push({'x': new Date(d['datetime']).getTime(),'title': d['title'],'url':d['pdfurl']});
                  }

                }

                tdata.sort(function(a,b){
                    return a['x']-b['x'];
                })

                var anaseries = {
                    type: 'flags',
                    name: 'Flags on series',
                    data: tdata,
                    onSeries: 'dataseries',
                    shape: 'squarepin'
                };

                $scope.chartConfig.options.plotOptions={
                    flags:{
                        point:{
                        events:{
                            click:function(e){
                                e.preventDefault();

                                var url = this.url;

                                window.open(url,'_blank');
                            }
                        }
                        }
                    }
                },
                $scope.chartConfig.series = [];
                $scope.chartConfig.series.push({
                id: 'dataseries',
                data: $scope.stockdata
                });
                $scope.chartConfig.series.push(anaseries);

            }catch(e){
                $scope.response = JSON.stringify({error:"error from server side"});
            }
        }).error(function(data){

              $scope.response = JSON.stringify({"error":"error from server side","data":data},null,Number(2));
        });

    }

    $scope.chartConfig = {
        options: {
            chart: {
                zoomType: 'x'
            },
            navigator: {
                enabled: true
            }
        },
        series: [],
        title: {
            text: '分析师研报跟踪'
        },
        rangeSelector : {
            selected : 1
        },
        useHighStocks: true
    }

    $scope.queryfromYahoo();
});<commit_msg>all anas<commit_after>'use strict';

angular.module('myApp')
  .config(function ($stateProvider) {
    $stateProvider
      .state('home', {
        url: '/',
        templateUrl: 'home/home.html',
        controller: 'homeController'
      });
  })
  .controller('homeController', function($scope, $http){
  	$scope.code = "000001"
    $scope.codex = "000001.SS";
    $scope.analyst = '赵晓光';
    //52.198.111.194
    $scope.serverurl = 'http://52.198.111.194:3000/';

    $scope.itemArray = [
        {uid: 1, name: '赵晓光',domain:'电子'},
        {uid: 2, name: '武超则',domain:'通讯'},
    ];

    if(localStorage.getItem('anas')){
        $scope.itemArray = JSON.parse(localStorage.getItem('anas'));
        $scope.itemArray.push({uid:999,name:'所有头牌',domain:'其他'})
    }else{
        $http.get($scope.serverurl+"ana").success(function(data) {
            localStorage.setItem('anas',JSON.stringify(data));
            $scope.itemArray = data;
            $scope.itemArray.push({uid:999,name:'所有头牌',domain:'其他'})
        }).error(function(data){
          console.log("get analysts failed")
        });
    }

    $scope.selected = { analyst: $scope.itemArray[0] };

    $scope.queryfromYahoo = function(){
        if($scope.code.startsWith("000001")){
            $scope.codex = "sh000001";
        }else if($scope.code.startsWith("60")){
            $scope.codex = "sh"+$scope.code;
        }else{
            $scope.codex = "sz"+$scope.code;
        }

        var url = 'http://web.ifzq.gtimg.cn/appstock/app/fqkline/get?_var=kline_dayqfq&param='+$scope.codex+',day,,,320,qfq';
        var encodedURI = encodeURI(url);

         $http.get(encodedURI).success(function(data) {
            try{
                var p = /kline_dayqfq=(.*)/
                var m = p.exec(data)
                var payload = m[1]
                var jload = JSON.parse(payload)
                var  datat = []
                for(var item in jload.data){
                    if("sh000001"==item || item.startsWith("sz399")){
                        for(var i = 0; i<jload.data[item].day.length;i++){
                            datat.push([
                                ( new Date( jload.data[item].day[ i ][0]) ).getTime(),
                                parseFloat( jload.data[item].day[ i ][2] )
                            ]);
                        }
                    }else{
                         for(var i = 0; i<jload.data[item].qfqday.length;i++){
                            datat.push([
                                ( new Date( jload.data[item].qfqday[ i ][0]) ).getTime(),
                                parseFloat( jload.data[item].qfqday[ i ][2] )
                            ]);
                        }
                    }

                }

                $scope.stockdata = datat;
                $scope.chartConfig.series = [];
                $scope.chartConfig.series.push({
                id: 'dataseries',
                data: datat
                });

                $scope.queryReport();

                return datat;

            }catch(e){
                $scope.response = JSON.stringify({error:"error from server side"});
                console.log("get data error")
            }

        }).error(function(data){

              $scope.response = JSON.stringify({"error":"error from server side","data":data},null,Number(2));
              console.log("parse data error")
        });

    }

    $scope.constructLink = function(item){
        var t = item['datetime'].split('T');
        var d = t[0];
        var dd = d.replace(/-/g,'');
        var url = "http://data.eastmoney.com/report/"+dd+"/"+item['infoCode']+".html";
        return url;
    };

    $scope.queryReport = function(){
        // var reporturl = "http://52.198.111.194:3000/author/"+$scope.selected.analyst.name;
        var reporturl = $scope.serverurl+"author/"+$scope.selected.analyst.name;
        if($scope.selected.analyst.uid = 999){
            reporturl = $scope.serverurl+"code/"+$scope.code;
        }

        $http.get(reporturl).success(function(data) {
            try{
                // console.log(data);
                var tdata = [];
                for (var i = 0; i <data.length; i++) {
                  var d = data[i];
                  if("000001" ==  $scope.code || $scope.code.startsWith("399")){
                    tdata.push({'x': new Date(d['datetime']).getTime(),'title': d['title'],'url':d['pdfurl']});
                  }else if(d['secuFullCode'].indexOf($scope.code)>-1){
                    tdata.push({'x': new Date(d['datetime']).getTime(),'title': d['title'],'url':d['pdfurl']});
                  }

                }

                tdata.sort(function(a,b){
                    return a['x']-b['x'];
                })

                var anaseries = {
                    type: 'flags',
                    name: 'Flags on series',
                    data: tdata,
                    onSeries: 'dataseries',
                    shape: 'squarepin'
                };

                $scope.chartConfig.options.plotOptions={
                    flags:{
                        point:{
                        events:{
                            click:function(e){
                                e.preventDefault();

                                var url = this.url;

                                window.open(url,'_blank');
                            }
                        }
                        }
                    }
                },
                $scope.chartConfig.series = [];
                $scope.chartConfig.series.push({
                id: 'dataseries',
                data: $scope.stockdata
                });
                $scope.chartConfig.series.push(anaseries);

            }catch(e){
                $scope.response = JSON.stringify({error:"error from server side"});
            }
        }).error(function(data){

              $scope.response = JSON.stringify({"error":"error from server side","data":data},null,Number(2));
        });

    }

    $scope.chartConfig = {
        options: {
            chart: {
                zoomType: 'x'
            },
            navigator: {
                enabled: true
            }
        },
        series: [],
        title: {
            text: '分析师研报跟踪'
        },
        rangeSelector : {
            selected : 1
        },
        useHighStocks: true
    }

    $scope.queryfromYahoo();
});<|endoftext|>


<commit_before>// View model for the courses page
function pageViewModel(gvm) {
    gvm.userId = -1;
    // Page specific i18n bindings
    gvm.title = ko.computed(function(){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("AccountTitle");}, gvm);
    gvm.pageHeader = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("UserlistsTitle");}, gvm);
    gvm.myLists = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("myLists");}, gvm);
    gvm.addStudListBtn = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("addStudListBtn");}, gvm);
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);

    gvm.codeTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("CodeTableTitle");}, gvm);
    gvm.nameTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("NameTableTitle");}, gvm);
    gvm.descTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("DescTableTitle");}, gvm);
    gvm.actionTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ActionTableTitle");}, gvm);

    gvm.firstname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("Firstname");}, gvm);
    gvm.lastname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("Lastname");}, gvm);
    gvm.email = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("Email");}, gvm);
    gvm.memberSince = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("MemberSince");}, gvm);
    gvm.myProjects = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("MyProjects");}, gvm);

    // The table data observable array
    gvm.tabledata = ko.observableArray([]);

    // Add data to the table
    gvm.addTableData = function(id, name) {
        // Push data
        var tblOject = {tid: id, tname: name};
        gvm.tabledata.push(tblOject);

        $('#editbtn-' + id).bind('click', function(event, data) {
            showEditStudentListsModal(tblOject);
            event.stopPropagation();
        });

        $('#removebtn-' + id).bind('click', function(event, data){
            // Delete the table item
            deleteTableItem(id, tblOject);
            event.stopPropagation();
        });
    }

    gvm.clearTable = function() {
        gvm.tabledata.removeAll();
    }
}

function showEditStudentListsModal(object) {
        resetGeneralModal();
        setGeneralModalTitle(i18n.__("EditProjectTitle"));
        setGeneralModalBody('<form id="updateStudentLists"> \

 \
                <input type="text" class="form-control input-lg" placeholder="' + object.tname + '" " name="name" value="' + object.tname + '"> \

 \
        </form>');
        $.getJSON()

        addGeneralModalButton(i18n.__("SaveBtn"), function(){
            updateStudentsList(object.tid, $('#updateStudentLists').serialize(), function(result){
                hideModal();
            });
        });

        addGeneralModalButton(i18n.__("CancelBtn"), function(){
            hideModal();
        })

        showGeneralModal();
}

function updateStudentsList(id, name) {
    $.ajax({
        url: "/api/studentlist/" + id,
        type: "PUT",
        data: name,
        success: function(data) {
            //viewModel.addTableData(data['id'], data['code'], data['name'], data['description']);
            loadTable(viewModel.userId); //TODO now it is refreshing table after updating but it redirects to pagenr 1
            callback(true);
        },
        error: function(data) {
            callback(false);
        }
    });
}

function deleteTableItem(id, tblObject){
    showYesNoModal("Bent u zeker dat u dit item wil verwijderen?", function(val){
        if(val){
            $.ajax({
                url: '/api/studentlist/delete/' + id,
                type: "DELETE",
                success: function() {
                    viewModel.tabledata.remove(tblObject);
                }
            });
        }
    });
}

function loadTable(id) {
    $.getJSON('/api/studentlists/' + id, function(data) {
        viewModel.clearTable();
        // Load table data
        $.each(data, function(i, item) {
            viewModel.addTableData(item.id, item.name);
        });
    });
}

function initPage() {
    // Fetch userdata
    $.getJSON('/api/currentuser', function(data) {
        viewModel.userId = data.id;
        loadTable(data.id);
    });

    $('#addStudentList').click(function(){
        showUploadModal(function(src, id){
            // Save new picture in database
            $.ajax({
               type: "POST",
               url: '/api/csv/studentlist',
               data: "fileid="+id,
               success: function(data){
                   location.reload();
                   // Do something with student list
               }
            });
        });
    })
}<commit_msg>student list insert<commit_after>// View model for the courses page
function pageViewModel(gvm) {
    gvm.userId = -1;
    // Page specific i18n bindings
    gvm.title = ko.computed(function(){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("AccountTitle");}, gvm);
    gvm.pageHeader = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("UserlistsTitle");}, gvm);
    gvm.myLists = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("myLists");}, gvm);
    gvm.addStudListBtn = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("addStudListBtn");}, gvm);
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);

    gvm.codeTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("CodeTableTitle");}, gvm);
    gvm.nameTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("NameTableTitle");}, gvm);
    gvm.descTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("DescTableTitle");}, gvm);
    gvm.actionTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ActionTableTitle");}, gvm);

    gvm.firstname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("Firstname");}, gvm);
    gvm.lastname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("Lastname");}, gvm);
    gvm.email = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("Email");}, gvm);
    gvm.memberSince = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("MemberSince");}, gvm);
    gvm.myProjects = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("MyProjects");}, gvm);

    // The table data observable array
    gvm.tabledata = ko.observableArray([]);

    // Add data to the table
    gvm.addTableData = function(id, name) {
        // Push data
        var tblOject = {tid: id, tname: name};
        gvm.tabledata.push(tblOject);

        $('#editbtn-' + id).bind('click', function(event, data) {
            showEditStudentListsModal(tblOject);
            event.stopPropagation();
        });

        $('#removebtn-' + id).bind('click', function(event, data){
            // Delete the table item
            deleteTableItem(id, tblOject);
            event.stopPropagation();
        });
    }

    gvm.clearTable = function() {
        gvm.tabledata.removeAll();
    }
}

function showEditStudentListsModal(object) {
        resetGeneralModal();
        setGeneralModalTitle(i18n.__("EditProjectTitle"));
        setGeneralModalBody('<form id="updateStudentLists"> \

 \
                <input type="text" class="form-control input-lg" placeholder="' + object.tname + '" " name="name" value="' + object.tname + '"> \

 \
        </form>');
        $.getJSON()

        addGeneralModalButton(i18n.__("SaveBtn"), function(){
            updateStudentsList(object.tid, $('#updateStudentLists').serialize(), function(result){
                hideModal();
            });
        });

        addGeneralModalButton(i18n.__("CancelBtn"), function(){
            hideModal();
        })

        showGeneralModal();
}

function updateStudentsList(id, name, callback) {
    $.ajax({
        url: "/api/studentlist/" + id,
        type: "PUT",
        data: name,
        success: function(data) {
            //viewModel.addTableData(data['id'], data['code'], data['name'], data['description']);
            loadTable(viewModel.userId); //TODO now it is refreshing table after updating but it redirects to pagenr 1
            callback(true);
        },
        error: function(data) {
            callback(false);
        }
    });
}

function deleteTableItem(id, tblObject){
    showYesNoModal("Bent u zeker dat u dit item wil verwijderen?", function(val){
        if(val){
            $.ajax({
                url: '/api/studentlist/delete/' + id,
                type: "DELETE",
                success: function() {
                    viewModel.tabledata.remove(tblObject);
                }
            });
        }
    });
}

function loadTable(id) {
    $.getJSON('/api/studentlists/' + id, function(data) {
        viewModel.clearTable();
        // Load table data
        $.each(data, function(i, item) {
            viewModel.addTableData(item.id, item.name);
        });
    });
}

function initPage() {
    // Fetch userdata
    $.getJSON('/api/currentuser', function(data) {
        viewModel.userId = data.id;
        loadTable(data.id);
    });

    $('#addStudentList').click(function(){
        showUploadModal(function(src, id){
            // Save new picture in database
            $.ajax({
               type: "POST",
               url: '/api/csv/studentlist',
               data: "fileid="+id,
               success: function(data){
                   location.reload();
                   // Do something with student list
               }
            });
        });
    })
}<|endoftext|>


<commit_before>
(function ($) {
var app = $.app;

function Storage() {}

Storage.titleIndex = {};
Storage.artistIndex = {};
Storage.nameIndex = {};

Storage.updateIndex = function (muFile) {
  if(muFile.title && muFile.title.length > 0) {
    var key = muFile.artist.toLowerCase();
    if(Storage.titleIndex[key]) {
      Storage.titleIndex[key].push(muFile.path);
    } else {
      Storage.titleIndex[key] = [muFile.path];
    }
  }

  if(muFile.artist && muFile.artist.length > 0) {
    var key = muFile.artist.toLowerCase();
    if(Storage.artistIndex[key]) {
      Storage.artistIndex[key].push(muFile.path);
    } else {
      Storage.artistIndex[key] = [muFile.path];
    }
  }

  if(muFile.name && muFile.name.length > 0) {
    var key = muFile.name.toLowerCase().replace(/\.\w+$/,'');
    if(Storage.nameIndex[key]) {
      Storage.nameIndex[key].push(muFile.path);
    } else {
      Storage.nameIndex[key] = [muFile.path];
    }
  }
}

Storage.load = function () {
  var s = window.localStorage.getItem('__title_index__')
  Storage.titleIndex = s ? JSON.parse(s) : {};
  s = window.localStorage.getItem('__artist_index__')
  Storage.artistIndex = s ? JSON.parse(s) : {};
  var s = window.localStorage.getItem('__name_index__')
  Storage.nameIndex = s ? JSON.parse(s) : {};
}

Storage.read = function (path) {
  var s = window.localStorage.getItem(path);
  return s ? JSON.parse(s) : null;
};

Storage.write = function (muFile) {
  window.localStorage.setItem(muFile.path, JSON.stringify(muFile));
  Storage.updateIndex(muFile);
};

Storage.save = function () {
  window.localStorage.setItem('__title_index__',
    JSON.stringify(Storage.titleIndex));
  window.localStorage.setItem('__artist_index__',
    JSON.stringify(Storage.artistIndex));
  window.localStorage.setItem('__name_index__',
    JSON.stringify(Storage.nameIndex));
};

Storage.search = function (keyword) {
  keyword = keyword.toLowerCase();
  var results = [];
  function searchIndex(index) {
    for (key in index) {
      if(key.indexOf(keyword) >= 0) {
        var paths = index[key];
        _(paths).each(function (path) {
          results.push(Storage.read(path));
        });
      }
    }
  }
  searchIndex(Storage.titleIndex);
  searchIndex(Storage.artistIndex);
  searchIndex(Storage.nameIndex);
  return results;
};

app.Storage = Storage;
})(jQuery);

(function ($) {
var app = $.app;

function getPath(file) {
  return file.webkitRelativePath;
}

function getName(file) {
  return file.fileName || file.name;
}

function getSubPaths(path) {
  var subpaths = [];
  while(path.indexOf('/') > 0) {
    subpaths.push(path);
    path = path.substr(path.indexOf('/')+1);
  }
  subpaths.push(path);
  return subpaths;
}

function getSize(file) {
  return file.fileSize;
}

/*
function findInDatabase(subpaths, onFound, onNotFound) {
  var count = 0;

  function find() {
    app.db.getByPath(subpaths[count], function (tx, r) {
      if(r.rows.length > 0) {
        onFound(r.rows.item(0));
      } else {
        count++;
        if(count < subpaths.length) {
          find();
        } else {
          onNotFound();
        }
      }
    });
  }

  find();
}
*/

function MuFile(file, doneCallback) {
  var thisref = this;

  this.fileObject = file;

  function findInStorage(subpaths) {
    for(var i=0, l=subpaths.length; i<l; i++) {
      var subpath = subpaths[i];
      var pMuFile = app.Storage.read(subpath);
      if(pMuFile) { return pMuFile; }
    }
  }

  this.path = getPath(file);
  this.subpaths = getSubPaths(this.path);
  this.name = getName(file);
  this.size = getSize(file);

  if(pMuFile = findInStorage(this.subpaths)) {
    thisref.title = pMuFile.title;
    thisref.artist = pMuFile.artist;
    thisref.album = pMuFile.album;
    thisref.genre = pMuFile.genre;
    doneCallback();
  } else {
    if(/\.mp3$/.test(this.path.toLowerCase())) {
      ID3v2.parseFile(file, function (tags) {
        thisref.title = tags.Title ? tags.Title : '';
        thisref.artist = tags.Artist ? tags.Artist : '';
        thisref.album = tags.Album ? tags.Album : '';
        thisref.genre = tags.Genre ? tags.Genre : '';

        app.Storage.write({
          path : thisref.path,
          name : thisref.name,
          size : thisref.size,
          title : thisref.title,
          artist : thisref.artist,
          album : thisref.album,
          genre : thisref.genre
        });
        doneCallback();
      });
    } else {
      thisref.title = thisref.name;
      thisref.artist = '';
      thisref.album = '';
      thisref.genre = '';
      doneCallback();
    }
  }
}

MuFile.prototype = {
};

app.MuFile = MuFile;
})(jQuery);

<commit_msg>Save non mp3 files to the database too<commit_after>
(function ($) {
var app = $.app;

function Storage() {}

Storage.titleIndex = {};
Storage.artistIndex = {};
Storage.nameIndex = {};

Storage.updateIndex = function (muFile) {
  if(muFile.title && muFile.title.length > 0) {
    var key = muFile.artist.toLowerCase();
    if(Storage.titleIndex[key]) {
      Storage.titleIndex[key].push(muFile.path);
    } else {
      Storage.titleIndex[key] = [muFile.path];
    }
  }

  if(muFile.artist && muFile.artist.length > 0) {
    var key = muFile.artist.toLowerCase();
    if(Storage.artistIndex[key]) {
      Storage.artistIndex[key].push(muFile.path);
    } else {
      Storage.artistIndex[key] = [muFile.path];
    }
  }

  if(muFile.name && muFile.name.length > 0) {
    var key = muFile.name.toLowerCase().replace(/\.\w+$/,'');
    if(Storage.nameIndex[key]) {
      Storage.nameIndex[key].push(muFile.path);
    } else {
      Storage.nameIndex[key] = [muFile.path];
    }
  }
}

Storage.load = function () {
  var s = window.localStorage.getItem('__title_index__')
  Storage.titleIndex = s ? JSON.parse(s) : {};
  s = window.localStorage.getItem('__artist_index__')
  Storage.artistIndex = s ? JSON.parse(s) : {};
  var s = window.localStorage.getItem('__name_index__')
  Storage.nameIndex = s ? JSON.parse(s) : {};
}

Storage.read = function (path) {
  var s = window.localStorage.getItem(path);
  return s ? JSON.parse(s) : null;
};

Storage.write = function (muFile) {
  window.localStorage.setItem(muFile.path, JSON.stringify(muFile));
  Storage.updateIndex(muFile);
};

Storage.save = function () {
  window.localStorage.setItem('__title_index__',
    JSON.stringify(Storage.titleIndex));
  window.localStorage.setItem('__artist_index__',
    JSON.stringify(Storage.artistIndex));
  window.localStorage.setItem('__name_index__',
    JSON.stringify(Storage.nameIndex));
};

Storage.search = function (keyword) {
  keyword = keyword.toLowerCase();
  var results = [];
  function searchIndex(index) {
    for (key in index) {
      if(key.indexOf(keyword) >= 0) {
        var paths = index[key];
        _(paths).each(function (path) {
          results.push(Storage.read(path));
        });
      }
    }
  }
  searchIndex(Storage.titleIndex);
  searchIndex(Storage.artistIndex);
  searchIndex(Storage.nameIndex);
  return results;
};

app.Storage = Storage;
})(jQuery);

(function ($) {
var app = $.app;

function getPath(file) {
  return file.webkitRelativePath;
}

function getName(file) {
  return file.fileName || file.name;
}

function getSubPaths(path) {
  var subpaths = [];
  while(path.indexOf('/') > 0) {
    subpaths.push(path);
    path = path.substr(path.indexOf('/')+1);
  }
  subpaths.push(path);
  return subpaths;
}

function getSize(file) {
  return file.fileSize;
}

/*
function findInDatabase(subpaths, onFound, onNotFound) {
  var count = 0;

  function find() {
    app.db.getByPath(subpaths[count], function (tx, r) {
      if(r.rows.length > 0) {
        onFound(r.rows.item(0));
      } else {
        count++;
        if(count < subpaths.length) {
          find();
        } else {
          onNotFound();
        }
      }
    });
  }

  find();
}
*/

function MuFile(file, doneCallback) {
  var thisref = this;

  this.fileObject = file;

  function findInStorage(subpaths) {
    for(var i=0, l=subpaths.length; i<l; i++) {
      var subpath = subpaths[i];
      var pMuFile = app.Storage.read(subpath);
      if(pMuFile) { return pMuFile; }
    }
  }

  this.path = getPath(file);
  this.subpaths = getSubPaths(this.path);
  this.name = getName(file);
  this.size = getSize(file);

  if(pMuFile = findInStorage(this.subpaths)) {
    thisref.title = pMuFile.title;
    thisref.artist = pMuFile.artist;
    thisref.album = pMuFile.album;
    thisref.genre = pMuFile.genre;
    doneCallback();
  } else {
    if(/\.mp3$/.test(this.path.toLowerCase())) {
      ID3v2.parseFile(file, function (tags) {
        thisref.title = tags.Title ? tags.Title : '';
        thisref.artist = tags.Artist ? tags.Artist : '';
        thisref.album = tags.Album ? tags.Album : '';
        thisref.genre = tags.Genre ? tags.Genre : '';

        app.Storage.write({
          path : thisref.path,
          name : thisref.name,
          size : thisref.size,
          title : thisref.title,
          artist : thisref.artist,
          album : thisref.album,
          genre : thisref.genre
        });
        doneCallback();
      });
    } else {
      thisref.title = thisref.name;
      thisref.artist = '';
      thisref.album = '';
      thisref.genre = '';

      app.Storage.write({
        path : thisref.path,
        name : thisref.name,
        size : thisref.size,
        title : thisref.title,
        artist : thisref.artist,
        album : thisref.album,
        genre : thisref.genre
      });
      doneCallback();
    }
  }
}

MuFile.prototype = {
};

app.MuFile = MuFile;
})(jQuery);

<|endoftext|>


<commit_before>var gzip = require('zlib').gzip;
var url = require('url');
var testSuffix = /\.[a-zA-Z0-9]+$/;

// the index client
var client;

// index document http headers
var resHeaders = {
    'Cache-Control': 'public, max-age=31536000',
    'Vary': 'Accept-Encoding',
    'Content-Type': 'text/html; charset=utf-8',
    'Content-Encoding': 'gzip',
    'Server': 'JCES'
};

/**
 * Send the index document or the requeted public file.
 *
 * @public
 * @param {array} The url paht splitted into an array.
 * @param {object} The http request object.
 * @param {object} The http respone object.
 */
exports.public = function (pathname, req, res) {
    var self = this;

    // load client, if path has no suffix
    if (!testSuffix.test(pathname)) {
        return sendClient(res, req._sidInvalid);
    }

    engine.file.get(engine.paths.app_public, pathname, function (err, file) {

        if (err) {
            res.writeHead(404, {'Content-Type': 'text/plain; charset=utf-8'});
            res.end('File "' + pathname + '" not found.');
            return;
        }

        res.writeHead(200, file.http);
        res.end(file.data);
    });
};

/**
 * Send the index document to the client.
 *
 * @private
 * @param {object} The http response object.
 * @param {boolean} Remove session if true.
 */
function sendClient (res, removeSid) {

    // remove sid
    if (removeSid) {
        resHeaders['Set-Cookie'] = 'sid=;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT';
    } else if (resHeaders['Set-Cookie']) {
        delete resHeaders['Set-Cookie'];
    }

    if (client) {
        res.writeHead(200, resHeaders);
        return res.end(client);
    }

    // get the engine client and core modules paths with fingerprints
    // TODO combine the client files in production mode
    engine.file.addFingerprints(engine.root + 'lib/client/', ['engine.js', 'flow.js', 'utils.js',  'link.js'], function (err, files) {

        // send the error in the browser
        if (err) {
            res.writeHead(500, {'Content-Type': 'text/plain; charset=utf-8'});
            res.end(err.toString());
            return;
        }

        // load engine client in a browser
        var script = '';

        // load the core engine modules
        script += '';

        // zip and send index document
        gzip(
            '<!DOCTYPE html>' +
                '<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">' +
                '<link rel="icon" href="/favicon.ico"/>' +
                script +
            '',
            function (err, data) {

                client = data;
                resHeaders['Content-Length'] = client.length;

                resHeaders['Last-Modified'] = new Date().toString();

                res.writeHead(200, resHeaders);
                res.end(client);
            }
        );
    }, {prependPath: engine.operation_id + '/'});
}

/**
 * Get the client side module and engine scripts.
 *
 * @public
 * @param {object} The link object.
 */
exports.moduleFile = function (link) {
    var self = this;

    // the module name must be almost alphanumeric
    if (link.pathname.replace(/[^a-z0-9\/\.\-_@\|]|\.\.\//gi, '') !== link.pathname) {
        link.headers = {'content-type': 'text/plain'};
        return link.end(400, 'Incorrect data in module request URL');
    }

    // get client engine files
    if (link.path[0] === engine.operation_id) {

        // save compressed/compiled script in cache and send it to the client
        engine.file.get(
            engine.root + 'lib/client/',
            link.path[1],
            function (err, script) {

                if (err) {
                    link.headers = {'content-type': 'text/plain'};
                    link.end(404, err.toString());
                    return;
                }

                link.headers = script.http;
                link.end(200, script.data);
            },
            {prependPath: engine.operation_id + '/'}
        );

        return;
    }

    // get module composition
    var module = engine.modules.get(link.path[0]);

    // check if module exists
    if (!module || !module.clientLoad) {
        link.headers = {'content-type': 'text/plain'};
        return link.end(404, 'File not found.');
    }

    // save compressed/compiled script in cache and send it to the client
    engine.file.get(

        // parent direcotry
        engine.paths.app_modules,

        // create absolute file path
        link.path.slice(1).join('/'),

        // callback
        function (err, script) {

            if (err) {
                link.headers = {'content-type': 'text/plain'};
                return link.end(404, err.toString());
            }

            link.headers = script.http;
            link.end(200, script.data);
        },
        {
            // pass an array of allowed files
            allowedFiles: module.clientLoad,

            // prepend module path to the requested file
            prependPath: link.path[0] + '/'
        }
    );
}

/**
 * Get a HTML snippet.
 *
 * @public
 * @param {object} The link object.
 */
exports.markup = function (link) {
    var self = this;

    link.data(function (err, path) {

        // ignore errors
        if (err) {
            return;
        }

        // send html snippet to client
        engine.file.get(
            engine.paths.app_markup, path,
            function (err, snippet) {

                if (err || !snippet && !snippet.data) {
                    err = err|| 'HTML snippet not found.';
                    console.error('lib/static.js#229', err.toString());
                    return link.send(err);
                }

                link.send(err, [snippet.path, snippet.data.toString('utf8')]);
            },
            {noCompression: true}
        );
    });
}
<commit_msg>use module base as parent dir<commit_after>var gzip = require('zlib').gzip;
var url = require('url');
var testSuffix = /\.[a-zA-Z0-9]+$/;

// the index client
var client;

// index document http headers
var resHeaders = {
    'Cache-Control': 'public, max-age=31536000',
    'Vary': 'Accept-Encoding',
    'Content-Type': 'text/html; charset=utf-8',
    'Content-Encoding': 'gzip',
    'Server': 'JCES'
};

/**
 * Send the index document or the requeted public file.
 *
 * @public
 * @param {array} The url paht splitted into an array.
 * @param {object} The http request object.
 * @param {object} The http respone object.
 */
exports.public = function (pathname, req, res) {
    var self = this;

    // load client, if path has no suffix
    if (!testSuffix.test(pathname)) {
        return sendClient(res, req._sidInvalid);
    }

    engine.file.get(engine.paths.app_public, pathname.substr(1), function (err, file) {

        if (err) {
            res.writeHead(404, {'Content-Type': 'text/plain; charset=utf-8'});
            res.end('File "' + pathname + '" not found.');
            return;
        }

        res.writeHead(200, file.http);
        res.end(file.data);
    });
};

/**
 * Send the index document to the client.
 *
 * @private
 * @param {object} The http response object.
 * @param {boolean} Remove session if true.
 */
function sendClient (res, removeSid) {

    // remove sid
    if (removeSid) {
        resHeaders['Set-Cookie'] = 'sid=;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT';
    } else if (resHeaders['Set-Cookie']) {
        delete resHeaders['Set-Cookie'];
    }

    if (client) {
        res.writeHead(200, resHeaders);
        return res.end(client);
    }

    // get the engine client and core modules paths with fingerprints
    // TODO combine the client files in production mode
    engine.file.addFingerprints(engine.root + 'lib/client/', ['engine.js', 'flow.js', 'utils.js',  'link.js'], function (err, files) {

        // send the error in the browser
        if (err) {
            res.writeHead(500, {'Content-Type': 'text/plain; charset=utf-8'});
            res.end(err.toString());
            return;
        }

        // load engine client in a browser
        var script = '';

        // load the core engine modules
        script += '';

        // zip and send index document
        gzip(
            '<!DOCTYPE html>' +
                '<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">' +
                '<link rel="icon" href="/favicon.ico"/>' +
                script +
            '',
            function (err, data) {

                client = data;
                resHeaders['Content-Length'] = client.length;

                resHeaders['Last-Modified'] = new Date().toString();

                res.writeHead(200, resHeaders);
                res.end(client);
            }
        );
    }, {prependPath: engine.operation_id + '/'});
}

/**
 * Get the client side module and engine scripts.
 *
 * @public
 * @param {object} The link object.
 */
exports.moduleFile = function (link) {
    var self = this;

    // the module name must be almost alphanumeric
    if (link.pathname.replace(/[^a-z0-9\/\.\-_@\|]|\.\.\//gi, '') !== link.pathname) {
        link.headers = {'content-type': 'text/plain'};
        return link.end(400, 'Incorrect data in module request URL');
    }

    // get client engine files
    if (link.path[0] === engine.operation_id) {

        // save compressed/compiled script in cache and send it to the client
        engine.file.get(
            engine.root + 'lib/client/',
            link.path[1],
            function (err, script) {

                if (err) {
                    link.headers = {'content-type': 'text/plain'};
                    link.end(404, err.toString());
                    return;
                }

                link.headers = script.http;
                link.end(200, script.data);
            },
            {prependPath: engine.operation_id + '/'}
        );

        return;
    }

    // get module composition
    var modulePackage = engine.modules.get(link.path[0]);

    // check if module exists
    if (!modulePackage || !modulePackage.components) {
        link.headers = {'content-type': 'text/plain'};
        return link.end(404, 'File not found.');
    }

    // save compressed/compiled script in cache and send it to the client
    engine.file.get(

        // parent direcotry
        modulePackage._base,

        // create absolute file path
        link.path.slice(1).join('/'),

        // callback
        function (err, script) {

            if (err) {
                link.headers = {'content-type': 'text/plain'};
                return link.end(404, err.toString());
            }

            link.headers = script.http;
            link.end(200, script.data);
        },
        {
            // pass an array of allowed files
            allowedFiles: modulePackage.components.scripts,

            // prepend module path to the requested file
            prependPath: link.path[0] + '/'
        }
    );
}

/**
 * Get a HTML snippet.
 *
 * @public
 * @param {object} The link object.
 */
exports.markup = function (link) {
    var self = this;

    link.data(function (err, path) {

        // ignore errors
        if (err) {
            return;
        }

        // send html snippet to client
        engine.file.get(
            engine.paths.app_markup, path,
            function (err, snippet) {

                if (err || !snippet && !snippet.data) {
                    err = err|| 'HTML snippet not found.';
                    console.error('lib/static.js#229', err.toString());
                    return link.send(err);
                }

                link.send(err, [snippet.path, snippet.data.toString('utf8')]);
            },
            {noCompression: true}
        );
    });
}
<|endoftext|>


<commit_before>/*
 * Sozi - A presentation tool using the SVG standard
 *
 * Copyright (C) 2010-2014 Guillaume Savaton
 *
 * This program is dual licensed under the terms of the MIT license
 * or the GNU General Public License (GPL) version 3.
 * A copy of both licenses is provided in the doc/ folder of the
 * official release of Sozi.
 *
 * See http://sozi.baierouge.fr/wiki/en:license for details.
 */

namespace("sozi.display", function (exports) {
    "use strict";

    /*
     * The browser-specific function to request an animation frame.
     */
    var requestAnimationFrame =
            window.requestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            window.oRequestAnimationFrame;

    var perf = window.performance && window.performance.now ? window.performance : Date;

    /*
     * The default time step.
     * For browsers that do not support animation frames.
     */
    var TIME_STEP_MS = 40;

    /*
     * The handle provided by setInterval().
     * For browsers that do not support animation frames.
     */
    var timer;

    /*
     * The list of running animators.
     */
    var animatorList = [];

    /*
     * The main animation loop.
     *
     * This function is called periodically and triggers the
     * animation steps in all running animators.
     *
     * If all animators are removed from the list of running animators,
     * then the periodic calling is disabled.
     *
     * This function can be called either through requestAnimationFrame()
     * or through setInterval().
     */
    function loop() {
        if (animatorList.length > 0) {
            // If there is at least one animator,
            // and if the browser provides animation frames,
            // schedule this function to be called again in the next frame.
            if (requestAnimationFrame) {
                requestAnimationFrame(loop);
            }

            // Step all animators. We iterate over a copy of the animator list
            // in case the step() method removes an animator from the list.
            animatorList.slice().forEach(function (animator) {
                animator.step();
            });
        }
        else if (!requestAnimationFrame) {
            // If all animators have been removed,
            // and if this function is called periodically
            // by setInterval(), disable the periodic calling.
            window.clearInterval(timer);
        }
    }

    /*
     * Start the animation loop.
     *
     * This function delegates the periodic update of all animators
     * to the loop() function, either using requestAnimationFrame()
     * if the browser supports it, or using setInterval().
     */
    function start() {
        if (requestAnimationFrame) {
            requestAnimationFrame(loop);
        }
        else {
            timer = window.setInterval(loop, TIME_STEP_MS);
        }
    }

    /*
     * Add a new animator object to the list of running animators.
     *
     * If the animator list was empty before calling this function,
     * then the animation loop is started.
     */
    function addAnimator(animator) {
        if (animatorList.push(animator) === 1) {
            start();
        }
    }

    /*
     * Remove the given animator from the list of running animators.
     */
    function removeAnimator(animator) {
        animatorList.splice(animatorList.indexOf(animator), 1);
    }

    /*
     * An animator provides the logic for animating other objects.
     *
     * The main purpose of an animator is to schedule the update
     * operations in the animated objects.
     */
    exports.Animator = sozi.model.Object.create({

        init: function () {
            sozi.model.Object.init.call(this);

            // The animation duration, in milliseconds.
            this.durationMs = 500;

            // The animation timing function.
            this.timingFunction = exports.timing.linear;

            // The start time of the animation.
            this.initialTime = 0;

            // The current state of this animator.
            this.started = false;

            return this;
        },

        /*
         * Start the current animator.
         *
         * The current animator is added to the list of running animators
         * and is put in the "started" state.
         * It will be removed from the list automatically when the given duration
         * has elapsed.
         *
         * The "step" event is fired once before starting the animation.
         */
        start: function (durationMs, timingFunction) {
            this.durationMs = durationMs || 500;
            this.timingFunction = timingFunction;
            this.initialTime = perf.now();
            this.fire("step", 0);
            if (!this.started) {
                this.started = true;
                addAnimator(this);
            }
        },

        /*
         * Stop the current animator.
         *
         * The current animator is removed from the list of running animators
         * and is put in the "stopped" state.
         */
        stop: function () {
            if (this.started) {
                removeAnimator(this);
                this.started = false;
            }
        },

        /*
         * Perform one animation step.
         *
         * This function is called automatically by the loop() function.
         * It fires the "step" event with the current progress (elapsed time / duration).
         * If the animation duration has elapsed, the "done" event is fired.
         */
        step: function () {
            if (this.started) {
                var elapsedTime = perf.now() - this.initialTime;
                if (elapsedTime >= this.durationMs) {
                    this.fire("step", 1);
                    this.stop();
                    this.fire("done");
                } else {
                    this.fire("step", this.timingFunction(elapsedTime / this.durationMs));
                }
            }
        }
    });
});
<commit_msg>Safer implementation of Animator<commit_after>/*
 * Sozi - A presentation tool using the SVG standard
 *
 * Copyright (C) 2010-2014 Guillaume Savaton
 *
 * This program is dual licensed under the terms of the MIT license
 * or the GNU General Public License (GPL) version 3.
 * A copy of both licenses is provided in the doc/ folder of the
 * official release of Sozi.
 *
 * See http://sozi.baierouge.fr/wiki/en:license for details.
 */

namespace("sozi.display", function (exports) {
    "use strict";

    /*
     * The browser-specific function to request an animation frame.
     */
    var requestAnimationFrame =
            window.requestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            window.oRequestAnimationFrame;

    var perf = window.performance && window.performance.now ? window.performance : Date;

    /*
     * The default time step.
     * For browsers that do not support animation frames.
     */
    var TIME_STEP_MS = 40;

    /*
     * The handle provided by setInterval().
     * For browsers that do not support animation frames.
     */
    var timer;

    // The number of running animators.
    var runningAnimators = 0;

    /*
     * The list of managed animators.
     */
    var animatorList = [];

    /*
     * The main animation loop.
     *
     * This function is called periodically and triggers the
     * animation steps in all running animators.
     *
     * If all animators are removed from the list of running animators,
     * then the periodic calling is disabled.
     *
     * This function can be called either through requestAnimationFrame()
     * or through setInterval().
     */
    function loop() {
        if (runningAnimators > 0) {
            // If there is at least one animator,
            // and if the browser provides animation frames,
            // schedule this function to be called again in the next frame.
            if (requestAnimationFrame) {
                requestAnimationFrame(loop);
            }

            // Step all animators. We iterate over a copy of the animator list
            // in case the step() method removes an animator from the list.
            animatorList.forEach(function (animator) {
                if (animator.running) {
                    animator.step();
                }
            });
        }
        else if (!requestAnimationFrame) {
            // If all animators have been removed,
            // and if this function is called periodically
            // by setInterval(), disable the periodic calling.
            window.clearInterval(timer);
        }
    }

    /*
     * Start the animation loop.
     *
     * This function delegates the periodic update of all animators
     * to the loop() function, either using requestAnimationFrame()
     * if the browser supports it, or using setInterval().
     */
    function start() {
        if (requestAnimationFrame) {
            requestAnimationFrame(loop);
        }
        else {
            timer = window.setInterval(loop, TIME_STEP_MS);
        }
    }

    /*
     * An animator provides the logic for animating other objects.
     *
     * The main purpose of an animator is to schedule the update
     * operations in the animated objects.
     */
    exports.Animator = sozi.model.Object.create({

        init: function () {
            sozi.model.Object.init.call(this);

            animatorList.push(this);

            // The animation duration, in milliseconds.
            this.durationMs = 500;

            // The animation timing function.
            this.timingFunction = exports.timing.linear;

            // The start time of the animation.
            this.initialTime = 0;

            // The current state of this animator.
            this.running = false;

            return this;
        },

        /*
         * Start the current animator.
         *
         * The "step" event is fired once before starting the animation.
         */
        start: function (durationMs, timingFunction) {
            this.durationMs = durationMs || 500;
            this.timingFunction = timingFunction || sozi.display.timing.linear;
            this.initialTime = perf.now();
            this.fire("step", 0);
            if (!this.running) {
                this.running = true;
                runningAnimators ++;
                if (runningAnimators === 1) {
                    start();
                }
            }
        },

        /*
         * Stop the current animator.
         */
        stop: function () {
            if (this.running) {
                this.running = false;
                runningAnimators --;
            }
        },

        /*
         * Perform one animation step.
         *
         * This function is called automatically by the loop() function.
         * It fires the "step" event with the current progress (elapsed time / duration).
         * If the animation duration has elapsed, the "done" event is fired.
         */
        step: function () {
            var elapsedTime = perf.now() - this.initialTime;
            if (elapsedTime >= this.durationMs) {
                this.fire("step", 1);
                this.stop();
                this.fire("done");
            } else {
                this.fire("step", this.timingFunction(elapsedTime / this.durationMs));
            }
        }
    });
});
<|endoftext|>


<commit_before>/*global Arenite:true*/
// Collection of utility functions to handle objects.
// This is an integral part for the usage of the Namespace pattern since this provides the ability to, for example,
// retrieve the functions by namespace.
Arenite.Object = function () {

  var _navigateToBeforeLast = function (object, path) {
    if (!object) {
      return;
    }

    var split = path.split(".");
    var last = split.splice(split.length - 1, 1);

    var result = object;
    split.forEach(function (pathEl) {
      if (!result) {
        return;
      }
      if (!result[pathEl]) {
        result[pathEl] = {};
      }
      result = result[pathEl];
    });
    return {object: result, path: last};
  };

  var _getInObject = function (object, path) {
    if (!object) {
      return;
    }

    var split = path.split(".");
    var result = object;
    split.forEach(function (pathEl) {
      if (!result) {
        return;
      }
      result = result[pathEl];
    });
    return result;
  };

  var _setInObject = function (object, path, element) {
    var beforeLast = _navigateToBeforeLast(object, path);
    if (beforeLast && beforeLast.object) {
      beforeLast.object[beforeLast.path] = element;
      return beforeLast.object[beforeLast.path];
    }
  };

  var _deleteInObject = function (object, path) {
    var beforeLast = _navigateToBeforeLast(object, path);
    if (beforeLast && beforeLast.object) {
      delete beforeLast.object[beforeLast.path];
    }
  };

  var _extend = function (source, target) {
    for (var f in target) {
      if (target.hasOwnProperty(f)) {
        if (source[f] && typeof source[f] === 'object') {
          if (source[f].constructor === Array && target[f].constructor === Array) {
            source[f] = _uniq(source[f].concat(target[f]));
          } else {
            _extend(source[f], target[f]);
          }
        } else {
          source[f] = target[f];
        }
      }
    }
    return source;
  };

  var _keys = function (obj) {
    var keys = [], key;
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        keys.push(key);
      }
    }
    return keys;
  };

  var _contains = function (obj, key) {
    if (obj.length) {
      var result = false;
      obj.forEach(function (_key) {
        result = result || _key === key;
      });
      return result;
    } else {
      for (var _key in obj) {
        if (obj.hasOwnProperty(_key)) {
          if (_key === key) {
            return true;
          }
        }
      }
    }
    return false;
  };

  var _uniq = function (arr) {
    var result = [];
    arr.forEach(function (el) {
      if (result.indexOf(el) < 0) {
        result.push(el);
      }
    });
    return result;
  };

  var _merge = function (arr1, arr2, keepDups) {
    var result = [];
    arr1.forEach(function (el) {
      result.push(el);
    });
    arr2.forEach(function (el) {
      result.push(el);
    });
    return keepDups ? result : _uniq(result);
  };

  var _extract = function (obj, prop) {
    var result = [];
    for (var _key in obj) {
      if (obj.hasOwnProperty(_key)) {
        result.push(obj[_key][prop]);
      }
    }
    return result;
  };

  return {
    object: {
      //###object.get
      // Retrieves a property from an object. The property is expressed as a string, denoting a path.
      //

      // get(object, path)
      //

      //where *object* is the target object and *path* is the path of the value to be fetched.
      get: _getInObject,
      //###object.set
      // Sets a property in an object. The property is expressed as a string, denoting a path.
      //

      // set(object, path, value)
      //

      //where *object* is the target object,
      // *path* is the path of the value and *value* the value to be set at the given path.
      set: _setInObject,
      //###object.delete
      // Removes a property from an object. The property is expressed as a string, denoting a path.
      //

      // get(object, path)
      //

      //where *object* is the target object and *path* is the path of the value to be deleted.
      delete: _deleteInObject,
      //###object.extend
      // Extend merges to objects. The second object will "override" properties also existing in the first.
      //

      // extend(object, other)
      //

      //where *object* is the object to be merged and extended by *other*.
      extend: _extend,
      //###object.keys
      //Returns all the properties available to an object in the form of an array.
      //

      // keys(object)
      //

      //where *object* is the object from which the properties will be extracted.
      keys: _keys,
      //###object.contains
      // Determines if a element is present in an array or a key exists in an object:
      //

      // contains(object, key)
      //

      //where *object* is the object to test for the presence of key and *key* is the property/element to be tested.
      contains: _contains
    },
    array: {
      //###array.contains
      // Determines if a element is present in an array or a key exists in an object:
      //

      // contains(object, key)
      //

      //where *object* is the object to test for the presence of key and *key* is the property/element to be tested.
      contains: _contains,
      //###array.uniq
      // Filters an array returning a new one with the unique values.
      //

      // contains(array)
      //

      //where *array* is the array to be stripped o duplicate values
      uniq: _uniq,
      //###array.merge
      // Merges two arrays returning a new one with the unique values.
      //

      // merge(arr1, arr2)
      //

      //where *arr1* and *arr2* are the arrays to be merged
      merge: _merge,
      //###array.extract
      // Extract an array composed of a specified property of the subobjects of a given object
      //

      // extract(object, property)
      //

      //where *object* is the object whose members will be analysed *property* the property to be extracted from those members
      extract: _extract
    }
  };
};<commit_msg>Adding some helper functions for objects and arrays<commit_after>/*global Arenite:true*/
// Collection of utility functions to handle objects.
// This is an integral part for the usage of the Namespace pattern since this provides the ability to, for example,
// retrieve the functions by namespace.
Arenite.Object = function () {

  var _navigateToBeforeLast = function (object, path) {
    if (!object) {
      return;
    }

    var split = path.split(".");
    var last = split.splice(split.length - 1, 1);

    var result = object;
    split.forEach(function (pathEl) {
      if (!result) {
        return;
      }
      if (!result[pathEl]) {
        result[pathEl] = {};
      }
      result = result[pathEl];
    });
    return {object: result, path: last};
  };

  var _getInObject = function (object, path) {
    if (!object) {
      return;
    }

    var split = path.split(".");
    var result = object;
    split.forEach(function (pathEl) {
      if (!result) {
        return;
      }
      result = result[pathEl];
    });
    return result;
  };

  var _setInObject = function (object, path, element) {
    var beforeLast = _navigateToBeforeLast(object, path);
    if (beforeLast && beforeLast.object) {
      beforeLast.object[beforeLast.path] = element;
      return beforeLast.object[beforeLast.path];
    }
  };

  var _deleteInObject = function (object, path) {
    var beforeLast = _navigateToBeforeLast(object, path);
    if (beforeLast && beforeLast.object) {
      delete beforeLast.object[beforeLast.path];
    }
  };

  var _extend = function (source, target) {
    for (var f in target) {
      if (target.hasOwnProperty(f)) {
        if (source[f] && typeof source[f] === 'object') {
          if (source[f].constructor === Array && target[f].constructor === Array) {
            source[f] = _uniq(source[f].concat(target[f]));
          } else {
            _extend(source[f], target[f]);
          }
        } else {
          source[f] = target[f];
        }
      }
    }
    return source;
  };

  var _keys = function (obj) {
    var keys = [], key;
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        keys.push(key);
      }
    }
    return keys;
  };

  var _contains = function (obj, key) {
    if (obj.length) {
      var result = false;
      obj.forEach(function (_key) {
        result = result || _key === key;
      });
      return result;
    } else {
      for (var _key in obj) {
        if (obj.hasOwnProperty(_key)) {
          if (_key === key) {
            return true;
          }
        }
      }
    }
    return false;
  };

  var _uniq = function (arr) {
    var result = [];
    arr.forEach(function (el) {
      if (result.indexOf(el) < 0) {
        result.push(el);
      }
    });
    return result;
  };

  var _merge = function (arr1, arr2, keepDups) {
    var result = [];
    arr1.forEach(function (el) {
      result.push(el);
    });
    arr2.forEach(function (el) {
      result.push(el);
    });
    return keepDups ? result : _uniq(result);
  };

  var _extract = function (obj, prop) {
    var result = [];
    for (var _key in obj) {
      if (obj.hasOwnProperty(_key)) {
        result.push(obj[_key][prop]);
      }
    }
    return result;
  };

  var _obj = function (arr, key) {
    var obj = {};
    arr.forEach(function (elem) {
      obj[_getInObject(elem, key)] = elem;
    });
    return obj;
  };

  var _forEach = function (obj, func) {
    var key;
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        func(obj[key], key);
      }
    }
  };

  var _values = function (obj) {
    var key;
    var arr = [];
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        arr.push(obj[key]);
      }
    }
    return arr;
  };

  return {
    object: {
      //###object.get
      // Retrieves a property from an object. The property is expressed as a string, denoting a path.
      //

      // get(object, path)
      //

      //where *object* is the target object and *path* is the path of the value to be fetched.
      get: _getInObject,
      //###object.set
      // Sets a property in an object. The property is expressed as a string, denoting a path.
      //

      // set(object, path, value)
      //

      //where *object* is the target object,
      // *path* is the path of the value and *value* the value to be set at the given path.
      set: _setInObject,
      //###object.delete
      // Removes a property from an object. The property is expressed as a string, denoting a path.
      //

      // get(object, path)
      //

      //where *object* is the target object and *path* is the path of the value to be deleted.
      delete: _deleteInObject,
      //###object.extend
      // Extend merges to objects. The second object will "override" properties also existing in the first.
      //

      // extend(object, other)
      //

      //where *object* is the object to be merged and extended by *other*.
      extend: _extend,
      //###object.keys
      //Returns all the properties available to an object in the form of an array.
      //

      // keys(object)
      //

      //where *object* is the object from which the properties will be extracted.
      keys: _keys,
      //###object.forEach
      //Iterates through the object the equivalent to the way forEach works for arrays.
      //

      // forEach(object, func(elem, key))
      //

      //where *object* is the object to iterate. *func(elem, key)* is the function called for each element and receives the element and its key.
      forEach: _forEach,
      //###object.values
      //Returns all the values available to an object in the form of an array.
      //

      // keys(object)
      //

      //where *object* is the object from which the elements will be extracted.
      values: _values,
      //###object.contains
      // Determines if a element is present in an array or a key exists in an object:
      //

      // contains(object, key)
      //

      //where *object* is the object to test for the presence of key and *key* is the property/element to be tested.
      contains: _contains
    },
    array: {
      //###array.contains
      // Determines if a element is present in an array or a key exists in an object:
      //

      // contains(object, key)
      //

      //where *object* is the object to test for the presence of key and *key* is the property/element to be tested.
      contains: _contains,
      //###array.uniq
      // Filters an array returning a new one with the unique values.
      //

      // contains(array)
      //

      //where *array* is the array to be stripped o duplicate values
      uniq: _uniq,
      //###array.merge
      // Merges two arrays returning a new one with the unique values.
      //

      // merge(arr1, arr2)
      //

      //where *arr1* and *arr2* are the arrays to be merged
      merge: _merge,
      //###array.extract
      // Extract an array composed of a specified property of the subobjects of a given object
      //

      // extract(object, property)
      //

      //where *object* is the object whose members will be analysed *property* the property to be extracted from those members
      extract: _extract,
      //###array.obj
      // Extract an object indexed by a given key
      //

      // obj(array, property)
      //

      //where *array* is the array whose members will be analysed *property* the property of each element to be turned into the key of that element in the resulting object
      obj: _obj
    }
  };
};
<|endoftext|>


<commit_before>(function(root) {
  root.AV = root.AV || {};
  var AV = root.AV;
  var _ = AV._;

  /**
   * Contains functions to deal with Status in AVOS Cloud.
   * @name AV.Status
   * @namespace
   */
  AV.Status = function(imageUrl, message) {
    this.data = {};
    this.inboxType = 'default';
    this.query = null;
    if(imageUrl && typeof imageUrl === 'object') {
        this.data = imageUrl;
    } else {
      if(imageUrl){
        this.data.image = imageUrl;
      }
      if(message){
        this.data.message = message;
      }
    }
    return this;
  };

  AV.Status.prototype = {
    /**
     * Gets the value of an attribute in status data.
     * @param {String} attr The string name of an attribute.
     */
    get: function(attr){
      return this.data[attr];
    },
    /**
     * Sets a hash of model attributes on the status data.
     * @param {String} key The key to set.
     * @param {} value The value to give it.
     */
    set: function(key, value){
      this.data[key] = value;
      return this;
    },
    /**
     * Destroy this status,then it will not be avaiable in other user's inboxes.
     * @param {Object} options An optional Backbone-like options object with
     *     success and error callbacks that will be invoked once the iteration
     *     has finished.
     * @return {AV.Promise} A promise that is fulfilled when the destroy
     *     completes.
     */
    destroy: function(options){
      if(!this.id)
        return AV.Promise.error('The status id is not exists.')._thenRunCallbacks(options);
      var request = AV._request("statuses", null, this.id, 'DELETE');
      return request._thenRunCallbacks(options);
    },
    /**
      * Cast the AV.Status object to an AV.Object pointer.
      * @return {AV.Object} A AV.Object pointer.
      */
    toObject: function(){
      if(!this.id)
          return null;
      return AV.Object.createWithoutData('_Status', this.id);
    },
    _getDataJSON: function() {
      var json = AV._.clone(this.data);
      return AV._encode(json);
    },
   /**
    * Send  a status by a AV.Query object.
    *
For example,send a status to male users:

    *     var status = new AVStatus('image url', 'a message');
    *     status.query = new AV.Query('_User');
    *     status.query.equalTo('gender', 'male');
    *     status.send().then(function(){
    *              //send status successfully.
    *      }, function(err){
    *             //an error threw.
    *             console.dir(err);
    *      });
    *

    * @since 0.3.0
    * @param {Object} options An optional Backbone-like options object with
    *     success and error callbacks that will be invoked once the iteration
    *     has finished.
    * @return {AV.Promise} A promise that is fulfilled when the send
    *     completes.
    */
    send: function(options){
      if(!AV.User.current()){
        throw 'Please signin an user.';
      }
      if(!this.query){
        return AV.Status.sendStatusToFollowers(this, options);
      }

      var query = this.query.toJSON();
      query.className = this.query.className;
      var data = {};
      data.query = query;
      this.data = this.data || {};
      var currUser =  AV.Object.createWithoutData('_User', AV.User.current().id)._toPointer();
      this.data.source =  this.data.source || currUser;
      data.data = this._getDataJSON();
      data.inboxType = this.inboxType || 'default';

      var request = AV._request('statuses', null, null, 'POST', data);
      var self = this;
      return request.then(function(response){
        self.id = response.objectId;
        self.createdAt = AV._parseDate(response.createdAt);
        return self;
      })._thenRunCallbacks(options);
    },

    _finishFetch: function(data){
        this.id = data.objectId;
        this.createdAt = AV._parseDate(data.createdAt);
        this.updatedAt = AV._parseDate(data.updatedAt);
        this.messageId = data.messageId;
        delete data.messageId;
        delete data.objectId;
        delete data.createdAt;
        delete data.updatedAt;
        this.data = data;
    }
  };

  /**
   * Send  a status to current signined user's followers.For example:
   *

   *     var status = new AVStatus('image url', 'a message');
   *     AV.Status.sendStatusToFollowers(status).then(function(){
   *              //send status successfully.
   *      }, function(err){
   *             //an error threw.
   *             console.dir(err);
   *      });
   *

   * @since 0.3.0
   * @param {AV.Status} status  A status object to be send to followers.
   * @param {Object} options An optional Backbone-like options object with
   *     success and error callbacks that will be invoked once the iteration
   *     has finished.
   * @return {AV.Promise} A promise that is fulfilled when the send
   *     completes.
   */
  AV.Status.sendStatusToFollowers = function(status, options) {
    if(!AV.User.current()){
      throw 'Please signin an user.';
    }
    var query = {};
    query.className = '_Follower';
    query.keys = 'follower';
    var currUser =  AV.Object.createWithoutData('_User', AV.User.current().id). _toPointer();
    query.where = {user: currUser};
    var data = {};
    data.query = query;
    status.data = status.data || {};
    status.data.source =  status.data.source || currUser;
    data.data = status._getDataJSON();
    data.inboxType = status.inboxType || 'default';

    var request = AV._request('statuses', null, null, 'POST', data);
    return request.then(function(response){
      status.id = response.objectId;
      status.createdAt = AV._parseDate(response.createdAt);
      return status;
    })._thenRunCallbacks(options);
  };

  /**
   *
Send  a status from current signined user to other user's private status inbox.

   *
For example,send a private status to user '52e84e47e4b0f8de283b079b':

   *

   *    var status = new AVStatus('image url', 'a message');
   *     AV.Status.sendPrivateStatus(status, '52e84e47e4b0f8de283b079b').then(function(){
   *              //send status successfully.
   *      }, function(err){
   *             //an error threw.
   *             console.dir(err);
   *      });
   *

   * @since 0.3.0
   * @param {AV.Status} status  A status object to be send to followers.
   * @param {} target The target user or user's objectId.
   * @param {Object} options An optional Backbone-like options object with
   *     success and error callbacks that will be invoked once the iteration
   *     has finished.
   * @return {AV.Promise} A promise that is fulfilled when the send
   *     completes.
   */
  AV.Status.sendPrivateStatus = function(status, target, options) {
    if(!AV.User.current()){
      throw 'Please signin an user.';
    }
    if(!target){
          throw "Invalid target user.";
    }
    var userObjectId = _.isString(target) ? target: target.id;
    if(!userObjectId){
        throw "Invalid target user.";
    }

    var query = {};
    query.className = '_User';
    var currUser =  AV.Object.createWithoutData('_User', AV.User.current().id). _toPointer();
    query.where = {objectId: userObjectId};
    var data = {};
    data.query = query;
    status.data = status.data || {};
    status.data.source =  status.data.source || currUser;
    data.data = status._getDataJSON();
    data.inboxType = 'private';
    status.inboxType = 'private';

    var request = AV._request('statuses', null, null, 'POST', data);
    return request.then(function(response){
      status.id = response.objectId;
      status.createdAt = AV._parseDate(response.createdAt);
      return status;
    })._thenRunCallbacks(options);
  };

  /**
   * Count unread statuses in someone's inbox.For example:

   *

   *  AV.Status.countUnreadStatuses(AV.User.current()).then(function(response){
   *    console.log(response.unread); //unread statuses number.
   *    console.log(response.total);  //total statuses number.
   *  });
   *

   * @since 0.3.0
   * @param {Object} source The status source.
   * @return {AV.Query} The query object for status.
   * @return {AV.Promise} A promise that is fulfilled when the count
   *     completes.
   */
  AV.Status.countUnreadStatuses = function(owner){
    if(!AV.User.current() && owner == null){
      throw 'Please signin an user or pass the owner objectId.';
    }
    owner = owner || AV.User.current();
    var options = !_.isString(arguments[1]) ? arguments[1] : arguments[2];
    var inboxType =  !_.isString(arguments[1]) ? 'default' : arguments[1];
    var params = {};
    params.inboxType = AV._encode(inboxType);
    params.owner = AV._encode(owner);
    var request = AV._request('subscribe/statuses/count', null, null, 'GET', params);
    return request._thenRunCallbacks(options);
  };

  /**
   * Create a status query to find someone's published statuses.For example:

   *

   *   //Find current user's published statuses.
   *   var query = AV.Status.statusQuery(AV.User.current());
   *   query.find().then(function(statuses){
   *      //process statuses
   *   });
   *

   * @since 0.3.0
   * @param {Object} source The status source.
   * @return {AV.Query} The query object for status.
   */
  AV.Status.statusQuery = function(source){
    var query = new AV.Query('_Status');
    if(source){
      query.equalTo('source', source);
    }
    return query;
  };

   /**
    *
AV.InboxQuery defines a query that is used to fetch somebody's inbox statuses.

    * @see AV.Status#inboxQuery
    * @class
    */
   AV.InboxQuery = AV.Query._extend(/** @lends AV.InboxQuery.prototype */{
     _objectClass: AV.Status,
     _sinceId: 0,
     _maxId:  0,
     _inboxType: 'default',
     _owner: null,
     _newObject: function(){
      return new AV.Status();
    },
    _createRequest: function(params){
      return AV._request("subscribe/statuses", null, null, "GET",
                                   params || this.toJSON());
    },

    /**
     * Sets the messageId of results to skip before returning any results.
     * This is useful for pagination.
     * Default is zero.
     * @param {Number} n the mesage id.
     * @return {AV.InboxQuery} Returns the query, so you can chain this call.
     */
    sinceId: function(id){
      this._sinceId = id;
      return this;
    },
    /**
     * Sets the maximal messageId of results。
     * This is useful for pagination.
     * Default is zero that is no limition.
     * @param {Number} n the mesage id.
     * @return {AV.InboxQuery} Returns the query, so you can chain this call.
     */
    maxId: function(id){
      this._maxId = id;
      return this;
    },
    /**
     * Sets the owner of the querying inbox.
     * @param {Object} owner The inbox owner.
     * @return {AV.InboxQuery} Returns the query, so you can chain this call.
     */
    owner: function(owner){
      this._owner = owner;
      return this;
    },
    /**
     * Sets the querying inbox type.default is 'default'.
     * @param {Object} owner The inbox type.
     * @return {AV.InboxQuery} Returns the query, so you can chain this call.
     */
    inboxType: function(type){
      this._inboxType = type;
      return this;
    },
    toJSON: function(){
      var params = AV.InboxQuery.__super__.toJSON.call(this);
      params.owner = AV._encode(this._owner);
      params.inboxType = AV._encode(this._inboxType);
      params.sinceId = AV._encode(this._sinceId);
      params.maxId = AV._encode(this._maxId);
      return params;
    }
   });

  /**
   * Create a inbox status query to find someone's inbox statuses.For example:

   *

   *   //Find current user's default inbox statuses.
   *   var query = AV.Status.inboxQuery(AV.User.current());
   *   //find the statuses after the last message id
   *   query.sinceId(lastMessageId);
   *   query.find().then(function(statuses){
   *      //process statuses
   *   });
   *

   * @since 0.3.0
   * @param {Object} owner The inbox's owner
   * @param {String} inboxType The inbox type,'default' by default.
   * @return {AV.InboxQuery} The inbox query object.
   * @see AV.InboxQuery
   */
  AV.Status.inboxQuery = function(owner, inboxType){
    var query = new AV.InboxQuery(AV.Status);
    if(owner){
      query._owner = owner;
    }
    if(inboxType){
      query._inboxType = inboxType;
    }
    return query;
  };

}(this));
<commit_msg>Decode server data after querying statuses.<commit_after>(function(root) {
  root.AV = root.AV || {};
  var AV = root.AV;
  var _ = AV._;

  /**
   * Contains functions to deal with Status in AVOS Cloud.
   * @name AV.Status
   * @namespace
   */
  AV.Status = function(imageUrl, message) {
    this.data = {};
    this.inboxType = 'default';
    this.query = null;
    if(imageUrl && typeof imageUrl === 'object') {
        this.data = imageUrl;
    } else {
      if(imageUrl){
        this.data.image = imageUrl;
      }
      if(message){
        this.data.message = message;
      }
    }
    return this;
  };

  AV.Status.prototype = {
    /**
     * Gets the value of an attribute in status data.
     * @param {String} attr The string name of an attribute.
     */
    get: function(attr){
      return this.data[attr];
    },
    /**
     * Sets a hash of model attributes on the status data.
     * @param {String} key The key to set.
     * @param {} value The value to give it.
     */
    set: function(key, value){
      this.data[key] = value;
      return this;
    },
    /**
     * Destroy this status,then it will not be avaiable in other user's inboxes.
     * @param {Object} options An optional Backbone-like options object with
     *     success and error callbacks that will be invoked once the iteration
     *     has finished.
     * @return {AV.Promise} A promise that is fulfilled when the destroy
     *     completes.
     */
    destroy: function(options){
      if(!this.id)
        return AV.Promise.error('The status id is not exists.')._thenRunCallbacks(options);
      var request = AV._request("statuses", null, this.id, 'DELETE');
      return request._thenRunCallbacks(options);
    },
    /**
      * Cast the AV.Status object to an AV.Object pointer.
      * @return {AV.Object} A AV.Object pointer.
      */
    toObject: function(){
      if(!this.id)
          return null;
      return AV.Object.createWithoutData('_Status', this.id);
    },
    _getDataJSON: function() {
      var json = AV._.clone(this.data);
      return AV._encode(json);
    },
   /**
    * Send  a status by a AV.Query object.
    *
For example,send a status to male users:

    *     var status = new AVStatus('image url', 'a message');
    *     status.query = new AV.Query('_User');
    *     status.query.equalTo('gender', 'male');
    *     status.send().then(function(){
    *              //send status successfully.
    *      }, function(err){
    *             //an error threw.
    *             console.dir(err);
    *      });
    *

    * @since 0.3.0
    * @param {Object} options An optional Backbone-like options object with
    *     success and error callbacks that will be invoked once the iteration
    *     has finished.
    * @return {AV.Promise} A promise that is fulfilled when the send
    *     completes.
    */
    send: function(options){
      if(!AV.User.current()){
        throw 'Please signin an user.';
      }
      if(!this.query){
        return AV.Status.sendStatusToFollowers(this, options);
      }

      var query = this.query.toJSON();
      query.className = this.query.className;
      var data = {};
      data.query = query;
      this.data = this.data || {};
      var currUser =  AV.Object.createWithoutData('_User', AV.User.current().id)._toPointer();
      this.data.source =  this.data.source || currUser;
      data.data = this._getDataJSON();
      data.inboxType = this.inboxType || 'default';

      var request = AV._request('statuses', null, null, 'POST', data);
      var self = this;
      return request.then(function(response){
        self.id = response.objectId;
        self.createdAt = AV._parseDate(response.createdAt);
        return self;
      })._thenRunCallbacks(options);
    },

    _finishFetch: function(serverData){
        this.id = serverData.objectId;
        this.createdAt = AV._parseDate(serverData.createdAt);
        this.updatedAt = AV._parseDate(serverData.updatedAt);
        this.messageId = serverData.messageId;
        delete serverData.messageId;
        delete serverData.objectId;
        delete serverData.createdAt;
        delete serverData.updatedAt;
        this.data = AV._decode(serverData);
    }
  };

  /**
   * Send  a status to current signined user's followers.For example:
   *

   *     var status = new AVStatus('image url', 'a message');
   *     AV.Status.sendStatusToFollowers(status).then(function(){
   *              //send status successfully.
   *      }, function(err){
   *             //an error threw.
   *             console.dir(err);
   *      });
   *

   * @since 0.3.0
   * @param {AV.Status} status  A status object to be send to followers.
   * @param {Object} options An optional Backbone-like options object with
   *     success and error callbacks that will be invoked once the iteration
   *     has finished.
   * @return {AV.Promise} A promise that is fulfilled when the send
   *     completes.
   */
  AV.Status.sendStatusToFollowers = function(status, options) {
    if(!AV.User.current()){
      throw 'Please signin an user.';
    }
    var query = {};
    query.className = '_Follower';
    query.keys = 'follower';
    var currUser =  AV.Object.createWithoutData('_User', AV.User.current().id). _toPointer();
    query.where = {user: currUser};
    var data = {};
    data.query = query;
    status.data = status.data || {};
    status.data.source =  status.data.source || currUser;
    data.data = status._getDataJSON();
    data.inboxType = status.inboxType || 'default';

    var request = AV._request('statuses', null, null, 'POST', data);
    return request.then(function(response){
      status.id = response.objectId;
      status.createdAt = AV._parseDate(response.createdAt);
      return status;
    })._thenRunCallbacks(options);
  };

  /**
   *
Send  a status from current signined user to other user's private status inbox.

   *
For example,send a private status to user '52e84e47e4b0f8de283b079b':

   *

   *    var status = new AVStatus('image url', 'a message');
   *     AV.Status.sendPrivateStatus(status, '52e84e47e4b0f8de283b079b').then(function(){
   *              //send status successfully.
   *      }, function(err){
   *             //an error threw.
   *             console.dir(err);
   *      });
   *

   * @since 0.3.0
   * @param {AV.Status} status  A status object to be send to followers.
   * @param {} target The target user or user's objectId.
   * @param {Object} options An optional Backbone-like options object with
   *     success and error callbacks that will be invoked once the iteration
   *     has finished.
   * @return {AV.Promise} A promise that is fulfilled when the send
   *     completes.
   */
  AV.Status.sendPrivateStatus = function(status, target, options) {
    if(!AV.User.current()){
      throw 'Please signin an user.';
    }
    if(!target){
          throw "Invalid target user.";
    }
    var userObjectId = _.isString(target) ? target: target.id;
    if(!userObjectId){
        throw "Invalid target user.";
    }

    var query = {};
    query.className = '_User';
    var currUser =  AV.Object.createWithoutData('_User', AV.User.current().id). _toPointer();
    query.where = {objectId: userObjectId};
    var data = {};
    data.query = query;
    status.data = status.data || {};
    status.data.source =  status.data.source || currUser;
    data.data = status._getDataJSON();
    data.inboxType = 'private';
    status.inboxType = 'private';

    var request = AV._request('statuses', null, null, 'POST', data);
    return request.then(function(response){
      status.id = response.objectId;
      status.createdAt = AV._parseDate(response.createdAt);
      return status;
    })._thenRunCallbacks(options);
  };

  /**
   * Count unread statuses in someone's inbox.For example:

   *

   *  AV.Status.countUnreadStatuses(AV.User.current()).then(function(response){
   *    console.log(response.unread); //unread statuses number.
   *    console.log(response.total);  //total statuses number.
   *  });
   *

   * @since 0.3.0
   * @param {Object} source The status source.
   * @return {AV.Query} The query object for status.
   * @return {AV.Promise} A promise that is fulfilled when the count
   *     completes.
   */
  AV.Status.countUnreadStatuses = function(owner){
    if(!AV.User.current() && owner == null){
      throw 'Please signin an user or pass the owner objectId.';
    }
    owner = owner || AV.User.current();
    var options = !_.isString(arguments[1]) ? arguments[1] : arguments[2];
    var inboxType =  !_.isString(arguments[1]) ? 'default' : arguments[1];
    var params = {};
    params.inboxType = AV._encode(inboxType);
    params.owner = AV._encode(owner);
    var request = AV._request('subscribe/statuses/count', null, null, 'GET', params);
    return request._thenRunCallbacks(options);
  };

  /**
   * Create a status query to find someone's published statuses.For example:

   *

   *   //Find current user's published statuses.
   *   var query = AV.Status.statusQuery(AV.User.current());
   *   query.find().then(function(statuses){
   *      //process statuses
   *   });
   *

   * @since 0.3.0
   * @param {Object} source The status source.
   * @return {AV.Query} The query object for status.
   */
  AV.Status.statusQuery = function(source){
    var query = new AV.Query('_Status');
    if(source){
      query.equalTo('source', source);
    }
    return query;
  };

   /**
    *
AV.InboxQuery defines a query that is used to fetch somebody's inbox statuses.

    * @see AV.Status#inboxQuery
    * @class
    */
   AV.InboxQuery = AV.Query._extend(/** @lends AV.InboxQuery.prototype */{
     _objectClass: AV.Status,
     _sinceId: 0,
     _maxId:  0,
     _inboxType: 'default',
     _owner: null,
     _newObject: function(){
      return new AV.Status();
    },
    _createRequest: function(params){
      return AV._request("subscribe/statuses", null, null, "GET",
                                   params || this.toJSON());
    },

    /**
     * Sets the messageId of results to skip before returning any results.
     * This is useful for pagination.
     * Default is zero.
     * @param {Number} n the mesage id.
     * @return {AV.InboxQuery} Returns the query, so you can chain this call.
     */
    sinceId: function(id){
      this._sinceId = id;
      return this;
    },
    /**
     * Sets the maximal messageId of results。
     * This is useful for pagination.
     * Default is zero that is no limition.
     * @param {Number} n the mesage id.
     * @return {AV.InboxQuery} Returns the query, so you can chain this call.
     */
    maxId: function(id){
      this._maxId = id;
      return this;
    },
    /**
     * Sets the owner of the querying inbox.
     * @param {Object} owner The inbox owner.
     * @return {AV.InboxQuery} Returns the query, so you can chain this call.
     */
    owner: function(owner){
      this._owner = owner;
      return this;
    },
    /**
     * Sets the querying inbox type.default is 'default'.
     * @param {Object} owner The inbox type.
     * @return {AV.InboxQuery} Returns the query, so you can chain this call.
     */
    inboxType: function(type){
      this._inboxType = type;
      return this;
    },
    toJSON: function(){
      var params = AV.InboxQuery.__super__.toJSON.call(this);
      params.owner = AV._encode(this._owner);
      params.inboxType = AV._encode(this._inboxType);
      params.sinceId = AV._encode(this._sinceId);
      params.maxId = AV._encode(this._maxId);
      return params;
    }
   });

  /**
   * Create a inbox status query to find someone's inbox statuses.For example:

   *

   *   //Find current user's default inbox statuses.
   *   var query = AV.Status.inboxQuery(AV.User.current());
   *   //find the statuses after the last message id
   *   query.sinceId(lastMessageId);
   *   query.find().then(function(statuses){
   *      //process statuses
   *   });
   *

   * @since 0.3.0
   * @param {Object} owner The inbox's owner
   * @param {String} inboxType The inbox type,'default' by default.
   * @return {AV.InboxQuery} The inbox query object.
   * @see AV.InboxQuery
   */
  AV.Status.inboxQuery = function(owner, inboxType){
    var query = new AV.InboxQuery(AV.Status);
    if(owner){
      query._owner = owner;
    }
    if(inboxType){
      query._inboxType = inboxType;
    }
    return query;
  };

}(this));
<|endoftext|>