<commit_before>var layoutMenus = true;

var adjustLinks = function(element) {
  var links = element.find('a');
  for(var index in links) {
    var el = angular.element(links[index]);
    var href = el.prop('href');
    if(href && href.indexOf('File:') != -1) {
      el.removeAttr('href');
    } else if(href && href.indexOf('mediawiki') != -1) {
      var paths = href.split('/');
      var last = paths[paths.length - 1];
      el.prop('href', '#/' + last);
    };
  }
};

angular.module('handd', ['ngRoute'])
.value('wikiUrl', 'https://wiki.hackersanddesigners.nl/mediawiki/api.php')
.filter('unsafe', function($sce) {
  return function(val) {
    return $sce.trustAsHtml(val);
  };
})

.service('Homepage', function(wikiUrl, $http) {
  var self = this;
  this.fetch = function(wikipage, callback) {
    var wikiApiUrl = wikiUrl+'?action=parse&page=' + encodeURIComponent(wikipage) + '&format=json&disableeditsection=true';
		console.log(wikiApiUrl);
    $http.get(wikiApiUrl).then(function(res) {
      var obj = res.data;
      html = {
        'title' : obj.parse.title,
        'text' : obj.parse.text['*']
      };
      var semanticApiUrl = wikiUrl + '?action=browsebysubject&subject=' + encodeURIComponent(wikipage) + '&format=json';
      $http.get(semanticApiUrl).then(function(res) {
        var data = res.data.query.data;
        for(var i = 0; i < data.length; i++) {
          var item = data[i];
          html[item.property] = item.dataitem;
        }
        console.log(JSON.stringify(html));
        callback(html);
      });
    });
  };
})

.service('Ask', function(wikiUrl, $http, $q) {
  var self = this;
  this.fetchList = function(type, callback) {
    var objs = {};
    var promises = [];
    var query = '[[Category:Events]][[Type::' + type + ']]|?NameOfEvent|?OnDate|?Venue|?Time|sort=OnDate|order=descending';
    var url = wikiUrl + '?action=ask&query=' + query + '&format=json';
    //promises.push(
    $http.get(url).then(function(res) {
      //Aucallback(res.data);
      var data = res.data;
      var obj = data.query.results;
      if(!Array.isArray(obj)) {
        for (var attr in obj) { objs[attr] = obj[attr]; }
      }
      callback(objs);
    //}));
    });
    //$q.all(promises).then(function() {
    //});
  };
  this.fetchImages = function(callback) {
    var url = wikiUrl + '?action=query&list=allimages&ailimit=100&format=json&aisort=timestamp&aidir=newer'
    $http.get(url).then(function(res) {
      var data = res.data;
      var allimages = data.query.allimages;
      var images = [];
      for(var i = 0; i < allimages.length; i++) {
        if(i % 10 == 0) {
          images.push(allimages[i]);
        }
      }
      callback(images);
    });
  };
})

.config(function($routeProvider) {
  $routeProvider
  .when('/', {
    controller:'HomepageController as homepage',
    templateUrl:'homepage.html',
    resolve: {
    }
  })
  .when('/:wikipage', {
    controller:'HomepageController as homepage',
    templateUrl:'homepage.html',
    resolve: {
    }
  })
})

.controller('HomepageController', function(Homepage, $scope, $document, $routeParams) {
    var homepage = this;
    $scope.formatDate = function(date) {
      var dateStr = date[0].item;
      return dateStr.replace(/#.*#/,'');
      //var items = dateStr.split('/');
      //return items[2].replace(/#.*#/,'') + '/' + items[1] + '/' + items[0];
    };
    $scope.formatTime = function(time) {
      var timeStr = time[0].item;
      return timeStr.replace(/#.*#/,'');
    };
    $scope.formatStr = function(str) {
      if(Array.isArray(str)) {
        str = str[0];
      }
      if(!(str instanceof String)) {
        str = str.item;
      }
      if(str.replace) {
        str = str.replace(/_/g, ' ');
        return str.replace(/#.*#/,'');
      }
      return str;
    }
    $scope.formatPeople = function(people) {
      var peopleStr = '';
      for(var i = 0; i < people.length; i++) {
        var personStr = people[i].item.replace(/#.*#/,'');
        personStr = personStr.replace(/_/g, ' ');
        peopleStr += (personStr + ((i < people.length - 1) ? ', ' : ''));
      }
      return peopleStr.replace(/#.*#/,'');
    }
    var wikipage = $routeParams.wikipage || 'Hackers_&_Designers';
    Homepage.fetch(wikipage, function(html) {
      homepage.html = html;
    });
    $scope.$watch(function () {
      adjustLinks(angular.element(document.querySelector('.content')));
    });
})

.controller('PicsController', function(Ask, $scope, $window, $document) {
  var w = window,
    d = document,
    e = d.documentElement,
    g = d.getElementsByTagName('body')[0],
    x = (d.innerWidth || e.clientWidth || g.clientWidth) - 160,
    y = d.innerHeight || w.innerHeight || e.clientHeight|| g.clientHeight;
  var _startX = 0;
  var _startY = 0;
  var _offsetX = 0;
  var _offsetY = 0;
  var _dragElement;
  var _oldZIndex = 0;
  var _zindex = 10;
  var photosInitialized = false;
  var extractNumber = function(value) {
    var n = parseInt(value);
    return n == null || isNaN(n) ? 0 : n;
  };

  var fadeOut = function() {
    document.getElementById('photos').style.display = 'none';
    document.getElementById('close').style.display = 'none';
  };

  //document.querySelector('#close').onclick = fadeOut;

  var onMouseDown = function(e) {
    if (e == null) e = $window.event;
    var target = e.target != null ? e.target : e.srcElement;

    if ((e.button == 1 && $window.event != null ||
      e.button == 0) &&
      target.className.indexOf('drag') > -1) {
      _startX = e.clientX;
      _startY = e.clientY;
      _offsetX = extractNumber(target.style.left);
      _offsetY = extractNumber(target.style.top);
      _oldZIndex = target.style.zIndex;
      target.style.zIndex = 10000;
      _dragElement = target;
      document.onmousemove = onMouseMove;
      document.body.focus();
      document.onselectstart = function () { return false; };
      target.ondragstart = function() { return false; };
      return false;
    }
  };

  var onMouseMove = function(e) {
    if (e == null) var e = window.event;
    _dragElement.style.left = (_offsetX + e.clientX - _startX) + 'px';
    _dragElement.style.top = (_offsetY + e.clientY - _startY) + 'px';
    _dragElement.style.top + ')';
  };

  var onMouseUp = function(e) {
    if (_dragElement != null) {
      _dragElement.style.zIndex = ++_zindex;
      document.onmousemove = null;
      document.onselectstart = null;
      _dragElement.ondragstart = null;
      _dragElement = null;
    }
  };

  angular.element(document.querySelector('.flipcoin')).on('click', function(e) {
    angular.element( document.querySelector( '.flip-container' ) ).addClass('flip');
    fadeOut();
  });

  angular.element(document.querySelector('.back h1')).on('click', function(e) {
    angular.element( document.querySelector( '.flip-container' ) ).removeClass('flip');
  });

  document.getElementById('photos').style.display = 'block';
  document.getElementById('close').style.display = 'block';

  /*
  Ask.fetchImages(function(objs) {
    if(!photosInitialized) {
      photosInitialized = true;
      document.onmousedown = onMouseDown;
      document.onmouseup = onMouseUp;
    }
    document.getElementById('photos').style.display = 'block';
    document.getElementById('close').style.display = 'block';

    for(var i = 0; i < objs.length; i++) {
      var photo = objs[i];
      var div = document.querySelector('#photos');
      var image = document.createElement('img');
      image.src = photo.url;
      image.className = 'drag';
      image.style.top = '' + Math.random() * (y) + 'px';
      image.style.left = '' + (Math.random() * (x) - 160) + 'px';
      div.appendChild(image);
    }
  });
  */
})

.controller('LeftNavController', function(Ask, $scope, $document) {
  $scope.title = 'MEETUPS';
  $scope.showMenu = function($event) {
    angular.element($event.currentTarget).toggleClass('showing');
    angular.element($event.currentTarget).css('z-index', 2);
    angular.element($document[0].body.querySelector('.right')).css('z-index', 1);
  };
  $scope.formatDate = function(dateObj) {
    return dateObj.fulltext;
    //var date = new Date(dateObj.fulltext);
    //return date.getDate() + '/' + (parseInt(date.getMonth()) + 1) + '/' + date.getFullYear();
  };
  Ask.fetchList('Meetup', function(objs) {
    $scope.events = objs;
  });
})

.controller('RightNavController', function(Ask, $scope, $document) {
  $scope.title = 'SUMMER ACADEMY';
  $scope.showMenu = function($event) {
    angular.element($event.currentTarget).toggleClass('showing');
    angular.element($event.currentTarget).css('z-index', 2);
    angular.element($document[0].body.querySelector('.left')).css('z-index', 1);
  };
  $scope.formatDate = function(dateObj) {
    return dateObj.fulltext;
    //var date = new Date(dateObj.fulltext);
    //return date.getDate() + '/' + (parseInt(date.getMonth()) + 1) + '/' + date.getFullYear();
  };
  Ask.fetchList('Summer Academy', function(objs) {
    $scope.events = objs;
  });
})

.directive('updateimages', function($timeout) {
  return {
    link: function(scope, element) {
      $timeout(function() {
        var images = element.find('img');
        for(var index in images) {
          var el = angular.element(images[index]);
          var wikiUrl = 'http://wiki.hackersanddesigners.nl';
          // Fix src attr - JBG
          var src = el.prop('src');
          if(src) {
            src = src.replace(/.*?:\/\//g, '');
            var str = '.*?/mediawiki';
            var re = new RegExp(str, 'g');
            src = src.replace(re, wikiUrl + '/mediawiki');
            el.attr('src', src);
          }

          // Fix srcset attr - JBG
          var srcset = el.prop('srcset');
          if(srcset) {
            srcset = srcset.replace(/.*?:\/\//g, '');
            var str = '/mediawiki';
            var re = new RegExp(str, 'g');
            srcset = srcset.replace(re, wikiUrl + '/mediawiki');
            el.attr('srcset', srcset);
          }
        }
      }, 1000);
    }
  };
})

.directive('updatelinks', function($timeout) {
  return {
    link: function(scope, element) {
      $timeout(function() {
        adjustLinks(element);
      }, 1000);
    }
  };
});
<commit_msg>fix for secure images<commit_after>var layoutMenus = true;

var adjustLinks = function(element) {
  var links = element.find('a');
  for(var index in links) {
    var el = angular.element(links[index]);
    var href = el.prop('href');
    if(href && href.indexOf('File:') != -1) {
      el.removeAttr('href');
    } else if(href && href.indexOf('mediawiki') != -1) {
      var paths = href.split('/');
      var last = paths[paths.length - 1];
      el.prop('href', '#/' + last);
    };
  }
};

angular.module('handd', ['ngRoute'])
.value('wikiUrl', 'https://wiki.hackersanddesigners.nl/mediawiki/api.php')
.filter('unsafe', function($sce) {
  return function(val) {
    return $sce.trustAsHtml(val);
  };
})

.service('Homepage', function(wikiUrl, $http) {
  var self = this;
  this.fetch = function(wikipage, callback) {
    var wikiApiUrl = wikiUrl+'?action=parse&page=' + encodeURIComponent(wikipage) + '&format=json&disableeditsection=true';
		console.log(wikiApiUrl);
    $http.get(wikiApiUrl).then(function(res) {
      var obj = res.data;
      html = {
        'title' : obj.parse.title,
        'text' : obj.parse.text['*']
      };
      var semanticApiUrl = wikiUrl + '?action=browsebysubject&subject=' + encodeURIComponent(wikipage) + '&format=json';
      $http.get(semanticApiUrl).then(function(res) {
        var data = res.data.query.data;
        for(var i = 0; i < data.length; i++) {
          var item = data[i];
          html[item.property] = item.dataitem;
        }
        console.log(JSON.stringify(html));
        callback(html);
      });
    });
  };
})

.service('Ask', function(wikiUrl, $http, $q) {
  var self = this;
  this.fetchList = function(type, callback) {
    var objs = {};
    var promises = [];
    var query = '[[Category:Events]][[Type::' + type + ']]|?NameOfEvent|?OnDate|?Venue|?Time|sort=OnDate|order=descending';
    var url = wikiUrl + '?action=ask&query=' + query + '&format=json';
    //promises.push(
    $http.get(url).then(function(res) {
      //Aucallback(res.data);
      var data = res.data;
      var obj = data.query.results;
      if(!Array.isArray(obj)) {
        for (var attr in obj) { objs[attr] = obj[attr]; }
      }
      callback(objs);
    //}));
    });
    //$q.all(promises).then(function() {
    //});
  };
  this.fetchImages = function(callback) {
    var url = wikiUrl + '?action=query&list=allimages&ailimit=100&format=json&aisort=timestamp&aidir=newer'
    $http.get(url).then(function(res) {
      var data = res.data;
      var allimages = data.query.allimages;
      var images = [];
      for(var i = 0; i < allimages.length; i++) {
        if(i % 10 == 0) {
          images.push(allimages[i]);
        }
      }
      callback(images);
    });
  };
})

.config(function($routeProvider) {
  $routeProvider
  .when('/', {
    controller:'HomepageController as homepage',
    templateUrl:'homepage.html',
    resolve: {
    }
  })
  .when('/:wikipage', {
    controller:'HomepageController as homepage',
    templateUrl:'homepage.html',
    resolve: {
    }
  })
})

.controller('HomepageController', function(Homepage, $scope, $document, $routeParams) {
    var homepage = this;
    $scope.formatDate = function(date) {
      var dateStr = date[0].item;
      return dateStr.replace(/#.*#/,'');
      //var items = dateStr.split('/');
      //return items[2].replace(/#.*#/,'') + '/' + items[1] + '/' + items[0];
    };
    $scope.formatTime = function(time) {
      var timeStr = time[0].item;
      return timeStr.replace(/#.*#/,'');
    };
    $scope.formatStr = function(str) {
      if(Array.isArray(str)) {
        str = str[0];
      }
      if(!(str instanceof String)) {
        str = str.item;
      }
      if(str.replace) {
        str = str.replace(/_/g, ' ');
        return str.replace(/#.*#/,'');
      }
      return str;
    }
    $scope.formatPeople = function(people) {
      var peopleStr = '';
      for(var i = 0; i < people.length; i++) {
        var personStr = people[i].item.replace(/#.*#/,'');
        personStr = personStr.replace(/_/g, ' ');
        peopleStr += (personStr + ((i < people.length - 1) ? ', ' : ''));
      }
      return peopleStr.replace(/#.*#/,'');
    }
    var wikipage = $routeParams.wikipage || 'Hackers_&_Designers';
    Homepage.fetch(wikipage, function(html) {
      homepage.html = html;
    });
    $scope.$watch(function () {
      adjustLinks(angular.element(document.querySelector('.content')));
    });
})

.controller('PicsController', function(Ask, $scope, $window, $document) {
  var w = window,
    d = document,
    e = d.documentElement,
    g = d.getElementsByTagName('body')[0],
    x = (d.innerWidth || e.clientWidth || g.clientWidth) - 160,
    y = d.innerHeight || w.innerHeight || e.clientHeight|| g.clientHeight;
  var _startX = 0;
  var _startY = 0;
  var _offsetX = 0;
  var _offsetY = 0;
  var _dragElement;
  var _oldZIndex = 0;
  var _zindex = 10;
  var photosInitialized = false;
  var extractNumber = function(value) {
    var n = parseInt(value);
    return n == null || isNaN(n) ? 0 : n;
  };

  var fadeOut = function() {
    document.getElementById('photos').style.display = 'none';
    document.getElementById('close').style.display = 'none';
  };

  //document.querySelector('#close').onclick = fadeOut;

  var onMouseDown = function(e) {
    if (e == null) e = $window.event;
    var target = e.target != null ? e.target : e.srcElement;

    if ((e.button == 1 && $window.event != null ||
      e.button == 0) &&
      target.className.indexOf('drag') > -1) {
      _startX = e.clientX;
      _startY = e.clientY;
      _offsetX = extractNumber(target.style.left);
      _offsetY = extractNumber(target.style.top);
      _oldZIndex = target.style.zIndex;
      target.style.zIndex = 10000;
      _dragElement = target;
      document.onmousemove = onMouseMove;
      document.body.focus();
      document.onselectstart = function () { return false; };
      target.ondragstart = function() { return false; };
      return false;
    }
  };

  var onMouseMove = function(e) {
    if (e == null) var e = window.event;
    _dragElement.style.left = (_offsetX + e.clientX - _startX) + 'px';
    _dragElement.style.top = (_offsetY + e.clientY - _startY) + 'px';
    _dragElement.style.top + ')';
  };

  var onMouseUp = function(e) {
    if (_dragElement != null) {
      _dragElement.style.zIndex = ++_zindex;
      document.onmousemove = null;
      document.onselectstart = null;
      _dragElement.ondragstart = null;
      _dragElement = null;
    }
  };

  angular.element(document.querySelector('.flipcoin')).on('click', function(e) {
    angular.element( document.querySelector( '.flip-container' ) ).addClass('flip');
    fadeOut();
  });

  angular.element(document.querySelector('.back h1')).on('click', function(e) {
    angular.element( document.querySelector( '.flip-container' ) ).removeClass('flip');
  });

  document.getElementById('photos').style.display = 'block';
  document.getElementById('close').style.display = 'block';

  /*
  Ask.fetchImages(function(objs) {
    if(!photosInitialized) {
      photosInitialized = true;
      document.onmousedown = onMouseDown;
      document.onmouseup = onMouseUp;
    }
    document.getElementById('photos').style.display = 'block';
    document.getElementById('close').style.display = 'block';

    for(var i = 0; i < objs.length; i++) {
      var photo = objs[i];
      var div = document.querySelector('#photos');
      var image = document.createElement('img');
      image.src = photo.url;
      image.className = 'drag';
      image.style.top = '' + Math.random() * (y) + 'px';
      image.style.left = '' + (Math.random() * (x) - 160) + 'px';
      div.appendChild(image);
    }
  });
  */
})

.controller('LeftNavController', function(Ask, $scope, $document) {
  $scope.title = 'MEETUPS';
  $scope.showMenu = function($event) {
    angular.element($event.currentTarget).toggleClass('showing');
    angular.element($event.currentTarget).css('z-index', 2);
    angular.element($document[0].body.querySelector('.right')).css('z-index', 1);
  };
  $scope.formatDate = function(dateObj) {
    return dateObj.fulltext;
    //var date = new Date(dateObj.fulltext);
    //return date.getDate() + '/' + (parseInt(date.getMonth()) + 1) + '/' + date.getFullYear();
  };
  Ask.fetchList('Meetup', function(objs) {
    $scope.events = objs;
  });
})

.controller('RightNavController', function(Ask, $scope, $document) {
  $scope.title = 'SUMMER ACADEMY';
  $scope.showMenu = function($event) {
    angular.element($event.currentTarget).toggleClass('showing');
    angular.element($event.currentTarget).css('z-index', 2);
    angular.element($document[0].body.querySelector('.left')).css('z-index', 1);
  };
  $scope.formatDate = function(dateObj) {
    return dateObj.fulltext;
    //var date = new Date(dateObj.fulltext);
    //return date.getDate() + '/' + (parseInt(date.getMonth()) + 1) + '/' + date.getFullYear();
  };
  Ask.fetchList('Summer Academy', function(objs) {
    $scope.events = objs;
  });
})

.directive('updateimages', function($timeout) {
  return {
    link: function(scope, element) {
      $timeout(function() {
        var images = element.find('img');
        for(var index in images) {
          var el = angular.element(images[index]);
          var wikiUrl = 'https://wiki.hackersanddesigners.nl';
          // Fix src attr - JBG
          var src = el.prop('src');
          if(src) {
            src = src.replace(/.*?:\/\//g, '');
            var str = '.*?/mediawiki';
            var re = new RegExp(str, 'g');
            src = src.replace(re, wikiUrl + '/mediawiki');
            el.attr('src', src);
          }

          // Fix srcset attr - JBG
          var srcset = el.prop('srcset');
          if(srcset) {
            srcset = srcset.replace(/.*?:\/\//g, '');
            var str = '/mediawiki';
            var re = new RegExp(str, 'g');
            srcset = srcset.replace(re, wikiUrl + '/mediawiki');
            el.attr('srcset', srcset);
          }
        }
      }, 1000);
    }
  };
})

.directive('updatelinks', function($timeout) {
  return {
    link: function(scope, element) {
      $timeout(function() {
        adjustLinks(element);
      }, 1000);
    }
  };
});
<|endoftext|>


<commit_before>// Add topic page logic
//

'use strict';

var _ = require('lodash');
var medialinks = require('nodeca.core/lib/parser/medialinks');

var editor, parseRules, postOptions, sectionHid;

// Update post options
//
function updatePostOptions() {
  var rules = {
    cleanupRules: parseRules.cleanupRules,
    smiles: {},
    medialinkProviders: []
  };

  if (!postOptions.no_mlinks) {
    rules.medialinkProviders = parseRules.medialinkProviders;
  }

  if (!postOptions.no_smiles) {
    rules.smiles = parseRules.smiles;
  }

  editor.setOptions({ parseRules: rules });
}

// Load editor
//
N.wire.before('navigate.done:' + module.apiPath, function load_editor(event, callback) {
  N.loader.loadAssets('mdedit', callback);
});

// Fetch post options
//
N.wire.before('navigate.done:' + module.apiPath, function fetch_options(event, callback) {
  N.io.rpc('forum.topic.post.options').done(function (res) {
    parseRules = res.parse_rules;
    parseRules.medialinkProviders = medialinks(parseRules.medialinks.providers, parseRules.medialinks.content, true);
    postOptions = res.post_options;
    callback();
  });
});

// Init on page load
//
N.wire.on('navigate.done:' + module.apiPath, function page_setup(data) {
  sectionHid = data.params.section_hid;

  editor = new N.MDEdit({
    editArea: '.topic-create__editor',
    previewArea: '.topic-create__preview',
    parseRules: parseRules,
    toolbarButtons: '$$ JSON.stringify(N.config.mdedit.toolbar) $$'
  });

  $('.topic-create__medialinks').prop('checked', !postOptions.no_mlinks);
  $('.topic-create__smiles').prop('checked', !postOptions.no_smiles);
  updatePostOptions();
});

// Free resources on page leave
//
N.wire.on('navigate.exit:' + module.apiPath, function page_exit() {
  editor = null;
  parseRules = null;
  postOptions = null;
});

N.wire.once('navigate.done:' + module.apiPath, function page_once() {

  // Show/hide post options
  //
  N.wire.on('forum.topic_create:options', function toggle_options() {
    $('.topic-create__options').slideToggle();
  });

  // Change convert medialinks option
  //
  N.wire.on('forum.topic_create:opt_medialinks', function opt_medialinks(event) {
    postOptions.no_mlinks = !$(event.target).prop('checked');
    updatePostOptions();
  });

  // Change convert smiles option
  //
  N.wire.on('forum.topic_create:opt_smiles', function opt_smiles(event) {
    postOptions.no_smiles = !$(event.target).prop('checked');
    updatePostOptions();
  });

  // Show/hide preview
  //
  N.wire.on('forum.topic_create:preview_toggle', function toggle_preview() {
    $('.topic-create__preview').slideToggle();
  });

  // Create button click handler
  //
  N.wire.on('forum.topic_create:save', function create_topic() {
    var data = {
      section_hid:      sectionHid,
      post_md:          editor.markdown,
      topic_title:      $('.topic-create__title').val(),
      attach_tail:      _.map(editor.attachments, function (v) { return v.media_id; }),
      option_no_mlinks: postOptions.no_mlinks,
      option_no_smiles: postOptions.no_smiles
    };

    if (data.topic_title === '' || data.post_md === '') {
      N.wire.emit('notify', t('err_required_fields_empty'));
    }

    N.io.rpc('forum.topic.create', data).done(function (res) {
      N.wire.emit('navigate.to', {
        apiPath: 'forum.topic',
        params: {
          section_hid: sectionHid,
          hid: res.topic_hid
        }
      });
    });
  });
});
<commit_msg>added missing return<commit_after>// Add topic page logic
//

'use strict';

var _ = require('lodash');
var medialinks = require('nodeca.core/lib/parser/medialinks');

var editor, parseRules, postOptions, sectionHid;

// Update post options
//
function updatePostOptions() {
  var rules = {
    cleanupRules: parseRules.cleanupRules,
    smiles: {},
    medialinkProviders: []
  };

  if (!postOptions.no_mlinks) {
    rules.medialinkProviders = parseRules.medialinkProviders;
  }

  if (!postOptions.no_smiles) {
    rules.smiles = parseRules.smiles;
  }

  editor.setOptions({ parseRules: rules });
}

// Load editor
//
N.wire.before('navigate.done:' + module.apiPath, function load_editor(event, callback) {
  N.loader.loadAssets('mdedit', callback);
});

// Fetch post options
//
N.wire.before('navigate.done:' + module.apiPath, function fetch_options(event, callback) {
  N.io.rpc('forum.topic.post.options').done(function (res) {
    parseRules = res.parse_rules;
    parseRules.medialinkProviders = medialinks(parseRules.medialinks.providers, parseRules.medialinks.content, true);
    postOptions = res.post_options;
    callback();
  });
});

// Init on page load
//
N.wire.on('navigate.done:' + module.apiPath, function page_setup(data) {
  sectionHid = data.params.section_hid;

  editor = new N.MDEdit({
    editArea: '.topic-create__editor',
    previewArea: '.topic-create__preview',
    parseRules: parseRules,
    toolbarButtons: '$$ JSON.stringify(N.config.mdedit.toolbar) $$'
  });

  $('.topic-create__medialinks').prop('checked', !postOptions.no_mlinks);
  $('.topic-create__smiles').prop('checked', !postOptions.no_smiles);
  updatePostOptions();
});

// Free resources on page leave
//
N.wire.on('navigate.exit:' + module.apiPath, function page_exit() {
  editor = null;
  parseRules = null;
  postOptions = null;
});

N.wire.once('navigate.done:' + module.apiPath, function page_once() {

  // Show/hide post options
  //
  N.wire.on('forum.topic_create:options', function toggle_options() {
    $('.topic-create__options').slideToggle();
  });

  // Change convert medialinks option
  //
  N.wire.on('forum.topic_create:opt_medialinks', function opt_medialinks(event) {
    postOptions.no_mlinks = !$(event.target).prop('checked');
    updatePostOptions();
  });

  // Change convert smiles option
  //
  N.wire.on('forum.topic_create:opt_smiles', function opt_smiles(event) {
    postOptions.no_smiles = !$(event.target).prop('checked');
    updatePostOptions();
  });

  // Show/hide preview
  //
  N.wire.on('forum.topic_create:preview_toggle', function toggle_preview() {
    $('.topic-create__preview').slideToggle();
  });

  // Create button click handler
  //
  N.wire.on('forum.topic_create:save', function create_topic() {
    var data = {
      section_hid:      sectionHid,
      post_md:          editor.markdown,
      topic_title:      $('.topic-create__title').val(),
      attach_tail:      _.map(editor.attachments, function (v) { return v.media_id; }),
      option_no_mlinks: postOptions.no_mlinks,
      option_no_smiles: postOptions.no_smiles
    };

    if (data.topic_title === '' || data.post_md === '') {
      N.wire.emit('notify', t('err_required_fields_empty'));
      return;
    }

    N.io.rpc('forum.topic.create', data).done(function (res) {
      N.wire.emit('navigate.to', {
        apiPath: 'forum.topic',
        params: {
          section_hid: sectionHid,
          hid: res.topic_hid
        }
      });
    });
  });
});
<|endoftext|>


<commit_before>var Promise_ = require("es6-promise").Promise;
var { Observable, config } = require("rx/dist/rx.lite.js");
var { fromEvent, merge } = Observable;
var { identity, isArray, map, noop } = require("./misc");
var debounce = require("./debounce");

config.useNativeEvents = true;
config.Promise = Promise_;

var observableProto = Observable.prototype;

if (__DEV__) {
  observableProto.log = function(ns, fn) {
    if (!ns) ns = "";
    return this.do(
      x  => console.log(ns, "next",  (fn || identity)(x)),
      e  => console.log(ns, "error", e),
      () => console.log(ns, "completed")
    );
  };
} else {
  observableProto.log = function() { return this; };
}

observableProto.each = function(onNext) {
  return this.subscribe(onNext, noop);
};

var simpleEquals = (a, b) => a === b;
observableProto.changes = function(keySelector) {
  return this.distinctUntilChanged(keySelector, simpleEquals);
};

observableProto.customDebounce = function(time, debounceOptions) {
  var source = this;
  return Observable.create(observer => {
    var debounced = debounce(val => observer.onNext(val), time, debounceOptions);
    var subscribe = source.subscribe(
      debounced,
      e  => observer.onError(e),
      () => observer.onCompleted()
    );
    return () => {
      debounced.dispose();
      subscribe.dispose();
    };
  });
};

module.exports = {
  on(elt, evts) {
    if (isArray(evts)) {
      return merge(map(evts, evt => fromEvent(elt, evt)));
    } else {
      return fromEvent(elt, evts);
    }
  },
  first(obs) {
    return obs.take(1);
  },
  only(x) {
    return Observable.never().startWith(x);
  },
};
<commit_msg>rx-ext: add retryWithBack and flatMapMaxConcurrent<commit_after>var Promise_ = require("es6-promise").Promise;
var { Observable, SingleAssignmentDisposable, config } = require("rx/dist/rx.lite.js");
var { fromEvent, merge } = Observable;
var { getBackedoffDelay } = require("./backoff");
var { identity, isArray, map, noop } = require("./misc");
var debounce = require("./debounce");

config.useNativeEvents = true;
config.Promise = Promise_;

var observableProto = Observable.prototype;

if (__DEV__) {
  observableProto.log = function(ns, fn) {
    if (!ns) ns = "";
    return this.do(
      x  => console.log(ns, "next",  (fn || identity)(x)),
      e  => console.log(ns, "error", e),
      () => console.log(ns, "completed")
    );
  };
} else {
  observableProto.log = function() { return this; };
}

observableProto.each = function(onNext) {
  return this.subscribe(onNext, noop);
};

var simpleEquals = (a, b) => a === b;
observableProto.changes = function(keySelector) {
  return this.distinctUntilChanged(keySelector, simpleEquals);
};

observableProto.customDebounce = function(time, debounceOptions) {
  var source = this;
  return Observable.create(observer => {
    var debounced = debounce(val => observer.onNext(val), time, debounceOptions);
    var subscribe = source.subscribe(
      debounced,
      e  => observer.onError(e),
      () => observer.onCompleted()
    );
    return () => {
      debounced.dispose();
      subscribe.dispose();
    };
  });
};

function retryWithBackoff(fn, { retryDelay, totalRetry, shouldRetry }) {
  var retryCount = 0;

  return function doRetry() {
    // do not leak arguments
    for (var i = 0, l = arguments.length, args = Array(l); i < l; i++)
      args[i] = arguments[i];

    return fn.apply(null, args).catch(err => {
      var wantRetry = !shouldRetry || shouldRetry(err);
      if (wantRetry && retryCount++ < totalRetry) {
        var fuzzedDelay = getBackedoffDelay(retryDelay, retryCount);
        return Observable.timer(fuzzedDelay).flatMap(() => doRetry.apply(null, args));
      }
      else {
        throw err;
      }
    });
  };
}

observableProto.flatMapMaxConcurrent = function(selector, maxConcurrent) {
  var source = this;
  return Observable.create(observer => {
    var max = 0;
    var active = [];
    var queue = [];

    function launchQueuedSources(m) {
      var activeCount = active.length;
      if (activeCount < m) {
        var newSources = queue.splice(0, m - activeCount);
        for (var i = 0; i < newSources.length; i++)
          handleNewSource(newSources[i]);
      }
    }

    function onNewMax(oldMax, newMax) {
      if (newMax > oldMax)
        launchQueuedSources(newMax);
    }

    function handleNewSource(src) {
      var sad = new SingleAssignmentDisposable();
      active.push(sad);
      sad.setDisposable(src.subscribe(
        (val) => observer.onNext(val),
        (err) => observer.onError(err),
        () => handleCompletion(sad)
      ));
    }

    function handleCompletion(subscription) {
      var index = active.indexOf(subscription);
      if (index >= 0)
        active.splice(index, 1);

      launchQueuedSources(max);
    }

    var maxSub = maxConcurrent
      .subscribe(
        (newMax) => {
          var oldMax = max;
          max = newMax;
          onNewMax(oldMax, newMax);
        },
        (err) => observer.onError(err)
      );

    var srcSub = source.subscribe(
      (src) => {
        try {
          src = selector(src);
        } catch(e) {
          return observer.onError(e);
        }
        queue.push(src);
        launchQueuedSources(max);
      },
      (err) => observer.onError(err),
      () => observer.onCompleted()
    );

    return () => {
      maxSub.dispose();
      srcSub.dispose();
      queue.length = 0;
      for (var i = 0; i < active.length; i++)
        active[i].dispose();
      active.length = 0;
    };
  });
};

module.exports = {
  on(elt, evts) {
    if (isArray(evts)) {
      return merge(map(evts, evt => fromEvent(elt, evt)));
    } else {
      return fromEvent(elt, evts);
    }
  },
  first(obs) {
    return obs.take(1);
  },
  only(x) {
    return Observable.never().startWith(x);
  },
  retryWithBackoff,
};
<|endoftext|>


<commit_before>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

if (!window.frappe) window.frappe = {};

function flt(v, decimals, number_format) {
	if (v == null || v == '') return 0;

	if (typeof v !== "number") {
		v = v + "";

		// strip currency symbol if exists
		if (v.indexOf(" ") != -1) {
			// using slice(1).join(" ") because space could also be a group separator
			var parts = v.split(" ");
			v = isNaN(parseFloat(parts[0])) ? parts.slice(parts.length - 1).join(" ") : v;
		}

		v = strip_number_groups(v, number_format);

		v = parseFloat(v);
		if (isNaN(v))
			v = 0;
	}

	if (decimals != null)
		return _round(v, decimals);
	return v;
}

function cint(v, def) {
	if (v === true)
		return 1;
	if (v === false)
		return 0;
	v = v + '';
	if (v !== "0") v = lstrip(v, ['0']);
	v = parseInt(v);
	if (isNaN(v)) v = def === undefined ? 0 : def;
	return v;
}

function strip_number_groups(v, number_format) {
	if (!number_format) number_format = get_number_format();
	var info = get_number_format_info(number_format);

	// strip groups (,)
	var group_regex = new RegExp(info.group_sep === "." ? "\\." : info.group_sep, "g");
	v = v.replace(group_regex, "");

	// replace decimal separator with (.)
	if (info.decimal_str !== "." && info.decimal_str !== "") {
		var decimal_regex = new RegExp(info.decimal_str, "g");
		v = v.replace(decimal_regex, ".");
	}

	return v;
}

frappe.number_format_info = {
	"#,###.##": { decimal_str: ".", group_sep: "," },
	"#.###,##": { decimal_str: ",", group_sep: "." },
	"# ###.##": { decimal_str: ".", group_sep: " " },
	"# ###,##": { decimal_str: ",", group_sep: " " },
	"#'###.##": { decimal_str: ".", group_sep: "'" },
	"#, ###.##": { decimal_str: ".", group_sep: ", " },
	"#,##,###.##": { decimal_str: ".", group_sep: "," },
	"#,###.###": { decimal_str: ".", group_sep: "," },
	"#.###": { decimal_str: "", group_sep: "." },
	"#,###": { decimal_str: "", group_sep: "," },
}

window.format_number = function (v, format, decimals) {
	if (!format) {
		format = get_number_format();
		if (decimals == null) decimals = cint(frappe.defaults.get_default("float_precision")) || 3;
	}

	var info = get_number_format_info(format);

	// Fix the decimal first, toFixed will auto fill trailing zero.
	if (decimals == null) decimals = info.precision;

	v = flt(v, decimals, format);

	if (v < 0) var is_negative = true;
	v = Math.abs(v);

	v = v.toFixed(decimals);

	var part = v.split('.');

	// get group position and parts
	var group_position = info.group_sep ? 3 : 0;

	if (group_position) {
		var integer = part[0];
		var str = '';
		var offset = integer.length % group_position;
		for (var i = integer.length; i >= 0; i--) {
			var l = replace_all(str, info.group_sep, "").length;
			if (format == "#,##,###.##" && str.indexOf(",") != -1) { // INR
				group_position = 2;
				l += 1;
			}

			str += integer.charAt(i);

			if (l && !((l + 1) % group_position) && i != 0) {
				str += info.group_sep;
			}
		}
		part[0] = str.split("").reverse().join("");
	}
	if (part[0] + "" == "") {
		part[0] = "0";
	}

	// join decimal
	part[1] = (part[1] && info.decimal_str) ? (info.decimal_str + part[1]) : "";

	// join
	return (is_negative ? "-" : "") + part[0] + part[1];
};

function format_currency(v, currency, decimals) {
	var format = get_number_format(currency);
	var symbol = get_currency_symbol(currency);
	if(decimals === undefined) {
		decimals = frappe.boot.sysdefaults.currency_precision || null;
	}

	if (symbol)
		return symbol + " " + format_number(v, format, decimals);
	else
		return format_number(v, format, decimals);
}

function get_currency_symbol(currency) {
	if (frappe.boot) {
		if (frappe.boot.sysdefaults.hide_currency_symbol == "Yes")
			return null;

		if (!currency)
			currency = frappe.boot.sysdefaults.currency;

		return frappe.model.get_value(":Currency", currency, "symbol") || currency;
	} else {
		// load in template
		return frappe.currency_symbols[currency];
	}
}

function get_number_format(currency) {
	return (frappe.boot && frappe.boot.sysdefaults && frappe.boot.sysdefaults.number_format) || "#,###.##";
}

function get_number_format_info(format) {
	var info = frappe.number_format_info[format];

	if (!info) {
		info = { decimal_str: ".", group_sep: "," };
	}

	// get the precision from the number format
	info.precision = format.split(info.decimal_str).slice(1)[0].length;

	return info;
}

function _round(num, precision) {
	var is_negative = num < 0 ? true : false;
	var d = cint(precision);
	var m = Math.pow(10, d);
	var n = +(d ? Math.abs(num) * m : Math.abs(num)).toFixed(8); // Avoid rounding errors
	var i = Math.floor(n), f = n - i;
	var r = ((!precision && f == 0.5) ? ((i % 2 == 0) ? i : i + 1) : Math.round(n));
	r = d ? r / m : r;
	return is_negative ? -r : r;

}

function roundNumber(num, precision) {
	// backward compatibility
	return _round(num, precision);
}

function precision(fieldname, doc) {
	if (cur_frm) {
		if (!doc) doc = cur_frm.doc;
		var df = frappe.meta.get_docfield(doc.doctype, fieldname, doc.parent || doc.name);
		if (!df) console.log(fieldname + ": could not find docfield in method precision()");
		return frappe.meta.get_field_precision(df, doc);
	} else {
		return frappe.boot.sysdefaults.float_precision
	}
}

function in_list(list, item) {
	if (!list) return false;
	for (var i = 0, j = list.length; i < j; i++)
		if (list[i] == item) return true;
	return false;
}

// Proxy for in_list
Object.defineProperty(window, 'inList', {
	get: function() {
		console.warn('Please use `in_list` instead of `inList`. It will be deprecated soon.');
		return in_list;
	}
});

function remainder(numerator, denominator, precision) {
	precision = cint(precision);
	var multiplier = Math.pow(10, precision);
	if (precision) {
		var _remainder = ((numerator * multiplier) % (denominator * multiplier)) / multiplier;
	} else {
		var _remainder = numerator % denominator;
	}

	return flt(_remainder, precision);
}

function round_based_on_smallest_currency_fraction(value, currency, precision) {
	var smallest_currency_fraction_value = flt(frappe.model.get_value(":Currency",
		currency, "smallest_currency_fraction_value"))

	if (smallest_currency_fraction_value) {
		var remainder_val = remainder(value, smallest_currency_fraction_value, precision);
		if (remainder_val > (smallest_currency_fraction_value / 2)) {
			value += (smallest_currency_fraction_value - remainder_val);
		} else {
			value -= remainder_val;
		}
	} else {
		value = Math.round(value);
	}
	return value;
}

Object.assign(window, {
	flt,
	cint,
	strip_number_groups,
	format_currency,
	get_currency_symbol,
	get_number_format,
	get_number_format_info,
	_round,
	roundNumber,
	precision,
	remainder,
	round_based_on_smallest_currency_fraction,
	in_list
});<commit_msg>fix: Number format (#6028)<commit_after>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

if (!window.frappe) window.frappe = {};

function flt(v, decimals, number_format) {
	if (v == null || v == '') return 0;

	if (typeof v !== "number") {
		v = v + "";

		// strip currency symbol if exists
		if (v.indexOf(" ") != -1) {
			// using slice(1).join(" ") because space could also be a group separator
			var parts = v.split(" ");
			v = isNaN(parseFloat(parts[0])) ? parts.slice(parts.length - 1).join(" ") : v;
		}

		v = strip_number_groups(v, number_format);

		v = parseFloat(v);
		if (isNaN(v))
			v = 0;
	}

	if (decimals != null)
		return _round(v, decimals);
	return v;
}

function cint(v, def) {
	if (v === true)
		return 1;
	if (v === false)
		return 0;
	v = v + '';
	if (v !== "0") v = lstrip(v, ['0']);
	v = parseInt(v);
	if (isNaN(v)) v = def === undefined ? 0 : def;
	return v;
}

function strip_number_groups(v, number_format) {
	if (!number_format) number_format = get_number_format();
	var info = get_number_format_info(number_format);

	// strip groups (,)
	var group_regex = new RegExp(info.group_sep === "." ? "\\." : info.group_sep, "g");
	v = v.replace(group_regex, "");

	// replace decimal separator with (.)
	if (info.decimal_str !== "." && info.decimal_str !== "") {
		var decimal_regex = new RegExp(info.decimal_str, "g");
		v = v.replace(decimal_regex, ".");
	}

	return v;
}

frappe.number_format_info = {
	"#,###.##": { decimal_str: ".", group_sep: "," },
	"#.###,##": { decimal_str: ",", group_sep: "." },
	"# ###.##": { decimal_str: ".", group_sep: " " },
	"# ###,##": { decimal_str: ",", group_sep: " " },
	"#'###.##": { decimal_str: ".", group_sep: "'" },
	"#, ###.##": { decimal_str: ".", group_sep: ", " },
	"#,##,###.##": { decimal_str: ".", group_sep: "," },
	"#,###.###": { decimal_str: ".", group_sep: "," },
	"#.###": { decimal_str: "", group_sep: "." },
	"#,###": { decimal_str: "", group_sep: "," },
}

window.format_number = function (v, format, decimals) {
	if (!format) {
		format = get_number_format();
		if (decimals == null) decimals = cint(frappe.defaults.get_default("float_precision")) || 3;
	}

	var info = get_number_format_info(format);

	// Fix the decimal first, toFixed will auto fill trailing zero.
	if (decimals == null) decimals = info.precision;

	v = flt(v, decimals, format);

	let is_negative = false;
	if (v < 0) is_negative = true;
	v = Math.abs(v);

	v = v.toFixed(decimals);

	var part = v.split('.');

	// get group position and parts
	var group_position = info.group_sep ? 3 : 0;

	if (group_position) {
		var integer = part[0];
		var str = '';
		var offset = integer.length % group_position;
		for (var i = integer.length; i >= 0; i--) {
			var l = replace_all(str, info.group_sep, "").length;
			if (format == "#,##,###.##" && str.indexOf(",") != -1) { // INR
				group_position = 2;
				l += 1;
			}

			str += integer.charAt(i);

			if (l && !((l + 1) % group_position) && i != 0) {
				str += info.group_sep;
			}
		}
		part[0] = str.split("").reverse().join("");
	}
	if (part[0] + "" == "") {
		part[0] = "0";
	}

	// join decimal
	part[1] = (part[1] && info.decimal_str) ? (info.decimal_str + part[1]) : "";

	// join
	return (is_negative ? "-" : "") + part[0] + part[1];
};

function format_currency(v, currency, decimals) {
	var format = get_number_format(currency);
	var symbol = get_currency_symbol(currency);
	if(decimals === undefined) {
		decimals = frappe.boot.sysdefaults.currency_precision || null;
	}

	if (symbol)
		return symbol + " " + format_number(v, format, decimals);
	else
		return format_number(v, format, decimals);
}

function get_currency_symbol(currency) {
	if (frappe.boot) {
		if (frappe.boot.sysdefaults.hide_currency_symbol == "Yes")
			return null;

		if (!currency)
			currency = frappe.boot.sysdefaults.currency;

		return frappe.model.get_value(":Currency", currency, "symbol") || currency;
	} else {
		// load in template
		return frappe.currency_symbols[currency];
	}
}

function get_number_format(currency) {
	return (frappe.boot && frappe.boot.sysdefaults && frappe.boot.sysdefaults.number_format) || "#,###.##";
}

function get_number_format_info(format) {
	var info = frappe.number_format_info[format];

	if (!info) {
		info = { decimal_str: ".", group_sep: "," };
	}

	// get the precision from the number format
	info.precision = format.split(info.decimal_str).slice(1)[0].length;

	return info;
}

function _round(num, precision) {
	var is_negative = num < 0 ? true : false;
	var d = cint(precision);
	var m = Math.pow(10, d);
	var n = +(d ? Math.abs(num) * m : Math.abs(num)).toFixed(8); // Avoid rounding errors
	var i = Math.floor(n), f = n - i;
	var r = ((!precision && f == 0.5) ? ((i % 2 == 0) ? i : i + 1) : Math.round(n));
	r = d ? r / m : r;
	return is_negative ? -r : r;

}

function roundNumber(num, precision) {
	// backward compatibility
	return _round(num, precision);
}

function precision(fieldname, doc) {
	if (cur_frm) {
		if (!doc) doc = cur_frm.doc;
		var df = frappe.meta.get_docfield(doc.doctype, fieldname, doc.parent || doc.name);
		if (!df) console.log(fieldname + ": could not find docfield in method precision()");
		return frappe.meta.get_field_precision(df, doc);
	} else {
		return frappe.boot.sysdefaults.float_precision
	}
}

function in_list(list, item) {
	if (!list) return false;
	for (var i = 0, j = list.length; i < j; i++)
		if (list[i] == item) return true;
	return false;
}

// Proxy for in_list
Object.defineProperty(window, 'inList', {
	get: function() {
		console.warn('Please use `in_list` instead of `inList`. It will be deprecated soon.');
		return in_list;
	}
});

function remainder(numerator, denominator, precision) {
	precision = cint(precision);
	var multiplier = Math.pow(10, precision);
	if (precision) {
		var _remainder = ((numerator * multiplier) % (denominator * multiplier)) / multiplier;
	} else {
		var _remainder = numerator % denominator;
	}

	return flt(_remainder, precision);
}

function round_based_on_smallest_currency_fraction(value, currency, precision) {
	var smallest_currency_fraction_value = flt(frappe.model.get_value(":Currency",
		currency, "smallest_currency_fraction_value"))

	if (smallest_currency_fraction_value) {
		var remainder_val = remainder(value, smallest_currency_fraction_value, precision);
		if (remainder_val > (smallest_currency_fraction_value / 2)) {
			value += (smallest_currency_fraction_value - remainder_val);
		} else {
			value -= remainder_val;
		}
	} else {
		value = Math.round(value);
	}
	return value;
}

Object.assign(window, {
	flt,
	cint,
	strip_number_groups,
	format_currency,
	get_currency_symbol,
	get_number_format,
	get_number_format_info,
	_round,
	roundNumber,
	precision,
	remainder,
	round_based_on_smallest_currency_fraction,
	in_list
});<|endoftext|>


<commit_before>var actualitesBehaviours = {
	resources: {
		view: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|getThread'
		},
		contrib: {
			right: 'fr-wseduc.actualites-controllers-ActualitesController|createDraft'
		},
		updateDraft: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|updateDraft'
		},
		updatePending: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|updatePending'
		},
		updatePublished: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|updatePublished'
		},
		submit: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|submit'
		},
		unsubmit: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|unsubmit'
		},
		publish: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|publish'
		},
		unpublish: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|unpublish'
		},
		editThread: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|updateThread'
		},
		deleteThread: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|deleteThread'
		},
		share: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|shareThread'
		},
		trash: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|trash'
		},
		restore: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|restore'
		},
		delete: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|delete'
		},
		comment: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|comment'
		}
	},
	workflow: {
		admin: 'fr.wseduc.actualites.controllers.ActualitesController|createThread'
	}
};

Behaviours.register('actualites', {
	behaviours: actualitesBehaviours,
	resource: function(resource){
		var rightsContainer = resource;
		if(resource.thread){
			rightsContainer = resource.thread;
		}
		if(!resource.myRights){
			resource.myRights = {};
		}

		for(var behaviour in actualitesBehaviours.resources){
			if(model.me.hasRight(rightsContainer, actualitesBehaviours.resources[behaviour]) || model.me.userId === resource.owner.userId){
				if(resource.myRights[behaviour] !== undefined){
					resource.myRights[behaviour] = resource.myRights[behaviour] && actualitesBehaviours.resources[behaviour];
				}
				else{
					resource.myRights[behaviour] = actualitesBehaviours.resources[behaviour];
				}
			}
		}
		return resource;
	},
	workflow: function(){
		var workflow = { };
		var actualitesWorkflow = actualitesBehaviours.workflow;
		for(var prop in actualitesWorkflow){
			if(model.me.hasWorkflow(actualitesWorkflow[prop])){
				workflow[prop] = true;
			}
		}

		return workflow;
	},
	resourceRights: function(){
		return ['read', 'contrib', 'publish', 'manager', 'comment'];
	},

	// Used by component "linker" to load news
	loadResources: function(callback){
		http().get('/actualites/linker/infos').done(function(data) {
			var infosArray = _.map(data, function(thread){
				var infos = _.map(thread.infos, function(info){
					// Keep news that are published and not expired
					if(info.status === 3) {
						if(info.expirationDate && (moment().unix() > moment(info.expirationDate).unix())) {
							return;
						}

						var threadIcon;
						if (typeof (thread.icon) === 'undefined' || thread.icon === '' ) {
							threadIcon = '/img/icons/glyphicons_036_file.png';
						}
						else {
							threadIcon = thread.icon + '?thumbnail=48x48';
						}

						return {
							title : info.title + ' [' + thread.title + ']',
							ownerName : info.owner.displayName,
							owner : info.owner.userId,
							icon : threadIcon,
							path : '/actualites#/view/thread/' + thread._id + '/info/' + info._id,
							id : info._id,
							thread_id : thread._id
						};
					}
				});

				return infos;
			});

			this.resources = _.compact(_.flatten(infosArray));
			this.resources = _.sortBy(this.resources, function(info) {
				return info.title;
			});

			if(typeof callback === 'function'){
				callback(this.resources);
			}
		}.bind(this));
	}

});<commit_msg>Order news by alphabetical order in linker. Fix #2499 : news with expirationDate were not displayed in linker<commit_after>var actualitesBehaviours = {
	resources: {
		view: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|getThread'
		},
		contrib: {
			right: 'fr-wseduc.actualites-controllers-ActualitesController|createDraft'
		},
		updateDraft: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|updateDraft'
		},
		updatePending: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|updatePending'
		},
		updatePublished: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|updatePublished'
		},
		submit: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|submit'
		},
		unsubmit: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|unsubmit'
		},
		publish: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|publish'
		},
		unpublish: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|unpublish'
		},
		editThread: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|updateThread'
		},
		deleteThread: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|deleteThread'
		},
		share: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|shareThread'
		},
		trash: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|trash'
		},
		restore: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|restore'
		},
		delete: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|delete'
		},
		comment: {
			right: 'fr-wseduc-actualites-controllers-ActualitesController|comment'
		}
	},
	workflow: {
		admin: 'fr.wseduc.actualites.controllers.ActualitesController|createThread'
	}
};

Behaviours.register('actualites', {
	behaviours: actualitesBehaviours,
	resource: function(resource){
		var rightsContainer = resource;
		if(resource.thread){
			rightsContainer = resource.thread;
		}
		if(!resource.myRights){
			resource.myRights = {};
		}

		for(var behaviour in actualitesBehaviours.resources){
			if(model.me.hasRight(rightsContainer, actualitesBehaviours.resources[behaviour]) || model.me.userId === resource.owner.userId){
				if(resource.myRights[behaviour] !== undefined){
					resource.myRights[behaviour] = resource.myRights[behaviour] && actualitesBehaviours.resources[behaviour];
				}
				else{
					resource.myRights[behaviour] = actualitesBehaviours.resources[behaviour];
				}
			}
		}
		return resource;
	},
	workflow: function(){
		var workflow = { };
		var actualitesWorkflow = actualitesBehaviours.workflow;
		for(var prop in actualitesWorkflow){
			if(model.me.hasWorkflow(actualitesWorkflow[prop])){
				workflow[prop] = true;
			}
		}

		return workflow;
	},
	resourceRights: function(){
		return ['read', 'contrib', 'publish', 'manager', 'comment'];
	},

	// Used by component "linker" to load news
	loadResources: function(callback){
		http().get('/actualites/linker/infos').done(function(data) {
			var infosArray = _.map(data, function(thread){
				var infos = _.map(thread.infos, function(info){
					// Keep news that are published and not expired
					if(info.status === 3) {
						if(info.expirationDate && info.expirationDate.$date &&
								moment().isAfter(moment(info.expirationDate.$date))) {
							return;
						}

						var threadIcon;
						if (typeof (thread.icon) === 'undefined' || thread.icon === '' ) {
							threadIcon = '/img/icons/glyphicons_036_file.png';
						}
						else {
							threadIcon = thread.icon + '?thumbnail=48x48';
						}

						return {
							title : info.title + ' [' + thread.title + ']',
							ownerName : info.owner.displayName,
							owner : info.owner.userId,
							icon : threadIcon,
							path : '/actualites#/view/thread/' + thread._id + '/info/' + info._id,
							id : info._id,
							thread_id : thread._id
						};
					}
				});

				return infos;
			});

			this.resources = _.compact(_.flatten(infosArray));
			this.resources = _.sortBy(this.resources, function(info) {
				return info.title.toLowerCase();
			});

			if(typeof callback === 'function'){
				callback(this.resources);
			}
		}.bind(this));
	}

});<|endoftext|>


<commit_before>"use strict";

angular.module("hikeio").
	directive("fancybox", ["$http", "$log", "$rootScope", "$timeout", "resourceCache", function($http, $log, $rootScope, $timeout, resourceCache) {
		return {
			link: function (scope, element, attrs) {
				var flickrLicenses = {
					"0": {"name":"All Rights Reserved", "url":"" },
					"1": {"name":"Attribution-NonCommercial-ShareAlike License", "short":"CC BY-NC-SA 2.0", "url":"http://creativecommons.org/licenses/by-nc-sa/2.0/"},
					"2": {"name":"Attribution-NonCommercial License", "short":"CC BY-NC 2.0", "url":"http://creativecommons.org/licenses/by-nc/2.0/"},
					"3": {"name":"Attribution-NonCommercial-NoDerivs License", "short":"CC BY-NC-ND 2.0", "url":"http://creativecommons.org/licenses/by-nc-nd/2.0/"},
					"4": {"name":"Attribution License", "short":"CC BY 2.0", "url":"http://creativecommons.org/licenses/by/2.0/"},
					"5": {"name":"Attribution-ShareAlike License", "short":"CC BY-SA 2.0", "url":"http://creativecommons.org/licenses/by-sa/2.0/"},
					"6": {"name":"Attribution-NoDerivs License", "short":"CC BY-ND 2.0", "url":"http://creativecommons.org/licenses/by-nd/2.0/"},
					"7": {"name":"No known copyright restrictions", "url":"http://www.flickr.com/commons/usage/"},
					"8": {"name":"United States Government Work", "url":"http://www.usa.gov/copyright.shtml"}
				};
				var context = {
					beforeLoad: function() {
						var fancyboxElement = this.element;
						var attributionLink = fancyboxElement.attr("data-attribution-link");
						if (!attributionLink) {
							return;
						} else if (attributionLink.indexOf("flickr.com") === -1) {
							$log.error("Unable to fetch attribution data for: " + attributionLink);
							return;
						}
						if (attributionLink.endsWith("/")) {
							attributionLink = attributionLink.slice(0, -1);
						}
						var splitAttributionLink = attributionLink.split("/");
						var photoId = splitAttributionLink[splitAttributionLink.length - 1];
						if (photoId) {
							$http({method: "GET", url: "http://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=d99915ff009f20c36657c94263e52674&photo_id=" + photoId + "&format=json&nojsoncallback=1", cache: resourceCache}).
								success(function(data, status, headers, config) {
									if (data.stat === "ok") {
										var attributionDiv = $("

");
										var authorName = data.photo.owner.realname ? data.photo.owner.realname : data.photo.owner.username;
										var nameAndTitleAnchor = $("\"" + data.photo.title._content + "\" by " + authorName + "");
										var separator = $(" • ");
										var licenseName = flickrLicenses[data.photo.license].short || flickrLicenses[data.photo.license].name;
										var licenseAnchor = $(" • " + licenseName + "");
										var modified = $(" Resized from original ");
										attributionDiv.append(nameAndTitleAnchor);
										attributionDiv.append(separator);
										attributionDiv.append(modified);
										attributionDiv.append(licenseAnchor);
										$(".fancybox-inner").append(attributionDiv);
										$timeout(function() {
											attributionDiv.css("opacity", "1");
										});
									} else {
										$log.error(data);
									}
								}).
								error(function(data, status, headers, config) {
									$log.error(config);
								});
						}
					},
					afterLoad: function() {
						$rootScope.$broadcast("fancyboxLoaded");
						var attributionLink = this.element.attr("data-attribution-link");
						if (attributionLink) {
							var anchor = $(".attribution-link").clone();
							anchor.attr("href", attributionLink);
							anchor.addClass("active");
							anchor.click(function(event) {
								event.stopPropagation();
							});
							$(".fancybox-inner").append(anchor);
						}
					},
					afterClose: function() {
						$rootScope.$broadcast("fancyboxClosed");
					},
					margin: 30,
					padding : 2,
					nextEffect : "none",
					prevEffect : "none",
					closeEffect : "none",
					closeBtn : true,
					arrows : false,
					keys : true,
					nextClick : true
				};

				scope.$on("$routeChangeStart", function () {
					$.fancybox.close();
				});
				scope.$on("fancyboxClose", function () {
					$.fancybox.close();
				});
				scope.$on("$destroy", function () {
					context.afterLoad = null;
					context.afterClose = null;
				});

				$(element).find(attrs.fancybox).fancybox(context);
			}
		};
	}]);<commit_msg>Include  in  in hopes of fixings weird iPad bug.<commit_after>"use strict";

angular.module("hikeio").
	directive("fancybox", ["$http", "$log", "$rootScope", "$timeout", "resourceCache", function($http, $log, $rootScope, $timeout, resourceCache) {
		return {
			link: function (scope, element, attrs) {
				var flickrLicenses = {
					"0": {"name":"All Rights Reserved", "url":"" },
					"1": {"name":"Attribution-NonCommercial-ShareAlike License", "short":"CC BY-NC-SA 2.0", "url":"http://creativecommons.org/licenses/by-nc-sa/2.0/"},
					"2": {"name":"Attribution-NonCommercial License", "short":"CC BY-NC 2.0", "url":"http://creativecommons.org/licenses/by-nc/2.0/"},
					"3": {"name":"Attribution-NonCommercial-NoDerivs License", "short":"CC BY-NC-ND 2.0", "url":"http://creativecommons.org/licenses/by-nc-nd/2.0/"},
					"4": {"name":"Attribution License", "short":"CC BY 2.0", "url":"http://creativecommons.org/licenses/by/2.0/"},
					"5": {"name":"Attribution-ShareAlike License", "short":"CC BY-SA 2.0", "url":"http://creativecommons.org/licenses/by-sa/2.0/"},
					"6": {"name":"Attribution-NoDerivs License", "short":"CC BY-ND 2.0", "url":"http://creativecommons.org/licenses/by-nd/2.0/"},
					"7": {"name":"No known copyright restrictions", "url":"http://www.flickr.com/commons/usage/"},
					"8": {"name":"United States Government Work", "url":"http://www.usa.gov/copyright.shtml"}
				};
				var context = {
					beforeLoad: function() {
						var fancyboxElement = this.element;
						var attributionLink = fancyboxElement.attr("data-attribution-link");
						if (!attributionLink) {
							return;
						} else if (attributionLink.indexOf("flickr.com") === -1) {
							$log.error("Unable to fetch attribution data for: " + attributionLink);
							return;
						}
						if (attributionLink.endsWith("/")) {
							attributionLink = attributionLink.slice(0, -1);
						}
						var splitAttributionLink = attributionLink.split("/");
						var photoId = splitAttributionLink[splitAttributionLink.length - 1];
						if (photoId) {
							$http({method: "GET", url: "http://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=d99915ff009f20c36657c94263e52674&photo_id=" + photoId + "&format=json&nojsoncallback=1", cache: resourceCache}).
								success(function(data, status, headers, config) {
									if (data.stat === "ok") {
										var attributionDiv = $("

");
										var authorName = data.photo.owner.realname ? data.photo.owner.realname : data.photo.owner.username;
										var nameAndTitleAnchor = $("\"" + data.photo.title._content + "\" by " + authorName + "");
										var separator = $(" • ");
										var licenseName = flickrLicenses[data.photo.license].short || flickrLicenses[data.photo.license].name;
										var licenseAnchor = $(" • " + licenseName + "");
										var modified = $(" Resized from original ");
										attributionDiv.append(nameAndTitleAnchor);
										attributionDiv.append(separator);
										attributionDiv.append(modified);
										attributionDiv.append(licenseAnchor);
										$(".fancybox-inner").append(attributionDiv);
										$timeout(function() {
											attributionDiv.css("opacity", "1");
										});
									} else {
										$log.error(data);
									}
								}).
								error(function(data, status, headers, config) {
									$log.error(config);
								});
						}
					},
					afterLoad: function() {
						$rootScope.$broadcast("fancyboxLoaded");
						var attributionLink = this.element.attr("data-attribution-link");
						if (attributionLink) {
							var anchor = $(".attribution-link").clone();
							anchor.attr("href", attributionLink);
							anchor.addClass("active");
							anchor.click(function(event) {
								event.stopPropagation();
							});
							$(".fancybox-inner").append(anchor);
						}
					},
					afterClose: function() {
						$rootScope.$broadcast("fancyboxClosed");
					},
					margin: 30,
					padding : 2,
					nextEffect : "none",
					prevEffect : "none",
					closeEffect : "none",
					closeBtn : true,
					arrows : false,
					keys : true,
					nextClick : true
				};

				scope.$on("$routeChangeStart", function () {
					$.fancybox.close();
				});
				scope.$on("fancyboxClose", function () {
					$.fancybox.close();
				});
				scope.$on("$destroy", function () {
					context.afterLoad = null;
					context.afterClose = null;
				});

				$(element).find(attrs.fancybox).fancybox(context);
			}
		};
	}]);<|endoftext|>


<commit_before>angular.module('AirMap2').controller('Airmap2Ctrl',function($scope){

  var map = null;

  var osmUrl='http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
  var osmAttrib='Map data © OpenStreetMap contributors';
  var osm = new L.TileLayer(osmUrl, {minZoom: 8, maxZoom: 18, attribution: osmAttrib});

  function update() {

    console.log("updated");

    $.ajax({
             url: "http://apps.opensheffield.org/sparql?default-graph-uri=&query=select+%3Fs+%3Fname+%3Flat+%3Flon%0D%0Awhere+%7B%0D%0A++%3Fs+a+%3Curi%3A%2F%2Fopensheffield.org%2Ftypes%23diffusionTube%3E+.%0D%0A++%3Fs+%3Chttp%3A%2F%2Fwww.w3.org%2F2000%2F01%2Frdf-schema%23label%3E+%3Fname+.%0D%0A++%3Fs+%3Chttp%3A%2F%2Fwww.w3.org%2F2003%2F01%2Fgeo%2Fwgs84_pos%23lat%3E+%3Flat+.%0D%0A++%3Fs+%3Chttp%3A%2F%2Fwww.w3.org%2F2003%2F01%2Fgeo%2Fwgs84_pos%23long%3E+%3Flon%0D%0A%7D%0D%0A&format=application%2Fsparql-results%2Bjson&timeout=0",
    }).done(function(data) {
      console.log("updated2");
      for ( var i = 0; i < data.results.bindings.length; i++ ) {
        console.log("%s %s %s %s",data.results.bindings[i].lat.value,data.results.bindings[i].lon.value,data.results.bindings[i].name.value,data.results.bindings[i].s.value);
      }
    });
  }

  update();

  map = new L.Map('map-canvas2', {
    center: new L.LatLng(53.383611, -1.466944),
    zoom: 12
    // ,layers: [grayscale, cities]
  });

  map.addLayer(osm);

});
<commit_msg>Added markers for diffusion tubes in airmap 2. problem with chrome on ubuntu, ff seems fine<commit_after>angular.module('AirMap2').controller('Airmap2Ctrl',function($scope){

  var map = null;

  var osmUrl='http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
  var osmAttrib='Map data © OpenStreetMap contributors';
  var osm = new L.TileLayer(osmUrl, {minZoom: 8, maxZoom: 18, attribution: osmAttrib});

  function update(map) {

    console.log("update");

    $.ajax({
             url: "http://apps.opensheffield.org/sparql?default-graph-uri=&query=select+%3Fs+%3Fname+%3Flat+%3Flon%0D%0Awhere+%7B%0D%0A++%3Fs+a+%3Curi%3A%2F%2Fopensheffield.org%2Ftypes%23diffusionTube%3E+.%0D%0A++%3Fs+%3Chttp%3A%2F%2Fwww.w3.org%2F2000%2F01%2Frdf-schema%23label%3E+%3Fname+.%0D%0A++%3Fs+%3Chttp%3A%2F%2Fwww.w3.org%2F2003%2F01%2Fgeo%2Fwgs84_pos%23lat%3E+%3Flat+.%0D%0A++%3Fs+%3Chttp%3A%2F%2Fwww.w3.org%2F2003%2F01%2Fgeo%2Fwgs84_pos%23long%3E+%3Flon%0D%0A%7D%0D%0A&format=application%2Fsparql-results%2Bjson&timeout=0",
    }).done(function(data) {
      console.log("update 2 "+data.results.bindings.length);
      for ( var i = 0; i < data.results.bindings.length; i++ ) {
      // for ( var i = 0; i < 100; i++ ) {
        // console.log("%i %s %s %s %s",i,data.results.bindings[i].lat.value,data.results.bindings[i].lon.value,data.results.bindings[i].name.value,data.results.bindings[i].s.value);
        // this.data.markers.push (L.marker( [data.results.bindings[i].lat.value,data.results.bindings[i].long.value])
                   // .bindPopup( lbl )
                   // .addTo(this.layer);

        var marker = L.marker( [data.results.bindings[i].lat.value,data.results.bindings[i].lon.value]).addTo(map);

      }
    });
  }

  map = new L.Map('map-canvas2', {
    center: new L.LatLng(53.383611, -1.466944),
    zoom: 12
    // ,layers: [grayscale, cities]
  });

  map.addLayer(osm);

  update(map);

});
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import Dropdown from 'fyndiq-component-dropdown'

import styles from '../styles.css'

class Tooltip extends React.Component {
  static propTypes = {
    type: PropTypes.oneOf(['black', 'white']),
    text: PropTypes.node,
    className: PropTypes.string,
    children: PropTypes.node,
    maxWidth: PropTypes.number,
    position: Dropdown.propTypes.position, // eslint-disable-line react/no-typos
  }

  static defaultProps = {
    type: 'black',
    text: null,
    children: null,
    className: '',
    position: 'bc',
    maxWidth: 190,
  }

  constructor(props) {
    super(props)

    this.state = { width: this.props.maxWidth }

    this.onDropdownOpen = this.onDropdownOpen.bind(this)
  }

  onDropdownOpen() {
    // Clone the wrapper node and hide it
    // The reason we're doing that is because of the animation
    // on the underlying Dropdown wrapper : calculating the width
    // of the wrapper when the Dropdown is just open will result
    // in a wrong width
    const cloneNode = this.wrapperElement.cloneNode(true)
    cloneNode.style.opacity = '0'

    document.body.appendChild(cloneNode)

    // Retrieve the width from the helper div
    // and apply it to the real tooltip wrapper element
    const { width } = cloneNode.children[0].getBoundingClientRect()
    this.setState({ width })

    // Remove the helper cloned node
    document.body.removeChild(cloneNode)
  }

  render() {
    const { type, text, children, position, className } = this.props

    if (!text) return children

    return (
      <Dropdown
        button={{children}}
        hoverMode
        noWrapperStyle
        position={position}
        onOpen={this.onDropdownOpen}
      >
        <div
          className={`
            ${styles.tooltip}
            ${styles[`position-${position}`]}
            ${styles[`type-${type}`]}
            ${className}
          `}
          style={{ width: this.state.width }}
          ref={e => {
            this.wrapperElement = e
          }}
        >

{text}

      </Dropdown>
    )
  }
}

export default Tooltip
<commit_msg>:bug: Fix width bug in tooltip<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import Dropdown from 'fyndiq-component-dropdown'

import styles from '../styles.css'

class Tooltip extends React.Component {
  static propTypes = {
    type: PropTypes.oneOf(['black', 'white']),
    text: PropTypes.node,
    className: PropTypes.string,
    children: PropTypes.node,
    maxWidth: PropTypes.number,
    position: Dropdown.propTypes.position, // eslint-disable-line react/no-typos
  }

  static defaultProps = {
    type: 'black',
    text: null,
    children: null,
    className: '',
    position: 'bc',
    maxWidth: 190,
  }

  constructor(props) {
    super(props)

    this.state = { width: this.props.maxWidth }

    this.recalculateWidth = this.recalculateWidth.bind(this)
  }

  componentDidUpdate(prevProps) {
    if (prevProps.text !== this.props.text) {
      this.recalculateWidth()
    }
  }

  recalculateWidth() {
    // Clone the wrapper node and hide it
    // The reason we're doing that is because of the animation
    // on the underlying Dropdown wrapper : calculating the width
    // of the wrapper when the Dropdown is just open will result
    // in a wrong width
    const cloneNode = this.wrapperElement.cloneNode(true)
    cloneNode.style.opacity = '0'
    cloneNode.style.width = 'initial'

    document.body.appendChild(cloneNode)

    // Retrieve the width from the helper div
    // and apply it to the real tooltip wrapper element
    const { width } = cloneNode.children[0].getBoundingClientRect()
    this.setState({ width })

    // Remove the helper cloned node
    document.body.removeChild(cloneNode)
  }

  render() {
    const { type, text, children, position, className } = this.props

    if (!text) return children

    return (
      <Dropdown
        button={{children}}
        hoverMode
        noWrapperStyle
        position={position}
        onOpen={this.recalculateWidth}
      >
        <div
          className={`
            ${styles.tooltip}
            ${styles[`position-${position}`]}
            ${styles[`type-${type}`]}
            ${className}
          `}
          style={{ width: this.state.width }}
          ref={e => {
            this.wrapperElement = e
          }}
        >

{text}

      </Dropdown>
    )
  }
}

export default Tooltip
<|endoftext|>


<commit_before>
function template( config, lights, texts, points, lines, surfaces ) {

  return `
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8">
<meta name=viewport content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>

     body { margin: 0px; overflow: hidden; }

</style>
</head>

<body>

<script src="https://cdn.rawgit.com/mrdoob/three.js/r90/build/three.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/r90/examples/js/controls/OrbitControls.js"></script>

<script>

var config = ${config};
var scene = new THREE.Scene();

var renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( config.clearColor, 1 );
document.body.appendChild( renderer.domElement );

var a = config.aspectRatio; // aspect multipliers
var animate = false; // config.animate;

var xMin = config.xMin, yMin = config.yMin, zMin = config.zMin;
var xMax = config.xMax, yMax = config.yMax, zMax = config.zMax;

if ( xMin === xMax ) { xMin -= 1; xMax += 1; }
if ( yMin === yMax ) { yMin -= 1; yMax += 1; }
if ( zMin === zMax ) { zMin -= 1; zMax += 1; }

// apply aspect multipliers for convenience
xMin *= a[0]; yMin *= a[1]; zMin *= a[2];
xMax *= a[0]; yMax *= a[1]; zMax *= a[2];

var xRange = xMax - xMin;
var yRange = yMax - yMin;
var zRange = zMax - zMin;
var rRange = Math.sqrt( xRange*xRange + yRange*yRange );

if ( zRange > rRange && a[2] === 1 ) {
  a[2] = rRange / zRange;
  zMin *= a[2];
  zMax *= a[2];
  zRange *= a[2];
}

var xMid = ( xMin + xMax ) / 2;
var yMid = ( yMin + yMax ) / 2;
var zMid = ( zMin + zMax ) / 2;

var box = new THREE.Geometry();
box.vertices.push( new THREE.Vector3( xMin, yMin, zMin ) );
box.vertices.push( new THREE.Vector3( xMax, yMax, zMax ) );
var boxMesh = new THREE.Line( box );
if ( config.frame ) scene.add( new THREE.BoxHelper( boxMesh, 'black' ) );

if ( config.axesLabels ) {

  var d = config.decimals; // decimals
  var offsetRatio = 0.1;
  var al = config.axesLabels;

  var offset = offsetRatio * ( yMax - yMin );
  var xm = ( xMid/a[0] ).toFixed(d);
  if ( /^-0.?0*$/.test(xm) ) xm = xm.substr(1);
  addLabel( al[0] + '=' + xm, xMid, yMax+offset, zMin );
  addLabel( ( xMin/a[0] ).toFixed(d), xMin, yMax+offset, zMin );
  addLabel( ( xMax/a[0] ).toFixed(d), xMax, yMax+offset, zMin );

  var offset = offsetRatio * ( xMax - xMin );
  var ym = ( yMid/a[1] ).toFixed(d);
  if ( /^-0.?0*$/.test(ym) ) ym = ym.substr(1);
  addLabel( al[1] + '=' + ym, xMax+offset, yMid, zMin );
  addLabel( ( yMin/a[1] ).toFixed(d), xMax+offset, yMin, zMin );
  addLabel( ( yMax/a[1] ).toFixed(d), xMax+offset, yMax, zMin );

  var offset = offsetRatio * ( yMax - yMin );
  var zm = ( zMid/a[2] ).toFixed(d);
  if ( /^-0.?0*$/.test(zm) ) zm = zm.substr(1);
  addLabel( al[2] + '=' + zm, xMax, yMin-offset, zMid );
  addLabel( ( zMin/a[2] ).toFixed(d), xMax, yMin-offset, zMin );
  addLabel( ( zMax/a[2] ).toFixed(d), xMax, yMin-offset, zMax );

}

function addLabel( text, x, y, z, color='black', fontsize=14 ) {

  var canvas = document.createElement( 'canvas' );
  canvas.width = 128;
  canvas.height = 32; // powers of two

  var context = canvas.getContext( '2d' );
  context.fillStyle = color;
  context.font = fontsize + 'px monospace';
  context.textAlign = 'center';
  context.textBaseline = 'middle';
  context.fillText( text, .5*canvas.width, .5*canvas.height );

  var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="128" height="32">'
            + '<text x="64" y="16" font-size="16" font-family="monospace">'
            + text + '</text></svg>';

  var img = new Image();
  img.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent( svg );

  var texture = new THREE.Texture( canvas );
  texture.needsUpdate = true;

  var sprite = new THREE.Sprite( new THREE.SpriteMaterial( { map: texture } ) );
  sprite.position.set( x, y, z );
  sprite.scale.set( 1, .25 ); // ratio of width to height
  scene.add( sprite );

}

if ( config.axes ) scene.add( new THREE.AxesHelper( Math.min( xMax, yMax, zMax ) ) );

var camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.up.set( 0, 0, 1 );

// default auto position, followed by rotation to viewpoint direction
camera.position.set( xMid, yMid, zMid );
var defaultOffset = new THREE.Vector3( xRange, yRange, zRange );

if ( config.viewpoint !== 'auto' ) {
  var v = config.viewpoint;
  var t = new THREE.Vector3( v[0], v[1], v[2] );
  var phi = defaultOffset.angleTo( t );
  var n = t.cross( defaultOffset ).normalize();
  defaultOffset.applyAxisAngle( n, -phi );
}

camera.position.add( defaultOffset );

var lights = ${lights};

for ( var i = 0 ; i < lights.length ; i++ ) {
  var light = new THREE.DirectionalLight( lights[i].color, 1 );
  var v = lights[i].position;
  light.position.set( a[0]*v[0], a[1]*v[1], a[2]*v[2] );
  if ( lights[i].parent === 'camera' ) {
    light.target.position.set( xMid, yMid, zMid );
    scene.add( light.target );
    camera.add( light );
  } else scene.add( light );
}
scene.add( camera );

scene.add( new THREE.AmbientLight( config.ambientLight, 1 ) );

var controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.target.set( xMid, yMid, zMid );
controls.addEventListener( 'change', function() { if ( !animate ) render(); } );

window.addEventListener( 'resize', function() {

  renderer.setSize( window.innerWidth, window.innerHeight );
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  if ( !animate ) render();

} );

var texts = ${texts};

for ( var i = 0 ; i < texts.length ; i++ ) {
  var t = texts[i];
  addLabel( t.text, t.point[0], t.point[1], t.point[2], t.options.color, t.options.fontSize );
}

var points = ${points};

for ( var i = 0 ; i < points.length ; i++ ) addPoint( points[i] );

function addPoint( p ) {

  var geometry = new THREE.Geometry();
  var v = p.point;
  geometry.vertices.push( new THREE.Vector3( a[0]*v[0], a[1]*v[1], a[2]*v[2] ) );

  var canvas = document.createElement( 'canvas' );
  canvas.width = 128;
  canvas.height = 128;

  var context = canvas.getContext( '2d' );
  context.arc( 64, 64, 64, 0, 2 * Math.PI );
  context.fillStyle = p.options.color;
  context.fill();

  var texture = new THREE.Texture( canvas );
  texture.needsUpdate = true;

  var transparent = p.options.opacity < 1 ? true : false;
  var material = new THREE.PointsMaterial( { size: p.options.size/20, map: texture,
                                             transparent: transparent, opacity: p.options.opacity,
                                             alphaTest: .1 } );

  var c = geometry.center().multiplyScalar( -1 );
  var mesh = new THREE.Points( geometry, material );
  mesh.position.set( c.x, c.y, c.z );
  scene.add( mesh );

}

var lines = ${lines};

for ( var i = 0 ; i < lines.length ; i++ ) addLine( lines[i] );

function addLine( l ) {

  var geometry = new THREE.Geometry();
  for ( var i = 0 ; i < l.points.length ; i++ ) {
    var v = l.points[i];
    geometry.vertices.push( new THREE.Vector3( a[0]*v[0], a[1]*v[1], a[2]*v[2] ) );
  }

  var transparent = l.options.opacity < 1 ? true : false;
  var material = new THREE.LineBasicMaterial( { color: l.options.color, linewidth: l.options.linewidth,
                                                transparent: transparent, opacity: l.options.opacity } );

  var c = geometry.center().multiplyScalar( -1 );
  var mesh = new THREE.Line( geometry, material );
  mesh.position.set( c.x, c.y, c.z );
  scene.add( mesh );

}

var surfaces = ${surfaces};

for ( var i = 0 ; i < surfaces.length ; i++ ) addSurface( surfaces[i] );

function addSurface( s ) {

  var geometry = new THREE.Geometry();
  for ( var i = 0 ; i < s.vertices.length ; i++ ) {
    var v = s.vertices[i];
    geometry.vertices.push( new THREE.Vector3( a[0]*v[0], a[1]*v[1], a[2]*v[2] ) );
  }
  for ( var i = 0 ; i < s.faces.length ; i++ ) {
    var f = s.faces[i];
    for ( var j = 0 ; j < f.length - 2 ; j++ )
      geometry.faces.push( new THREE.Face3( f[0], f[j+1], f[j+2] ) );
  }
  geometry.mergeVertices();

  // remove faces completely outside vertical range
  for ( var i = geometry.faces.length - 1 ; i >= 0 ; i-- ) {
    var f = geometry.faces[i];
    if ( geometry.vertices[f.a].z < zMin && geometry.vertices[f.b].z < zMin
           && geometry.vertices[f.c].z < zMin ) geometry.faces.splice( i, 1 );
    if ( geometry.vertices[f.a].z > zMax && geometry.vertices[f.b].z > zMax
           && geometry.vertices[f.c].z > zMax ) geometry.faces.splice( i, 1 );
  }

  // constrain vertices to vertical range
  for ( var i = 0 ; i < geometry.vertices.length ; i++ ) {
    if ( geometry.vertices[i].z < zMin ) geometry.vertices[i].z = zMin;
    if ( geometry.vertices[i].z > zMax ) geometry.vertices[i].z = zMax;
  }
  geometry.computeVertexNormals();

  var side = s.options.singleSide ? THREE.FrontSide : THREE.DoubleSide;
  var transparent = s.options.opacity < 1 ? true : false;
  var material = new THREE.MeshPhongMaterial( {
                               color: s.options.color, side: side,
                               transparent: transparent, opacity: s.options.opacity,
                               shininess: 20 } );

  if ( 'colors' in s.options ) {
    for ( var i = 0 ; i < geometry.vertices.length ; i++ )
      geometry.colors.push( new THREE.Color().setHSL( s.options.colors[i], 1, .5 ) );
    for ( var i = 0 ; i < geometry.faces.length ; i++ ) {
      var f = geometry.faces[i];
      f.vertexColors = [ geometry.colors[f.a], geometry.colors[f.b], geometry.colors[f.c] ];
    }
    material.vertexColors = THREE.VertexColors;
    material.color.set( 'white' ); // crucial!
  }

  if ( s.options.normalMaterial ) material = new THREE.MeshNormalMaterial( { side: THREE.DoubleSide } );

  var c = geometry.center().multiplyScalar( -1 );
  var mesh = new THREE.Mesh( geometry, material );
  mesh.position.set( c.x, c.y, c.z );
  if ( s.options.renderOrder ) mesh.renderOrder = s.options.renderOrder;
  scene.add( mesh );

}

if ( config.clippingPlane ) {

  var v = config.clippingPlane[0];
  var d = config.clippingPlane[1];
  var plane = new THREE.Plane( new THREE.Vector3(v[0],v[1],v[2]).normalize(), d );
  renderer.clippingPlanes = [ plane ];

}

var scratch = new THREE.Vector3();

function render() {

  if ( animate ) requestAnimationFrame( render );
  renderer.render( scene, camera );

  for ( var i = 0 ; i < scene.children.length ; i++ )
    if ( scene.children[i].type === 'Sprite' ) {
      var sprite = scene.children[i];
      var adjust = scratch.addVectors( sprite.position, scene.position )
                          .sub( camera.position ).length() / 5;
      sprite.scale.set( adjust, .25*adjust ); // ratio of canvas width to height
    }

}

render();
controls.update();
if ( !animate ) render();

</script>

</body>
</html>
  `;

}

<commit_msg>Check faces before create geometry<commit_after>
function template( config, lights, texts, points, lines, surfaces ) {

  return `
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8">
<meta name=viewport content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>

     body { margin: 0px; overflow: hidden; }

</style>
</head>

<body>

<script src="https://cdn.rawgit.com/mrdoob/three.js/r90/build/three.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/r90/examples/js/controls/OrbitControls.js"></script>

<script>

var config = ${config};
var scene = new THREE.Scene();

var renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( config.clearColor, 1 );
document.body.appendChild( renderer.domElement );

var a = config.aspectRatio; // aspect multipliers
var animate = false; // config.animate;

var xMin = config.xMin, yMin = config.yMin, zMin = config.zMin;
var xMax = config.xMax, yMax = config.yMax, zMax = config.zMax;

if ( xMin === xMax ) { xMin -= 1; xMax += 1; }
if ( yMin === yMax ) { yMin -= 1; yMax += 1; }
if ( zMin === zMax ) { zMin -= 1; zMax += 1; }

// apply aspect multipliers for convenience
xMin *= a[0]; yMin *= a[1]; zMin *= a[2];
xMax *= a[0]; yMax *= a[1]; zMax *= a[2];

var xRange = xMax - xMin;
var yRange = yMax - yMin;
var zRange = zMax - zMin;
var rRange = Math.sqrt( xRange*xRange + yRange*yRange );

if ( zRange > rRange && a[2] === 1 ) {
  a[2] = rRange / zRange;
  zMin *= a[2];
  zMax *= a[2];
  zRange *= a[2];
}

var xMid = ( xMin + xMax ) / 2;
var yMid = ( yMin + yMax ) / 2;
var zMid = ( zMin + zMax ) / 2;

var box = new THREE.Geometry();
box.vertices.push( new THREE.Vector3( xMin, yMin, zMin ) );
box.vertices.push( new THREE.Vector3( xMax, yMax, zMax ) );
var boxMesh = new THREE.Line( box );
if ( config.frame ) scene.add( new THREE.BoxHelper( boxMesh, 'black' ) );

if ( config.axesLabels ) {

  var d = config.decimals; // decimals
  var offsetRatio = 0.1;
  var al = config.axesLabels;

  var offset = offsetRatio * ( yMax - yMin );
  var xm = ( xMid/a[0] ).toFixed(d);
  if ( /^-0.?0*$/.test(xm) ) xm = xm.substr(1);
  addLabel( al[0] + '=' + xm, xMid, yMax+offset, zMin );
  addLabel( ( xMin/a[0] ).toFixed(d), xMin, yMax+offset, zMin );
  addLabel( ( xMax/a[0] ).toFixed(d), xMax, yMax+offset, zMin );

  var offset = offsetRatio * ( xMax - xMin );
  var ym = ( yMid/a[1] ).toFixed(d);
  if ( /^-0.?0*$/.test(ym) ) ym = ym.substr(1);
  addLabel( al[1] + '=' + ym, xMax+offset, yMid, zMin );
  addLabel( ( yMin/a[1] ).toFixed(d), xMax+offset, yMin, zMin );
  addLabel( ( yMax/a[1] ).toFixed(d), xMax+offset, yMax, zMin );

  var offset = offsetRatio * ( yMax - yMin );
  var zm = ( zMid/a[2] ).toFixed(d);
  if ( /^-0.?0*$/.test(zm) ) zm = zm.substr(1);
  addLabel( al[2] + '=' + zm, xMax, yMin-offset, zMid );
  addLabel( ( zMin/a[2] ).toFixed(d), xMax, yMin-offset, zMin );
  addLabel( ( zMax/a[2] ).toFixed(d), xMax, yMin-offset, zMax );

}

function addLabel( text, x, y, z, color='black', fontsize=14 ) {

  var canvas = document.createElement( 'canvas' );
  canvas.width = 128;
  canvas.height = 32; // powers of two

  var context = canvas.getContext( '2d' );
  context.fillStyle = color;
  context.font = fontsize + 'px monospace';
  context.textAlign = 'center';
  context.textBaseline = 'middle';
  context.fillText( text, .5*canvas.width, .5*canvas.height );

  var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="128" height="32">'
            + '<text x="64" y="16" font-size="16" font-family="monospace">'
            + text + '</text></svg>';

  var img = new Image();
  img.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent( svg );

  var texture = new THREE.Texture( canvas );
  texture.needsUpdate = true;

  var sprite = new THREE.Sprite( new THREE.SpriteMaterial( { map: texture } ) );
  sprite.position.set( x, y, z );
  sprite.scale.set( 1, .25 ); // ratio of width to height
  scene.add( sprite );

}

if ( config.axes ) scene.add( new THREE.AxesHelper( Math.min( xMax, yMax, zMax ) ) );

var camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.up.set( 0, 0, 1 );

// default auto position, followed by rotation to viewpoint direction
camera.position.set( xMid, yMid, zMid );
var defaultOffset = new THREE.Vector3( xRange, yRange, zRange );

if ( config.viewpoint !== 'auto' ) {
  var v = config.viewpoint;
  var t = new THREE.Vector3( v[0], v[1], v[2] );
  var phi = defaultOffset.angleTo( t );
  var n = t.cross( defaultOffset ).normalize();
  defaultOffset.applyAxisAngle( n, -phi );
}

camera.position.add( defaultOffset );

var lights = ${lights};

for ( var i = 0 ; i < lights.length ; i++ ) {
  var light = new THREE.DirectionalLight( lights[i].color, 1 );
  var v = lights[i].position;
  light.position.set( a[0]*v[0], a[1]*v[1], a[2]*v[2] );
  if ( lights[i].parent === 'camera' ) {
    light.target.position.set( xMid, yMid, zMid );
    scene.add( light.target );
    camera.add( light );
  } else scene.add( light );
}
scene.add( camera );

scene.add( new THREE.AmbientLight( config.ambientLight, 1 ) );

var controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.target.set( xMid, yMid, zMid );
controls.addEventListener( 'change', function() { if ( !animate ) render(); } );

window.addEventListener( 'resize', function() {

  renderer.setSize( window.innerWidth, window.innerHeight );
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  if ( !animate ) render();

} );

var texts = ${texts};

for ( var i = 0 ; i < texts.length ; i++ ) {
  var t = texts[i];
  addLabel( t.text, t.point[0], t.point[1], t.point[2], t.options.color, t.options.fontSize );
}

var points = ${points};

for ( var i = 0 ; i < points.length ; i++ ) addPoint( points[i] );

function addPoint( p ) {

  var geometry = new THREE.Geometry();
  var v = p.point;
  geometry.vertices.push( new THREE.Vector3( a[0]*v[0], a[1]*v[1], a[2]*v[2] ) );

  var canvas = document.createElement( 'canvas' );
  canvas.width = 128;
  canvas.height = 128;

  var context = canvas.getContext( '2d' );
  context.arc( 64, 64, 64, 0, 2 * Math.PI );
  context.fillStyle = p.options.color;
  context.fill();

  var texture = new THREE.Texture( canvas );
  texture.needsUpdate = true;

  var transparent = p.options.opacity < 1 ? true : false;
  var material = new THREE.PointsMaterial( { size: p.options.size/20, map: texture,
                                             transparent: transparent, opacity: p.options.opacity,
                                             alphaTest: .1 } );

  var c = geometry.center().multiplyScalar( -1 );
  var mesh = new THREE.Points( geometry, material );
  mesh.position.set( c.x, c.y, c.z );
  scene.add( mesh );

}

var lines = ${lines};

for ( var i = 0 ; i < lines.length ; i++ ) addLine( lines[i] );

function addLine( l ) {

  var geometry = new THREE.Geometry();
  for ( var i = 0 ; i < l.points.length ; i++ ) {
    var v = l.points[i];
    geometry.vertices.push( new THREE.Vector3( a[0]*v[0], a[1]*v[1], a[2]*v[2] ) );
  }

  var transparent = l.options.opacity < 1 ? true : false;
  var material = new THREE.LineBasicMaterial( { color: l.options.color, linewidth: l.options.linewidth,
                                                transparent: transparent, opacity: l.options.opacity } );

  var c = geometry.center().multiplyScalar( -1 );
  var mesh = new THREE.Line( geometry, material );
  mesh.position.set( c.x, c.y, c.z );
  scene.add( mesh );

}

var surfaces = ${surfaces};

for ( var i = 0 ; i < surfaces.length ; i++ ) addSurface( surfaces[i] );

function addSurface( s ) {

  // remove faces completely outside vertical range
  for ( var i = s.faces.length - 1 ; i >= 0 ; i-- ) {
    var f = s.faces[i];
    var check = true;
    f.forEach( index => check &= s.vertices[index][2] < zMin );
    if ( check ) s.faces.splice( i, 1 );
    var check = true;
    f.forEach( index => check &= s.vertices[index][2] > zMax );
    if ( check ) s.faces.splice( i, 1 );
  }

  // constrain vertices to vertical range
  for ( var i = 0 ; i < s.vertices.length ; i++ ) {
    if ( s.vertices[i][2] < zMin ) s.vertices[i][2] = zMin;
    if ( s.vertices[i][2] > zMax ) s.vertices[i][2] = zMax;
  }

  var geometry = new THREE.Geometry();
  for ( var i = 0 ; i < s.vertices.length ; i++ ) {
    var v = s.vertices[i];
    geometry.vertices.push( new THREE.Vector3( a[0]*v[0], a[1]*v[1], a[2]*v[2] ) );
  }
  for ( var i = 0 ; i < s.faces.length ; i++ ) {
    var f = s.faces[i];
    for ( var j = 0 ; j < f.length - 2 ; j++ )
      geometry.faces.push( new THREE.Face3( f[0], f[j+1], f[j+2] ) );
  }
  geometry.mergeVertices();

  geometry.computeVertexNormals();

  var side = s.options.singleSide ? THREE.FrontSide : THREE.DoubleSide;
  var transparent = s.options.opacity < 1 ? true : false;
  var material = new THREE.MeshPhongMaterial( {
                               color: s.options.color, side: side,
                               transparent: transparent, opacity: s.options.opacity,
                               shininess: 20 } );

  if ( 'colors' in s.options ) {
    for ( var i = 0 ; i < geometry.vertices.length ; i++ )
      geometry.colors.push( new THREE.Color().setHSL( s.options.colors[i], 1, .5 ) );
    for ( var i = 0 ; i < geometry.faces.length ; i++ ) {
      var f = geometry.faces[i];
      f.vertexColors = [ geometry.colors[f.a], geometry.colors[f.b], geometry.colors[f.c] ];
    }
    material.vertexColors = THREE.VertexColors;
    material.color.set( 'white' ); // crucial!
  }

  if ( s.options.normalMaterial ) material = new THREE.MeshNormalMaterial( { side: THREE.DoubleSide } );

  var c = geometry.center().multiplyScalar( -1 );
  var mesh = new THREE.Mesh( geometry, material );
  mesh.position.set( c.x, c.y, c.z );
  if ( s.options.renderOrder ) mesh.renderOrder = s.options.renderOrder;
  scene.add( mesh );

}

if ( config.clippingPlane ) {

  var v = config.clippingPlane[0];
  var d = config.clippingPlane[1];
  var plane = new THREE.Plane( new THREE.Vector3(v[0],v[1],v[2]).normalize(), d );
  renderer.clippingPlanes = [ plane ];

}

var scratch = new THREE.Vector3();

function render() {

  if ( animate ) requestAnimationFrame( render );
  renderer.render( scene, camera );

  for ( var i = 0 ; i < scene.children.length ; i++ )
    if ( scene.children[i].type === 'Sprite' ) {
      var sprite = scene.children[i];
      var adjust = scratch.addVectors( sprite.position, scene.position )
                          .sub( camera.position ).length() / 5;
      sprite.scale.set( adjust, .25*adjust ); // ratio of canvas width to height
    }

}

render();
controls.update();
if ( !animate ) render();

</script>

</body>
</html>
  `;

}

<|endoftext|>