<commit_before>
'use strict';

module.exports = function(kbox, pantheon) {

  // Node modules
  var path = require('path');

  // npm modules
  var _ = require('lodash');

  // Load some boxmods
  var Promise = kbox.Promise;

  /*
   * Add some other important things to our kalabox.yml before
   * creating it
   */
  kbox.core.events.on('pre-create-configure', function(data) {

    // Grab the config from teh data
    var config = data.config;
    var results = data.results;

    // Only run on Pantheon apps
    if (config.type === 'pantheon') {

      // Grab the current config
      var pantheonConfig = config.pluginconfig.pantheon;

      // Set the image version
      // Get relevant config options
      var prod = require(path.join(__dirname, 'config.json'));
      var locked = kbox.core.deps.get('globalConfig').locked;

      // Expose the correct pantheon img version
      pantheonConfig.images = (!locked) ? 'dev' : 'v' + prod.url.version;

      // Set a version
      config.version = (!locked) ? prod.url.dev : prod.url.version;

      // Make sure we have a session otherwise auth
      // NOTE: the only reason we wouldn't have a session
      return Promise.try(function() {
        if (!pantheon.getSession() && results.email && results.password) {
          return pantheon.auth(results.email, results.password);
        }
      })

      // Get site info
      .then(function() {
        return pantheon.getSites();
      })

      // Update our config with relevant info
      .then(function(pSites) {

        // Get cached site info
        var sites = pantheon.sites || pSites;
        // Get the UUID
        var uuid = _.findKey(sites, function(site) {
          return site.information.name === pantheonConfig.site;
        });
        var site = sites[uuid].information;

        // Set various kbox.yml properties
        pantheonConfig.framework = pantheonConfig.framework || site.framework;
        // jshint camelcase:false
        // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
        pantheonConfig.php = site.php_version || 53;
        pantheonConfig.upstream = site.upstream;

        // Remove password
        delete pantheonConfig.password;

        // Get the user profile
        var data = pantheon.__getAuthHeaders(pantheon.session);
        return pantheon.getProfile(pantheon.session.user_id, data);

      })

      .then(function(profile) {
        // jshint camelcase:false
        // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
        pantheonConfig.name = profile.full_name;
        // Rebuild json
        config.pluginconfig.pantheon = pantheonConfig;
      });

    }

  });

  /*
   * Make sure our pantheon SSH keys are set up
   */
  kbox.core.events.on('post-create-configure', function(data) {

    // Grab the config from teh data
    var config = data.config;

    if (config.type === 'pantheon') {

      // Set the correct session
      // @todo: it feels weird to have to do this again
      var account = config.pluginconfig.pantheon.email;
      pantheon.setSession(account, pantheon.getSessionFile(account));

      // Make sure we have SSH keys that can communciate with pantheon
      return pantheon.sshKeySetup();

    }

  });

};
<commit_msg>Add UUID property to kalabox.yml for pantheon apps<commit_after>
'use strict';

module.exports = function(kbox, pantheon) {

  // Node modules
  var path = require('path');

  // npm modules
  var _ = require('lodash');

  // Load some boxmods
  var Promise = kbox.Promise;

  /*
   * Add some other important things to our kalabox.yml before
   * creating it
   */
  kbox.core.events.on('pre-create-configure', function(data) {

    // Grab the config from teh data
    var config = data.config;
    var results = data.results;

    // Only run on Pantheon apps
    if (config.type === 'pantheon') {

      // Grab the current config
      var pantheonConfig = config.pluginconfig.pantheon;

      // Set the image version
      // Get relevant config options
      var prod = require(path.join(__dirname, 'config.json'));
      var locked = kbox.core.deps.get('globalConfig').locked;

      // Expose the correct pantheon img version
      pantheonConfig.images = (!locked) ? 'dev' : 'v' + prod.url.version;

      // Set a version
      config.version = (!locked) ? prod.url.dev : prod.url.version;

      // Make sure we have a session otherwise auth
      // NOTE: the only reason we wouldn't have a session
      return Promise.try(function() {
        if (!pantheon.getSession() && results.email && results.password) {
          return pantheon.auth(results.email, results.password);
        }
      })

      // Get site info
      .then(function() {
        return pantheon.getSites();
      })

      // Update our config with relevant info
      .then(function(pSites) {

        // Get cached site info
        var sites = pantheon.sites || pSites;
        // Get the UUID
        var uuid = _.findKey(sites, function(site) {
          return site.information.name === pantheonConfig.site;
        });
        var site = sites[uuid].information;

        // Set various kbox.yml properties
        pantheonConfig.framework = pantheonConfig.framework || site.framework;
        // jshint camelcase:false
        // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
        pantheonConfig.php = site.php_version || 53;
        pantheonConfig.upstream = site.upstream;
        pantheonConfig.uuid = uuid;

        // Remove password
        delete pantheonConfig.password;

        // Get the user profile
        var data = pantheon.__getAuthHeaders(pantheon.session);
        return pantheon.getProfile(pantheon.session.user_id, data);

      })

      .then(function(profile) {
        // jshint camelcase:false
        // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
        pantheonConfig.name = profile.full_name;
        // Rebuild json
        config.pluginconfig.pantheon = pantheonConfig;
      });

    }

  });

  /*
   * Make sure our pantheon SSH keys are set up
   */
  kbox.core.events.on('post-create-configure', function(data) {

    // Grab the config from teh data
    var config = data.config;

    if (config.type === 'pantheon') {

      // Set the correct session
      // @todo: it feels weird to have to do this again
      var account = config.pluginconfig.pantheon.email;
      pantheon.setSession(account, pantheon.getSessionFile(account));

      // Make sure we have SSH keys that can communciate with pantheon
      return pantheon.sshKeySetup();

    }

  });

};
<|endoftext|>


<commit_before>/*
 * Chrome-Last.fm-Scrobbler archive.org Connector by Malachi Soord
 *                (based on George Pollard's bandcamp connctor)
 * v0.1
 */
"use strict";

var lastTrack = null;

$(function () {
    // bind page unload function to discard current "now listening"
    cancel();
});

var durationElapsed = "#jw6_controlbar_elapsed";
var durationTotal = "#jw6_controlbar_duration";
var durationRegex = /(\d+):(\d+)/;

function parseDuration(elapsed, total) {
    try {
        var mEla = durationRegex.exec(elapsed);
        var mTot = durationRegex.exec(total);
        return {
            current: parseInt(mEla[1], 10) * 60 + parseInt(mEla[2], 10),
            total: parseInt(mTot[1], 10) * 60 + parseInt(mTot[2], 10)
        };
    } catch (err) {
        return 0;
    }
}

function parseArtist() {
    return $("span.key:contains('Artist/Composer:')").next().text();
}

function parseTitle() {
    var title = $(".playing > .ttl").text();

    // Some titles are stored as artist - track # - title
    var parts = title.split("-");
    if (parts.length > 0 && parts[0].trim() === parseArtist()) {
        // TODO: tighten up this
        title = parts[2].trim();
    }

    return title;
}

function parseAlbum() {
    var album = $(".x-archive-meta-title").text();

    // Remove artist from album
    var parts = album.split("-");
    if (parts.length > 0 && parts[0].trim() === parseArtist()) {
        album = album.substr(album.indexOf("-") + 1).trim();
    }

    return album;
}

function cancel() {
    $(window).unload(function () {
        // reset the background scrobbler song data
        chrome.runtime.sendMessage({
            type: "reset"
        });
        return true;
    });
}

//console.log('Archive.org: loaded');

$(durationElapsed).live("DOMSubtreeModified", function () {

    var duration = parseDuration($(durationElapsed).text(), $(durationTotal).text());

    //console.log('duration - ' + duration.current + ' / ' + duration.total);

    if (duration.current > 0) { // it's playing

        var artist = parseArtist();
        var track = parseTitle();
        var album = parseAlbum();

        if (lastTrack !== track) {
            lastTrack = track;

            //console.log("Archive.org: scrobbling - Artist: " + artist + "; Album:  " + album + "; Track: " + track + "; duration: " + duration.total);
            chrome.runtime.sendMessage({
                type: "validate",
                artist: artist,
                track: track
            }, function (response) {
                if (response !== false) {
                    chrome.runtime.sendMessage({
                        type: "nowPlaying",
                        artist: response.artist,
                        track: response.track,
                        duration: duration.total,
                        album: album
                    });
                } else {
                    chrome.runtime.sendMessage({
                        type: "nowPlaying",
                        duration: duration.total
                    });
                }
            });
        }
    }
});<commit_msg>Tightened up parseTitle and added comments<commit_after>/*
 * Chrome-Last.fm-Scrobbler archive.org Connector by Malachi Soord
 *                (based on George Pollard's bandcamp connctor)
 * v0.1
 */
"use strict";

var lastTrack = null;

$(function () {
    // bind page unload function to discard current "now listening"
    cancel();
});

var durationElapsed = "#jw6_controlbar_elapsed";
var durationTotal = "#jw6_controlbar_duration";
var durationRegex = /(\d+):(\d+)/;

function parseDuration(elapsed, total) {
    try {
        var mEla = durationRegex.exec(elapsed);
        var mTot = durationRegex.exec(total);
        return {
            current: parseInt(mEla[1], 10) * 60 + parseInt(mEla[2], 10),
            total: parseInt(mTot[1], 10) * 60 + parseInt(mTot[2], 10)
        };
    } catch (err) {
        return 0;
    }
}

function parseArtist() {
    return $("span.key:contains('Artist/Composer:')").next().text();
}

function parseTitle() {

    // Get title directly from player
    var title = $(".playing > .ttl").text();

    // Some titles are stored as artist - track # - title so strip out non-title elements
    var parts = title.split("-");
    if (parts.length === 3 && parts[0].trim() === parseArtist()) {
        title = parts[2].trim();
    }

    return title;
}

function parseAlbum() {
    var album = $(".x-archive-meta-title").text();

    // Remove artist from album
    var parts = album.split("-");
    if (parts.length > 0 && parts[0].trim() === parseArtist()) {
        album = album.substr(album.indexOf("-") + 1).trim();
    }

    return album;
}

function cancel() {
    $(window).unload(function () {
        // reset the background scrobbler song data
        chrome.runtime.sendMessage({
            type: "reset"
        });
        return true;
    });
}

//console.log('Archive.org: loaded');

$(durationElapsed).live("DOMSubtreeModified", function () {

    var duration = parseDuration($(durationElapsed).text(), $(durationTotal).text());

    //console.log('duration - ' + duration.current + ' / ' + duration.total);

    if (duration.current > 0) { // it's playing

        var artist = parseArtist();
        var track = parseTitle();
        var album = parseAlbum();

        if (lastTrack !== track) {
            lastTrack = track;

            //console.log("Archive.org: scrobbling - Artist: " + artist + "; Album:  " + album + "; Track: " + track + "; duration: " + duration.total);
            chrome.runtime.sendMessage({
                type: "validate",
                artist: artist,
                track: track
            }, function (response) {
                if (response !== false) {
                    chrome.runtime.sendMessage({
                        type: "nowPlaying",
                        artist: response.artist,
                        track: response.track,
                        duration: duration.total,
                        album: album
                    });
                } else {
                    chrome.runtime.sendMessage({
                        type: "nowPlaying",
                        duration: duration.total
                    });
                }
            });
        }
    }
});<|endoftext|>


<commit_before>/*
 * Chrome-Last.fm-Scrobbler Ghostly Discovery Connector by Peter McEvoy
 * 2011-08-28
 */

var track, artist, album, duration
var scrobbleTimeout = null;
var durationTimeout = null;

$(function(){

	//setup unload handler
	$(window).unload(function() {
		// reset the background scrobbler song data
		chrome.runtime.sendMessage({type: 'reset'});
		return true;
	});

	//Start when user has picked a mood to listen to.
	$("div#submit input").click(function(){
		//Listen for change in result div
		var c = 0;
		$('div#result').bind('DOMNodeInserted', function(e) {
			//HTML changed, NEW SONG!!!
			//The jquery dom bind gets triggerd two times, this is to avoid the code from being executed twice.
			if (c == 0) {c++; return false;}
			// cancel any previous timeout
			if (scrobbleTimeout != null)clearTimeout(scrobbleTimeout);
			if (durationTimeout != null)clearTimeout(durationTimeout);

			displayMsg()

			//set vars
			artist = $("dd.artist").text();
			track = $("dd.track").text();
			album = $("dd.album").text();

			//Song duration on page updates as the music gets loaded by the browser. Might take a while before you get a fixed duration for the track. waitForDuration checks if the track duration has not changed in a while (the track should then be fully loaded)
			waitForDuration(updateNowPlaying);

			c=0;
		})
	})
});

function updateNowPlaying() {
	if (artist == '' || track == '' || duration == 0) {return;}
	//validate track info
	chrome.runtime.sendMessage({type: 'validate', artist: artist, track: track}, function(response) {
		if (response != false){

			chrome.runtime.sendMessage({type: 'nowPlaying', artist: artist, track: track, duration: duration});

			console.log({type: 'nowPlaying', artist: artist, track: track, duration: duration});
		}

	});
}

function waitForDuration(callback) {

	function getDuration() {
		var duration_el = $("li#info_position").text();
		if (duration_el == "loading..." || duration_el == "0:00")return false;
		var total_length = duration_el.split(" / ")[1];
		return parseInt(total_length.split(":")[0]*60)+parseInt(total_length.split(":")[1]);	// turn something like 5:50 into seconds
	}

	var check_1 = getDuration();

	durationTimeout = setTimeout(function(){

									var check_2 = getDuration();
									if (check_1 == check_2 && check_1 != false && check_1 != 0 ){
										//not loading anymore
										duration = check_1;
										callback();
									}
									else {
										waitForDuration(callback);
									}
	}, 1000);

}
<commit_msg>Removed v1 connector<commit_after><|endoftext|>


<commit_before>import Ember from 'ember';
var Visualizer = require('asNEAT/asNEAT-visualizer')['default'];

export default Ember.Component.extend({
  // passed in
  instrumentNetwork: null,
  selected: false,
  isLive: false,
  index: 0,

  instrumentModel: null,

  makeLiveHandler: null,

  width: "100%",
  height: "100%",

  // created on init
  visualization: null,

  isSaved: function() {
    var model = this.get('instrumentModel');
    return model !== null && model !== undefined;
  }.property('instrumentModel'),

  selector: function() {
    return "#"+this.elementId+' .visualizer';
  }.property('elementId'),

  initVisualization: function() {
    Ember.run.scheduleOnce('afterRender', this, function() {
      var visualization = Visualizer.createMultiVisualization({
        network: this.get('instrumentNetwork'),
        selector: this.get('selector'),
        width: this.get('width'),
        height: this.get('height'),
        // white, green, yellow, Orange, red, purple, black
        colorScaleColors: ['#ffffff', '#308014', '#b2b200', '#cc8400', '#cc0000', '#660066', '#000000'],
        // following y=sqrt(x) for x_delta = 0.166667
        colorScalePositions: [0,0.40824829,0.577350269,0.707106781,0.816496581,0.912870929,1]
      });
      visualization.init();
      visualization.start();
      this.set('visualization', visualization);
    });
  }.on('init'),

  willDestroy: function() {
    this._super();
    this.get('visualization').stop();
  },

  actions: {
    play: function() {
      this.get('instrumentNetwork').play();
    },

    save: function() {
      var controller = this.get('targetObject');

      // Check if user is logged in first
      if (!controller.get('session').get('isAuthenticated')) {
        controller.get('controllers.application').send('showLogin');
        return;
      }

      // TODO: Start spinner?
      // TODO: Check if user is logged in (or just have a button that looks like save, but logs in first)
      var self = this,
          store = controller.get('.store');
      var instrument = store.createRecord('instrument', {
        created: Date.now(),
        user: controller.get('controllers.application.currentUser'),
        name: 'new Instr',
        json: this.get('instrumentNetwork').toJSON(),
        isPrivate: true
      });
      instrument.save().then(function(instrument) {
        self.set('instrumentModel', instrument);
      }, function(error) {
        // TODO: Show error?
        console.log('error saving: '+error);
      });
    },

    toggleSelected: function() {
      this.set('selected',
        !this.get('selected'));
    },

    makeLive: function() {
      var makeLiveHandler = this.get('makeLiveHandler');
      this.get('targetObject').send(makeLiveHandler, this);
    },

    nextVisualization: function() {
      this.get('visualization').nextVisualization();
    }
  }
});
<commit_msg>let server resolve the currently logged in user to tie to the new instrument<commit_after>import Ember from 'ember';
var Visualizer = require('asNEAT/asNEAT-visualizer')['default'];

export default Ember.Component.extend({
  // passed in
  instrumentNetwork: null,
  selected: false,
  isLive: false,
  index: 0,

  instrumentModel: null,

  makeLiveHandler: null,

  width: "100%",
  height: "100%",

  // created on init
  visualization: null,

  isSaved: function() {
    var model = this.get('instrumentModel');
    return model !== null && model !== undefined;
  }.property('instrumentModel'),

  selector: function() {
    return "#"+this.elementId+' .visualizer';
  }.property('elementId'),

  initVisualization: function() {
    Ember.run.scheduleOnce('afterRender', this, function() {
      var visualization = Visualizer.createMultiVisualization({
        network: this.get('instrumentNetwork'),
        selector: this.get('selector'),
        width: this.get('width'),
        height: this.get('height'),
        // white, green, yellow, Orange, red, purple, black
        colorScaleColors: ['#ffffff', '#308014', '#b2b200', '#cc8400', '#cc0000', '#660066', '#000000'],
        // following y=sqrt(x) for x_delta = 0.166667
        colorScalePositions: [0,0.40824829,0.577350269,0.707106781,0.816496581,0.912870929,1]
      });
      visualization.init();
      visualization.start();
      this.set('visualization', visualization);
    });
  }.on('init'),

  willDestroy: function() {
    this._super();
    this.get('visualization').stop();
  },

  actions: {
    play: function() {
      this.get('instrumentNetwork').play();
    },

    save: function() {
      var controller = this.get('targetObject');

      // Check if user is logged in first
      if (!controller.get('session').get('isAuthenticated')) {
        controller.get('controllers.application').send('showLogin');
        return;
      }

      // TODO: Start spinner?
      // TODO: Check if user is logged in (or just have a button that looks like save, but logs in first)
      var self = this,
          store = controller.get('.store');
      var instrument = store.createRecord('instrument', {
        created: Date.now(),
        name: 'new Instr',
        json: this.get('instrumentNetwork').toJSON(),
        isPrivate: true
      });
      instrument.save().then(function(instrument) {
        self.set('instrumentModel', instrument);
      }, function(error) {
        // TODO: Show error?
        console.log('error saving: '+error);
      });
    },

    toggleSelected: function() {
      this.set('selected',
        !this.get('selected'));
    },

    makeLive: function() {
      var makeLiveHandler = this.get('makeLiveHandler');
      this.get('targetObject').send(makeLiveHandler, this);
    },

    nextVisualization: function() {
      this.get('visualization').nextVisualization();
    }
  }
});
<|endoftext|>


<commit_before>//
// Copyright (c) 2013-2016 SKYARCH NETWORKS INC.
//
// This software is released under the MIT License.
//
// http://opensource.org/licenses/mit-license.php
//

const Infrastructure = require('models/infrastructure').default;
const CFTemplate = require('models/cf_template').default;
const Resource = require('models/resource').default;
const EC2Instance = require('models/ec2_instance').default;
const helpers = require('infrastructures/helper.js');

const AlertDanger = helpers.alert_danger;
const ReloadInfraIndexPage = require('infrastructures/show_infra').reload_infra_index_page;

module.exports = function () {
  function data() {
    return {
      infra_model: null, // これが別のオブジェクトを指したら、表示系の非同期処理は取り消される
      current_infra: {
        id: null,
        stack: {
          status: {
            type: null,
          },
        },
        resources: {},
        events: [],
        templates: { histories: null, globals: null },
        add_modify: {
          name: '', detail: '', format: 'JSON', value: '',
        },
      },
      tabpaneID: 'default', // tabpane 一つ一つのID. これに対応する tab の中身が表示される
      tabpaneGroupID: null, // 複数の tabpane をまとめるID. これに対応する tab が表示される
      spec_Columns: ['servertest', 'resource', 'message', 'status', 'created_at'],
      sec_group: null,
      instance_type: null,
      ops_sched_Columns: ['physical_id', 'screen_name', 'id'],
      serverspec_failed: t('infrastructures.serverspec_failed'),
      loading: true, // trueにすると、loading-tabpaneが表示される。
      infra_loading: true,
    };
  }
  return {
    template: '#infra-show-template',
    props: {
      initial_tab: String,
    },
    data,
    methods: {
      screen_name(res) {
        if (res.screen_name) {
          return `${res.screen_name} / ${this.subsStr(res.physical_id)}`;
        }
        return this.subsStr(res.physical_id);
      },
      subsStr(string) {
        if (string.length > 10) {
          return `${string.substring(0, string.length / 2)}...`;
        }
        return string;
      },
      show_ec2(PhysicalId) {
        this.show_tabpane('ec2');
        this.loading = true;
        this.tabpaneGroupID = PhysicalId;
      },
      show_rds(PhysicalId) {
        this.show_tabpane('rds');
        this.tabpaneGroupID = PhysicalId;
        this.loading = true;
      },
      show_elb(PhysicalId) {
        this.show_tabpane('elb');
        this.tabpaneGroupID = PhysicalId;
        this.loading = true;
      },
      show_s3(PhysicalId) {
        this.show_tabpane('s3');
        this.tabpaneGroupID = PhysicalId;
        this.loading = true;
      },
      show_no_resource() {
        this.loading = false;
      },
      show_add_modify() {
        const self = this;
        self.loading = true;

        const cft = new CFTemplate(self.infra_model);
        cft.new().done(self.wrapping_into_same_model_check((data) => {
          self.current_infra.templates.histories = data.histories;
          self.current_infra.templates.globals = data.globals;

          self.show_tabpane('add_modify');
        })).fail(self.wrapping_into_same_model_check(AlertDanger()));
      },

      show_add_ec2() { this.show_tabpane('add-ec2'); },

      show_cf_history() {
        const self = this;
        self.show_tabpane('cf_history');
        self.loading = true;
      },
      show_event_logs() {
        if (this.no_stack) { return; }
        const self = this;
        self.loading = true;

        self.infra_model.stack_events().done(self.wrapping_into_same_model_check((res) => {
          self.current_infra.events = res.stack_events;
          self.show_tabpane('event_logs');
        }));
      },
      show_infra_logs() {
        const self = this;
        self.show_tabpane('infra_logs');
        self.loading = true;
      },
      show_sec_groups() {
        const self = this;
        self.show_tabpane('security_groups');
        self.loading = true;
      },
      show_monitoring() {
        if (this.no_stack) { return; }
        const self = this;
        self.show_tabpane('monitoring');
        self.loading = true;
      },
      show_edit_monitoring() {
        if (this.no_stack) { return; }
        const self = this;
        self.show_tabpane('edit-monitoring');
        self.loading = true;
      },
      show_update_template() {
        if (this.no_stack) { return; }
        const self = this;
        self.show_tabpane('update-template');
        self.loading = true;
      },
      show_operation_sched(resources) {
        if (this.no_stack) { return; }
        const self = this;
        self.show_tabpane('operation-sched');
        self.resources = resources;
        self.loading = true;
      },

      tabpane_active(id) { return this.tabpaneID === id; },

      show_tabpane(id) {
        const self = this;
        self.loading = false;
        self.tabpaneGroupID = null;
        // 一旦 tabpane を null にすることで、同じ tabpane をリロードできるようにする。
        self.tabpaneID = null;
        Vue.nextTick(() => {
          self.tabpaneID = id;
        });
      },
      update_serverspec_status(PhysicalId) {
        const self = this;
        const ec2 = new EC2Instance(self.infra_model, PhysicalId);
        ec2.serverspec_status().done(self.wrapping_into_same_model_check((data) => {
          const r = _.find(self.current_infra.resources.ec2_instances, v => v.physical_id === PhysicalId);
          r.serverspec_status = data;
        }));
      },

      stack_in_progress() {
        const self = this;
        self.infra_model.stack_events().done(self.wrapping_into_same_model_check((res) => {
          self.$data.current_infra.events = res.stack_events;

          if (res.stack_status.type === 'IN_PROGRESS') {
            setTimeout(() => {
              self.stack_in_progress();
            }, 15000);
          } else {
            self.reset();
          }
        }));
      },
      is_progress() {
        return (this.current_infra.stack.status.type === 'IN_PROGRESS');
      },
      back_to_top() {
        const offset = 250;
        const duration = 300;

        $(window).scroll(function () {
          if ($(this).scrollTop() > offset) {
            $('.back-to-top').fadeIn(duration);
          } else {
            $('.back-to-top').fadeOut(duration);
          }
        });

        $('.back-to-top').click((event) => {
          event.preventDefault();
          $('html, body').animate({ scrollTop: 0 }, duration);
          return false;
        });
      },
      reset(OpenTab) {
        const self = this;
        const InfraId = this.$route.params.infra_id;
        self.data = data();
        self.current_infra.id = parseInt(InfraId, 10);
        self.$data.current_infra.events = [];
        self.infra_loading = true;
        self.infra_model = new Infrastructure(InfraId);
        self.infra_model.show().done(
          self.wrapping_into_same_model_check((stack) => {
            self.infra_loading = false;
            self.current_infra.stack = stack;
            self.init_infra(OpenTab);
          }),
        ).fail((msg) => {
          self.wrapping_into_same_model_check(AlertDanger(ReloadInfraIndexPage)(msg));
        });
      },
      wrapping_into_same_model_check(callback) {
        const self = this;
        return (function (MyModel) {
          return function (arg1) {
            if (MyModel !== self.infra_model) {
              return;
            }
            callback(arg1);
          };
        }(self.infra_model));
      },
      init_infra(CurrentTab) {
        const self = this;
        self.back_to_top();

        self.current_infra.resources = {};

        if (self.current_infra.stack.status.type === 'OK') {
          const res = new Resource(self.infra_model);
          res.index().done(self.wrapping_into_same_model_check((resources) => {
            _.forEach(resources.ec2_instances, (v) => {
              v.serverspec_status = true;
            });
            self.current_infra.resources = resources;
            // show first tab
            if (CurrentTab === 'show_sched') {
              self.show_operation_sched(resources);
            } else {
              const instance = _(resources).values().flatten().first();
              if (instance) {
                const PhysicalId = instance.physical_id;
                if (instance.type_name === 'AWS::EC2::Instance') {
                  self.show_ec2(PhysicalId);
                } else if (instance.type_name === 'AWS::RDS::DBInstance') {
                  self.show_rds(PhysicalId);
                } else if (instance.type_name === 'AWS::ElasticLoadBalancing::LoadBalancer') {
                  self.show_elb(PhysicalId);
                } else { // S3
                  self.show_s3(PhysicalId);
                }
              } else {
                self.show_no_resource();
              }
            }

            _.forEach(self.current_infra.resources.ec2_instances, (v) => {
              self.update_serverspec_status(v.physical_id);
            });
          }));
        } else if (self.current_infra.stack.status.type === 'IN_PROGRESS') {
          self.tabpaneID = 'default';
          self.stack_in_progress();
          self.$data.loading = false;
        } else if (self.current_infra.stack.status.type === 'NG') {
          self.tabpaneID = 'default';
          self.infra_model.stack_events().done(self.wrapping_into_same_model_check((res) => {
            self.$data.current_infra.events = res.stack_events;
            self.$data.loading = false;
          }));
        } else if (self.current_infra.stack.status.type === 'NONE') {
          // no stack info
          self.tabpaneID = 'default';
          self.$data.loading = false;
        }
      },
    },
    filters: {
      toLocaleString,
    },
    computed: {
      no_stack() { return this.current_infra.stack.status.type === 'NONE'; },
      in_progress() { return this.current_infra.stack.status.type === 'IN_PROGRESS'; },
      stack_fail() { return this.current_infra.stack.status.type === 'NG'; },
      no_resource() {
        return this.current_infra.stack.status.type === 'OK'
          && _(this.current_infra.resources).values().flatten().value().length === 0;
      },

      status_label_class() {
        let resp = 'label-';
        const StatusType = this.current_infra.stack.status.type;
        if (StatusType === 'OK') {
          resp += 'success';
        } else if (StatusType === 'NG') {
          resp += 'danger';
        } else {
          resp += 'default';
        }
        return resp;
      },
    },
    mounted() {
      this.$nextTick(function () {
        const self = this;
        self.$watch('$route.params.infra_id', (val) => {
          self.reset();
        });
        self.reset(self.initial_tab);
      });
    },
  };
};
<commit_msg>Fix: modules/newVM.jsをリファクタリング<commit_after>//
// Copyright (c) 2013-2016 SKYARCH NETWORKS INC.
//
// This software is released under the MIT License.
//
// http://opensource.org/licenses/mit-license.php
//

const Infrastructure = require('../models/infrastructure').default;
const CFTemplate = require('../models/cf_template').default;
const Resource = require('../models/resource').default;
const EC2Instance = require('../models/ec2_instance').default;
const helpers = require('../infrastructures/helper.js');

const AlertDanger = helpers.alert_danger;
const ReloadInfraIndexPage = require('../infrastructures/show_infra').reload_infra_index_page;

module.exports = () => {
  function makeData() {
    return {
      infra_model: null, // これが別のオブジェクトを指したら、表示系の非同期処理は取り消される
      current_infra: {
        id: null,
        stack: {
          status: {
            type: null,
          },
        },
        resources: {},
        events: [],
        templates: { histories: null, globals: null },
        add_modify: {
          name: '', detail: '', format: 'JSON', value: '',
        },
      },
      tabpaneID: 'default', // tabpane 一つ一つのID. これに対応する tab の中身が表示される
      tabpaneGroupID: null, // 複数の tabpane をまとめるID. これに対応する tab が表示される
      spec_Columns: ['servertest', 'resource', 'message', 'status', 'created_at'],
      sec_group: null,
      instance_type: null,
      ops_sched_Columns: ['physical_id', 'screen_name', 'id'],
      serverspec_failed: t('infrastructures.serverspec_failed'),
      loading: true, // trueにすると、loading-tabpaneが表示される。
      infra_loading: true,
    };
  }
  return {
    template: '#infra-show-template',
    props: {
      initial_tab: String,
    },
    data: makeData,
    methods: {
      screen_name(res) {
        if (res.screen_name) {
          return `${res.screen_name} / ${this.subsStr(res.physical_id)}`;
        }
        return this.subsStr(res.physical_id);
      },
      subsStr(string) {
        if (string.length > 10) {
          return `${string.substring(0, string.length / 2)}...`;
        }
        return string;
      },
      show_ec2(PhysicalId) {
        this.show_tabpane('ec2');
        this.loading = true;
        this.tabpaneGroupID = PhysicalId;
      },
      show_rds(PhysicalId) {
        this.show_tabpane('rds');
        this.tabpaneGroupID = PhysicalId;
        this.loading = true;
      },
      show_elb(PhysicalId) {
        this.show_tabpane('elb');
        this.tabpaneGroupID = PhysicalId;
        this.loading = true;
      },
      show_s3(PhysicalId) {
        this.show_tabpane('s3');
        this.tabpaneGroupID = PhysicalId;
        this.loading = true;
      },
      show_no_resource() {
        this.loading = false;
      },
      show_add_modify() {
        const self = this;
        self.loading = true;

        const cft = new CFTemplate(self.infra_model);
        cft.new().done(self.wrapping_into_same_model_check((data) => {
          self.current_infra.templates.histories = data.histories;
          self.current_infra.templates.globals = data.globals;

          self.show_tabpane('add_modify');
        })).fail(self.wrapping_into_same_model_check(AlertDanger()));
      },

      show_add_ec2() { this.show_tabpane('add-ec2'); },

      show_cf_history() {
        const self = this;
        self.show_tabpane('cf_history');
        self.loading = true;
      },
      show_event_logs() {
        if (this.no_stack) { return; }
        const self = this;
        self.loading = true;

        self.infra_model.stack_events().done(self.wrapping_into_same_model_check((res) => {
          self.current_infra.events = res.stack_events;
          self.show_tabpane('event_logs');
        }));
      },
      show_infra_logs() {
        const self = this;
        self.show_tabpane('infra_logs');
        self.loading = true;
      },
      show_sec_groups() {
        const self = this;
        self.show_tabpane('security_groups');
        self.loading = true;
      },
      show_monitoring() {
        if (this.no_stack) { return; }
        const self = this;
        self.show_tabpane('monitoring');
        self.loading = true;
      },
      show_edit_monitoring() {
        if (this.no_stack) { return; }
        const self = this;
        self.show_tabpane('edit-monitoring');
        self.loading = true;
      },
      show_update_template() {
        if (this.no_stack) { return; }
        const self = this;
        self.show_tabpane('update-template');
        self.loading = true;
      },
      show_operation_sched(resources) {
        if (this.no_stack) { return; }
        const self = this;
        self.show_tabpane('operation-sched');
        self.resources = resources;
        self.loading = true;
      },

      tabpane_active(id) { return this.tabpaneID === id; },

      show_tabpane(id) {
        const self = this;
        self.loading = false;
        self.tabpaneGroupID = null;
        // 一旦 tabpane を null にすることで、同じ tabpane をリロードできるようにする。
        self.tabpaneID = null;
        Vue.nextTick(() => {
          self.tabpaneID = id;
        });
      },
      update_serverspec_status(PhysicalId) {
        const self = this;
        const ec2 = new EC2Instance(self.infra_model, PhysicalId);
        ec2.serverspec_status().done(self.wrapping_into_same_model_check((data) => {
          const r = _.find(self.current_infra.resources.ec2_instances, v => v.physical_id === PhysicalId);
          r.serverspec_status = data;
        }));
      },

      stack_in_progress() {
        const self = this;
        self.infra_model.stack_events().done(self.wrapping_into_same_model_check((res) => {
          self.$data.current_infra.events = res.stack_events;

          if (res.stack_status.type === 'IN_PROGRESS') {
            setTimeout(() => {
              self.stack_in_progress();
            }, 15000);
          } else {
            self.reset();
          }
        }));
      },
      is_progress() {
        return (this.current_infra.stack.status.type === 'IN_PROGRESS');
      },
      back_to_top() {
        const offset = 250;
        const duration = 300;

        $(window).scroll(function windowScrollHandler() {
          if ($(this).scrollTop() > offset) {
            $('.back-to-top').fadeIn(duration);
          } else {
            $('.back-to-top').fadeOut(duration);
          }
        });

        $('.back-to-top').click((event) => {
          event.preventDefault();
          $('html, body').animate({ scrollTop: 0 }, duration);
          return false;
        });
      },
      reset(OpenTab) {
        const self = this;
        const InfraId = this.$route.params.infra_id;
        self.data = makeData();
        self.current_infra.id = parseInt(InfraId, 10);
        self.$data.current_infra.events = [];
        self.infra_loading = true;
        self.infra_model = new Infrastructure(InfraId);
        self.infra_model.show().done(
          self.wrapping_into_same_model_check((stack) => {
            self.infra_loading = false;
            self.current_infra.stack = stack;
            self.init_infra(OpenTab);
          }),
        ).fail((msg) => {
          self.wrapping_into_same_model_check(AlertDanger(ReloadInfraIndexPage)(msg));
        });
      },
      wrapping_into_same_model_check(callback) {
        const self = this;
        return (MyModel => (arg1) => {
          if (MyModel !== self.infra_model) {
            return;
          }
          callback(arg1);
        })(self.infra_model);
      },
      init_infra(CurrentTab) {
        const self = this;
        self.back_to_top();

        self.current_infra.resources = {};

        if (self.current_infra.stack.status.type === 'OK') {
          const res = new Resource(self.infra_model);
          res.index().done(self.wrapping_into_same_model_check((resources) => {
            _.forEach(resources.ec2_instances, (v) => {
              v.serverspec_status = true;
            });
            self.current_infra.resources = resources;
            // show first tab
            if (CurrentTab === 'show_sched') {
              self.show_operation_sched(resources);
            } else {
              const instance = Object.values(resources).flat()[0];
              if (instance) {
                const PhysicalId = instance.physical_id;
                if (instance.type_name === 'AWS::EC2::Instance') {
                  self.show_ec2(PhysicalId);
                } else if (instance.type_name === 'AWS::RDS::DBInstance') {
                  self.show_rds(PhysicalId);
                } else if (instance.type_name === 'AWS::ElasticLoadBalancing::LoadBalancer') {
                  self.show_elb(PhysicalId);
                } else { // S3
                  self.show_s3(PhysicalId);
                }
              } else {
                self.show_no_resource();
              }
            }

            self.current_infra.resources.ec2_instances.forEach((v) => {
              self.update_serverspec_status(v.physical_id);
            });
          }));
        } else if (self.current_infra.stack.status.type === 'IN_PROGRESS') {
          self.tabpaneID = 'default';
          self.stack_in_progress();
          self.$data.loading = false;
        } else if (self.current_infra.stack.status.type === 'NG') {
          self.tabpaneID = 'default';
          self.infra_model.stack_events().done(self.wrapping_into_same_model_check((res) => {
            self.$data.current_infra.events = res.stack_events;
            self.$data.loading = false;
          }));
        } else if (self.current_infra.stack.status.type === 'NONE') {
          // no stack info
          self.tabpaneID = 'default';
          self.$data.loading = false;
        }
      },
    },
    filters: {
      toLocaleString,
    },
    computed: {
      no_stack() { return this.current_infra.stack.status.type === 'NONE'; },
      in_progress() { return this.current_infra.stack.status.type === 'IN_PROGRESS'; },
      stack_fail() { return this.current_infra.stack.status.type === 'NG'; },
      no_resource() {
        return this.current_infra.stack.status.type === 'OK'
          && _(this.current_infra.resources).values().flatten().value().length === 0;
      },

      status_label_class() {
        let resp = 'label-';
        const StatusType = this.current_infra.stack.status.type;
        if (StatusType === 'OK') {
          resp += 'success';
        } else if (StatusType === 'NG') {
          resp += 'danger';
        } else {
          resp += 'default';
        }
        return resp;
      },
    },
    mounted() {
      this.$nextTick(function ready() {
        const self = this;
        self.$watch('$route.params.infra_id', () => {
          self.reset();
        });
        self.reset(self.initial_tab);
      });
    },
  };
};
<|endoftext|>


<commit_before>let ErrorDisplay = require('show_errors');

let PageModel = Backbone.Model.extend({
  urlRoot: '/api/pages',

  initialize: function(){
    this.lastSaved = null;
  },

  // override save to only actually save if it's new data
  save: function(data, callbacks) {
    if (_.isEqual(data, this.lastSaved)) {
      if (typeof callbacks.unchanged === 'function') {
        callbacks.unchanged();
      }
    } else {
      this.lastSaved = data;
      Backbone.Model.prototype.save.call(this, data, _.extend({patch: true}, callbacks));
    }
  },

  setLastSaved: function(data) {
    this.lastSaved = data;
  },

});

let PageEditBar = Backbone.View.extend({

  el: '.page-edit-bar',

  events: {
    'click .page-edit-bar__save-button': 'save',
    'click .page-edit-bar__error-message': 'findError',
    'change .page-edit-bar__toggle-autosave .onoffswitch__checkbox': 'toggleAutosave',
  },

  initialize: function() {
    this.outstandingSaveRequest = false;
    this.addStepsToSidebar();
    this.model = new PageModel();
    this.setupAutosave();
    this.$saveBtn = this.$('.page-edit-bar__save-button');
    $('body').scrollspy({ target: '.scrollspy', offset: 150});
  },

  isMobile: function() {
    return $('.mobile-indicator').is(':visible');
  },

  addStepsToSidebar: function() {
    const $existing = $('ul.page-edit-bar__step-list li');
    $('.page-edit-step').each((ii, step) => {
      this.addStepToSidebar($(step));
    });
    $existing.remove();
    this.$('ul.page-edit-bar__step-list').append($existing);
  },

  addStepToSidebar: function($step) {
    let $ul = this.$('ul.page-edit-bar__step-list');
    const title = $step.find('.page-edit-step__title')[0].childNodes[0].nodeValue.trim();
    const id = $step.attr('id');
    const icon = $step.data('icon') || 'cubes';
    const link_href = $step.data('link-to') ? $step.data('link-to') : `#${id}`;
    const link_target = $step.data('link-to') ? "_blank" : "_self";
    const li = `<li><a href="${link_href}" target="${link_target}"><i class="fa fa-${icon}"></i>${title}</a></li>`;
    $ul.append(li);
  },

  readData: function(){
    let data = {}
    $('form.one-form').each((ii, form) => {
      let $form = $(form);
      let type = $form.data('type') || 'base';
      if (!data.hasOwnProperty(type)) {
        data[type] = {}
      }
      $.extend(data[type], this.serializeForm($form))
    });
    data.id = data.page['page[id]'];
    return data;
  },

  serializeForm: function($form){
    let data = {}
    _.each($form.serializeArray(), function(pair) {
      // this is to handle form arrays cause their name ends in []
      if (pair.name.endsWith('[]')) {
        let name = pair.name.slice(0, -2);
        if (!data.hasOwnProperty(name)) {
          data[name] = []
        }
        data[name].push(pair.value)
      } else {
        data[pair.name] = pair.value;
      }
    });
    return data;
  },

  save: function() {
    $.publish('wysiwyg:submit'); // for summernote to update content
    if (!this.outstandingSaveRequest) {
      this.disableSubmit();
      this.model.save(this.readData(), {
          success: this.saved(),
          error: this.saveFailed(),
          unchanged: this.saveUnchanged()
      });
    }
  },

  saveUnchanged: function (){
    return () => { this.enableSubmit(); }
  },

  saved: function() {
    return (e, data) => { // closure for `this` cause it's an event callback
      if (data.refresh){ location.reload(); }
      this.enableSubmit();
      $.publish('page:saved', data);
      $('.page-edit-bar__save-box').removeClass('page-edit-bar__save-box--has-error');
      $('.page-edit-bar__error-message').text('');
      $('.page-edit-bar__last-saved').text(I18n.t('pages.edit.last_saved_at', {time: this.currentTime()}));
    }
  },

  currentTime: function() {
    const now = new Date();
    const minutes = (`0${now.getMinutes()}`).slice(-2); // for leading zero
    const seconds = (`0${now.getSeconds()}`).slice(-2); // for leading zero
    return `${now.getHours()}:${minutes}:${seconds}`
  },

  saveFailed: function() {
    return (e, data) => { // closure for `this` cause it's an event callback
      console.log("save failed with", e, data);
      this.enableSubmit();
      $('.page-edit-bar__save-box').addClass('page-edit-bar__save-box--has-error')
      if(data.status == 422) {
        ErrorDisplay.show(e, data);
        $('.page-edit-bar__error-message').text(I18n.t('pages.edit.user_error'));
        $.publish('page:errors');
      } else {
        $('.page-edit-bar__error-message').text(I18n.t('pages.edit.unknown_error'));
      }
    }
  },

  findError: function(){
    if (this.$('.page-edit-bar__save-box').hasClass('page-edit-bar__save-box--has-error')) {
      if ($('.has-error').length > 0) {
        $('html, body').animate({
            scrollTop: $('.has-error').first().offset().top
        }, 500);
      }
    }
  },

  toggleAutosave: function(e) {
    this.autosave = !this.autosave;
    this.$('.page-edit-bar__toggle-autosave').find('.toggle-button').toggleClass('btn-primary');
    if(this.autosave) {
      this.$('.page-edit-bar__btn-holder').addClass('page-edit-bar__btn-holder--hidden');
    } else {
      this.$('.page-edit-bar__btn-holder').removeClass('page-edit-bar__btn-holder--hidden');
    }
  },

  disableSubmit: function(){
    this.outstandingSaveRequest = true;
    this.$saveBtn.text(I18n.t('pages.edit.saving'));
    this.$saveBtn.addClass('disabled');
  },

  enableSubmit: function(){
    this.outstandingSaveRequest = false;
    this.$saveBtn.text(I18n.t('pages.edit.save_work'));
    this.$saveBtn.removeClass('disabled');
  },

  setupAutosave: function() {
    const SAVE_PERIOD = 5000; // milliseconds
    const shouldAutosave = (this.$('.page-edit-bar__toggle-autosave').data('autosave') == true);
    this.autosave = true;
    this.model.setLastSaved(this.readData());
    if (shouldAutosave != this.autosave) {
      this.toggleAutosave();
    }
    window.setInterval(() => {
      if(this.autosave) {
        this.save();
      } else {
        this.showUnsavedAlert();
      }
    }, SAVE_PERIOD)
  },

  showUnsavedAlert: function() {
    let $lastSaved = $('.page-edit-bar__last-saved');
    const noNotice = $lastSaved.find('.page-edit-bar__unsaved-notice').length < 1;
    const unsavedDataExists = !_.isEqual(this.model.lastSaved, this.readData());
    if (noNotice && unsavedDataExists){
      $lastSaved.append(`<div class="page-edit-bar__unsaved-notice">${I18n.t('pages.edit.unsaved_changes')}</div>`);
    }
  },

});

module.exports = PageEditBar;
<commit_msg>make unsaved notice disappear if campaigner undoes changes<commit_after>let ErrorDisplay = require('show_errors');

let PageModel = Backbone.Model.extend({
  urlRoot: '/api/pages',

  initialize: function(){
    this.lastSaved = null;
  },

  // override save to only actually save if it's new data
  save: function(data, callbacks) {
    if (_.isEqual(data, this.lastSaved)) {
      if (typeof callbacks.unchanged === 'function') {
        callbacks.unchanged();
      }
    } else {
      this.lastSaved = data;
      Backbone.Model.prototype.save.call(this, data, _.extend({patch: true}, callbacks));
    }
  },

  setLastSaved: function(data) {
    this.lastSaved = data;
  },

});

let PageEditBar = Backbone.View.extend({

  el: '.page-edit-bar',

  events: {
    'click .page-edit-bar__save-button': 'save',
    'click .page-edit-bar__error-message': 'findError',
    'change .page-edit-bar__toggle-autosave .onoffswitch__checkbox': 'toggleAutosave',
  },

  initialize: function() {
    this.outstandingSaveRequest = false;
    this.addStepsToSidebar();
    this.model = new PageModel();
    this.setupAutosave();
    this.$saveBtn = this.$('.page-edit-bar__save-button');
    $('body').scrollspy({ target: '.scrollspy', offset: 150});
  },

  isMobile: function() {
    return $('.mobile-indicator').is(':visible');
  },

  addStepsToSidebar: function() {
    const $existing = $('ul.page-edit-bar__step-list li');
    $('.page-edit-step').each((ii, step) => {
      this.addStepToSidebar($(step));
    });
    $existing.remove();
    this.$('ul.page-edit-bar__step-list').append($existing);
  },

  addStepToSidebar: function($step) {
    let $ul = this.$('ul.page-edit-bar__step-list');
    const title = $step.find('.page-edit-step__title')[0].childNodes[0].nodeValue.trim();
    const id = $step.attr('id');
    const icon = $step.data('icon') || 'cubes';
    const link_href = $step.data('link-to') ? $step.data('link-to') : `#${id}`;
    const link_target = $step.data('link-to') ? "_blank" : "_self";
    const li = `<li><a href="${link_href}" target="${link_target}"><i class="fa fa-${icon}"></i>${title}</a></li>`;
    $ul.append(li);
  },

  readData: function(){
    let data = {}
    $('form.one-form').each((ii, form) => {
      let $form = $(form);
      let type = $form.data('type') || 'base';
      if (!data.hasOwnProperty(type)) {
        data[type] = {}
      }
      $.extend(data[type], this.serializeForm($form))
    });
    data.id = data.page['page[id]'];
    return data;
  },

  serializeForm: function($form){
    let data = {}
    _.each($form.serializeArray(), function(pair) {
      // this is to handle form arrays cause their name ends in []
      if (pair.name.endsWith('[]')) {
        let name = pair.name.slice(0, -2);
        if (!data.hasOwnProperty(name)) {
          data[name] = []
        }
        data[name].push(pair.value)
      } else {
        data[pair.name] = pair.value;
      }
    });
    return data;
  },

  save: function() {
    $.publish('wysiwyg:submit'); // for summernote to update content
    if (!this.outstandingSaveRequest) {
      this.disableSubmit();
      this.model.save(this.readData(), {
          success: this.saved(),
          error: this.saveFailed(),
          unchanged: this.saveUnchanged()
      });
    }
  },

  saveUnchanged: function (){
    return () => { this.enableSubmit(); }
  },

  saved: function() {
    return (e, data) => { // closure for `this` cause it's an event callback
      if (data.refresh){ location.reload(); }
      this.enableSubmit();
      $.publish('page:saved', data);
      $('.page-edit-bar__save-box').removeClass('page-edit-bar__save-box--has-error');
      $('.page-edit-bar__error-message').text('');
      $('.page-edit-bar__last-saved').text(I18n.t('pages.edit.last_saved_at', {time: this.currentTime()}));
    }
  },

  currentTime: function() {
    const now = new Date();
    const minutes = (`0${now.getMinutes()}`).slice(-2); // for leading zero
    const seconds = (`0${now.getSeconds()}`).slice(-2); // for leading zero
    return `${now.getHours()}:${minutes}:${seconds}`
  },

  saveFailed: function() {
    return (e, data) => { // closure for `this` cause it's an event callback
      console.log("save failed with", e, data);
      this.enableSubmit();
      $('.page-edit-bar__save-box').addClass('page-edit-bar__save-box--has-error')
      if(data.status == 422) {
        ErrorDisplay.show(e, data);
        $('.page-edit-bar__error-message').text(I18n.t('pages.edit.user_error'));
        $.publish('page:errors');
      } else {
        $('.page-edit-bar__error-message').text(I18n.t('pages.edit.unknown_error'));
      }
    }
  },

  findError: function(){
    if (this.$('.page-edit-bar__save-box').hasClass('page-edit-bar__save-box--has-error')) {
      if ($('.has-error').length > 0) {
        $('html, body').animate({
            scrollTop: $('.has-error').first().offset().top
        }, 500);
      }
    }
  },

  toggleAutosave: function(e) {
    this.autosave = !this.autosave;
    this.$('.page-edit-bar__toggle-autosave').find('.toggle-button').toggleClass('btn-primary');
    if(this.autosave) {
      this.$('.page-edit-bar__btn-holder').addClass('page-edit-bar__btn-holder--hidden');
    } else {
      this.$('.page-edit-bar__btn-holder').removeClass('page-edit-bar__btn-holder--hidden');
    }
  },

  disableSubmit: function(){
    this.outstandingSaveRequest = true;
    this.$saveBtn.text(I18n.t('pages.edit.saving'));
    this.$saveBtn.addClass('disabled');
  },

  enableSubmit: function(){
    this.outstandingSaveRequest = false;
    this.$saveBtn.text(I18n.t('pages.edit.save_work'));
    this.$saveBtn.removeClass('disabled');
  },

  setupAutosave: function() {
    const SAVE_PERIOD = 5000; // milliseconds
    const shouldAutosave = (this.$('.page-edit-bar__toggle-autosave').data('autosave') == true);
    this.autosave = true;
    this.model.setLastSaved(this.readData());
    if (shouldAutosave != this.autosave) {
      this.toggleAutosave();
    }
    window.setInterval(() => {
      if(this.autosave) {
        this.save();
      } else {
        this.showUnsavedAlert();
      }
    }, SAVE_PERIOD)
  },

  showUnsavedAlert: function() {
    let $lastSaved = $('.page-edit-bar__last-saved');
    const noNotice = $lastSaved.find('.page-edit-bar__unsaved-notice').length < 1;
    const unsavedDataExists = !_.isEqual(this.model.lastSaved, this.readData());
    if (unsavedDataExists){
      if (noNotice) {
        $lastSaved.append(`<div class="page-edit-bar__unsaved-notice">${I18n.t('pages.edit.unsaved_changes')}</div>`);
      }
    } else {
      $lastSaved.find('.page-edit-bar__unsaved-notice').remove();
    }
  },

});

module.exports = PageEditBar;
<|endoftext|>


<commit_before>$(document).on('ready page:load', function () {
  // highlight active page in the top menu
  $('nav a').parents('li,ul').removeClass('active');
  $('nav a[href="' + this.location.pathname + '"]').parents('li,ul').addClass('active');
});
<commit_msg>Update link highlight<commit_after>$(document).on('ready turbolinks:load', function () {
  // highlight active page in the top menu
  $('nav a').parents('li,ul').removeClass('active');
  $('nav a[href="' + this.location.pathname + '"]').parents('li,ul').addClass('active');
});
<|endoftext|>


<commit_before>const setupOnce = require('setup_once');

(function(){

  let SharesEditor = Backbone.View.extend({

    events: {
      'click .shares-editor__delete-variant': 'deleteVariant',
      'click .shares-editor__toggle-edit': 'toggleEditor',
      'click .shares-editor__new-type-toggle .btn': 'switchVariantForm',
      'ajax:success form.shares-editor__new-form': 'clearForm',
    },

    initialize: function(){
      $.subscribe('page:errors', this.openEditorForErrors());
      $.subscribe('page:saved', this.updateSummaryRows());

      // this is the kind of DOM hosuekeeping that makes me want to use react
      $.subscribe('image:success', this.addImageSelectors());
      $.subscribe('image:destroyed', this.pruneImageSelectors());
    },

    deleteVariant: function(e){
        let $delete_button = this.$(e.target);
        let $summary_row = $delete_button.parents('.shares-editor__summary-row');
        let share_id = $summary_row.attr('id');
        // TODO: parse the row ID for variant type and ID: e.g. share_summary_facebook_12
        $.post(`/api/pages/1/delete-share`, {}, (whatever) => {
            console.log(whatever)
        })
    },

    toggleEditor: function(e) {
      let $target = this.$(e.target);
      $target = $target.is('tr') ? $target : $target.parents('tr');
      let $btn = $target.find('.shares-editor__toggle-edit');
      $target.next('.shares-editor__edit-row').toggleClass('hidden-closed');
      $btn.text( $btn.text() == "Edit" ? "Done" : "Edit" );
    },

    openEditor: function($edit_row){
      let $prev = $edit_row.prev('.shares-editor__summary-row');
      let $btn = $prev.find('.shares-editor__toggle-edit');
      $btn.text( "Done" );
      $edit_row.removeClass('hidden-closed');
    },

    switchVariantForm: function(e){
      let $target = this.$(e.target)
      const desired = $target.data('state');
      if (desired) {
        this.$('.shares-editor__new-type-toggle .btn').removeClass('btn-primary');
        $target.addClass('btn-primary');
        this.$('.shares-editor__new-form').addClass('hidden-closed');
        this.$(`.shares-editor__new-form[data-share="${desired}"]`).removeClass('hidden-closed');
      }
    },

    clearForm: function(e){
      $(e.target).find('input[type="text"], textarea').val('')
    },

    openEditorForErrors: function(){
      return () => { // closure for this in callback
        this.openEditor(this.$('.has-error').parents('.shares-editor__edit-row'));
      }
    },

    updateSummaryRows: function(){
      return (e, data) => { // closure for `this` in callback
        $.get(`/api/pages/${data.id}/share-rows`, (rows) => {
          _.each(rows, (row) => {
            let $row = $(row.html);
            $row = $(`#${$row.prop('id')}`).replaceWith($row);
            $row = $(`#${$row.prop('id')}`);
            if (!$row.next('.shares-editor__edit-row').hasClass('hidden-closed')) {
              $row.find('.shares-editor__toggle-edit').text('Done');
            }
          })
        });
      }
    },

    addImageSelectors: function(){
      return (e, file, id, html) => { // closure for `this` in callback
        let newOption = `<option value='${id}'>${file.name}</option>`;
        this.$('.shares-editor__image-selector').append(newOption);
      }
    },

    pruneImageSelectors: function(){
      return (e, id) => { // closure for `this` in callback
        this.$(`option[value="${id}"]`).remove()
      }
    },

  });

  $.subscribe("shares:edit", function(){
    setupOnce('.shares-editor', SharesEditor);
  });
}());
<commit_msg>fix up comments in shares_editor.js<commit_after>const setupOnce = require('setup_once');

(function(){

  let SharesEditor = Backbone.View.extend({

    events: {
      'click .shares-editor__delete-variant': 'deleteVariant',
      'click .shares-editor__toggle-edit': 'toggleEditor',
      'click .shares-editor__new-type-toggle .btn': 'switchVariantForm',
      'ajax:success form.shares-editor__new-form': 'clearForm',
    },

    initialize: function(){
      $.subscribe('page:errors', this.openEditorForErrors());
      $.subscribe('page:saved', this.updateSummaryRows());

      // this is the kind of DOM housekeeping that makes me want to use react
      $.subscribe('image:success', this.addImageSelectors());
      $.subscribe('image:destroyed', this.pruneImageSelectors());
    },

    deleteVariant: function(e){
        let $delete_button = this.$(e.target);
        let $summary_row = $delete_button.parents('.shares-editor__summary-row');
        let share_id = $summary_row.attr('id');
        // TODO: parse the row ID for variant type and ID: e.g. share_summary_facebook_12
        $.post(`/api/pages/1/delete-share`, {}, (whatever) => {
            console.log(whatever)
        })
    },

    toggleEditor: function(e) {
      let $target = this.$(e.target);
      $target = $target.is('tr') ? $target : $target.parents('tr');
      let $btn = $target.find('.shares-editor__toggle-edit');
      $target.next('.shares-editor__edit-row').toggleClass('hidden-closed');
      $btn.text( $btn.text() == "Edit" ? "Done" : "Edit" );
    },

    openEditor: function($edit_row){
      let $prev = $edit_row.prev('.shares-editor__summary-row');
      let $btn = $prev.find('.shares-editor__toggle-edit');
      $btn.text( "Done" );
      $edit_row.removeClass('hidden-closed');
    },

    switchVariantForm: function(e){
      let $target = this.$(e.target)
      const desired = $target.data('state');
      if (desired) {
        this.$('.shares-editor__new-type-toggle .btn').removeClass('btn-primary');
        $target.addClass('btn-primary');
        this.$('.shares-editor__new-form').addClass('hidden-closed');
        this.$(`.shares-editor__new-form[data-share="${desired}"]`).removeClass('hidden-closed');
      }
    },

    clearForm: function(e){
      $(e.target).find('input[type="text"], textarea').val('')
    },

    openEditorForErrors: function(){
      return () => { // closure for this in callback
        this.openEditor(this.$('.has-error').parents('.shares-editor__edit-row'));
      }
    },

    updateSummaryRows: function(){
      // this only updates existing shares. new ones are appended by
      // code in view/share/shares/create.js.erb, using rails UJS
      return (e, data) => { // closure for `this` in callback
        $.get(`/api/pages/${data.id}/share-rows`, (rows) => {
          _.each(rows, (row) => {
            let $row = $(row.html);
            $row = $(`#${$row.prop('id')}`).replaceWith($row);
            $row = $(`#${$row.prop('id')}`);
            if (!$row.next('.shares-editor__edit-row').hasClass('hidden-closed')) {
              $row.find('.shares-editor__toggle-edit').text('Done');
            }
          })
        });
      }
    },

    addImageSelectors: function(){
      return (e, file, id, html) => { // closure for `this` in callback
        let newOption = `<option value='${id}'>${file.name}</option>`;
        this.$('.shares-editor__image-selector').append(newOption);
      }
    },

    pruneImageSelectors: function(){
      return (e, id) => { // closure for `this` in callback
        this.$(`option[value="${id}"]`).remove()
      }
    },

  });

  $.subscribe("shares:edit", function(){
    setupOnce('.shares-editor', SharesEditor);
  });
}());
<|endoftext|>


<commit_before>/* ***** BEGIN LICENSE BLOCK *****
 * Distributed under the BSD license:
 *
 * Copyright (c) 2010, Ajax.org B.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Ajax.org B.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {
"use strict";

var bidiUtil = require("./lib/bidiutil");
var lang = require("./lib/lang");
var useragent = require("./lib/useragent");

/**
 * This object is used to ensure Bi-Directional support (for languages with text flowing from right to left, like Arabic or Hebrew)
 * including correct caret positioning, text selection mouse and keyboard arrows functioning
 * @class BidiHandler
 **/

/**
 * Creates a new `BidiHandler` object
 * @param {EditSession} session The session to use
 *
 * @constructor
 **/
var BidiHandler = function(session) {
    this.session = session;
    this.bidiMap = {};
    /* current screen row */
    this.currentRow = null;
    this.bidiUtil = bidiUtil;
    /* Arabic/Hebrew character width differs from regular character width */
    this.charWidths = [];
    this.EOL;
    this.showInvisibles;
    this.isRtlDir = false;
    this.line = "";
    this.wrapIndent = 0;
    /* font face has been set to Courier to ensure monospaced Bidi characters in Chrome/Opera */
    this.isCourierFont = false;
    this.isLastRow = false;
    this.EOF = "\xB6";
};

(function() {
    /**
     * Returns 'true' if row contains Bidi characters, in such case
     * creates Bidi map to be used in operations related to selection
     * (keyboard arrays, mouse click, select)
     * @param {Number} the screen row to be checked
     * @param {Number} the document row to be checked [optional]
     * @param {Number} the wrapped screen line index [ optional]
     **/
	this.isBidiRow = function(screenRow, docRow, splitIndex) {
		if (screenRow !== this.currentRow) {
			this.currentRow = screenRow;
			this.updateRowLine(docRow, splitIndex);
			this.updateBidiMap();
		}
		return this.bidiMap.bidiLevels;
	};

	this.hasBidiCharacters = function(str) {
		return /[\u0591-\u06ff\ufb1d-\ufefc]/.test(str);
	};

	this.getDocumentRow = function() {
		var docRow = 0;
		var rowCache = this.session.$screenRowCache;
		if (rowCache.length) {
			var index = this.session.$getRowCacheIndex(rowCache, this.currentRow);
			if (index >= 0)
				docRow = this.session.$docRowCache[index];
		}

		return docRow;
	};

	this.getSplitIndex = function() {
		var splitIndex = 0;
		var rowCache = this.session.$screenRowCache;
		if (rowCache.length) {
			var currentIndex, prevIndex = this.session.$getRowCacheIndex(rowCache, this.currentRow);
			while (this.currentRow - splitIndex > 0) {
				currentIndex = this.session.$getRowCacheIndex(rowCache, this.currentRow - splitIndex - 1);
				if (currentIndex !== prevIndex)
					break;

				prevIndex = currentIndex;
				splitIndex++;
			}
		}

		return splitIndex;
	};

	this.updateRowLine = function(docRow, splitIndex) {
		if (docRow === undefined)
			docRow = this.getDocumentRow();

		this.wrapIndent = 0;
		this.isLastRow = (docRow === this.session.getLength() - 1);
		this.line = this.session.getLine(docRow);
		if (this.session.$useWrapMode) {
			var splits = this.session.$wrapData[docRow];
			if (splits) {
				if (splitIndex === undefined)
					splitIndex = this.getSplitIndex();

				if(splitIndex > 0 && splits.length) {
					this.wrapIndent = splits.indent;
					this.line = (splitIndex < splits.length) ?
						this.line.substring(splits[splitIndex - 1], splits[splits.length - 1]) :
							this.line.substring(splits[splits.length - 1]);
				} else {
					this.line = this.line.substring(0, splits[splitIndex]);
				}
			}
		}

		/* replace tab and wide characters by commensurate spaces */
		var session = this.session, shift = 0, size;
		this.line = this.line.replace(/\t|[\u1100-\u2029, \u202F-\uFFE6]/g, function(ch, i){
			if (ch === '\t' || session.isFullWidth(ch.charCodeAt(0))) {
				size = (ch === '\t') ? session.getScreenTabSize(i + shift) : 2;
				shift += size - 1;
				return lang.stringRepeat(bidiUtil.DOT, size);
			}
		});
	};

	this.updateBidiMap = function() {
		var textCharTypes = [], endOfLine = this.isLastRow ? this.EOF : this.EOL;
		var line = this.line + (this.showInvisibles ? endOfLine : bidiUtil.DOT);
		if (bidiUtil.hasBidiCharacters(line, textCharTypes)) {
			this.bidiMap = bidiUtil.doBidiReorder(line, textCharTypes, this.isRtlDir);
		} else {
			this.bidiMap = {};
		}
	};

    /**
     * Resets stored info related to current screen row
     **/
	this.markAsDirty = function() {
		this.currentRow = null;
	};

    /**
     * Updates array of character widths
     * @param {Object} font metrics
     *
     **/
	this.updateCharacterWidths = function($fontMetrics) {
		var _regularCharWidth = $fontMetrics.$measureCharWidth('a'), _bidiCharWidth = $fontMetrics.$measureCharWidth('\u05d4');
		/* on some Windows7 OS not 'Monaco' but 'Courier' family is true monospaced font for Bidi characters in Chrome based browsers */
		if (!useragent.isMac && useragent.isChrome && (_bidiCharWidth !== $fontMetrics.$measureCharWidth('\u05d5'))) {
			this.isCourierFont = true;
			$fontMetrics.$main.style.fontFamily = "Courier";
			_regularCharWidth = $fontMetrics.$measureCharWidth('a');
			_bidiCharWidth = $fontMetrics.$measureCharWidth('\u05d4');
			$fontMetrics.$main.style.fontFamily = "inherit";
		}

		this.charWidths[bidiUtil.L] = this.charWidths[bidiUtil.EN] = this.charWidths[bidiUtil.ON_R] = _regularCharWidth;
		this.charWidths[bidiUtil.R] = this.charWidths[bidiUtil.AN] = _bidiCharWidth;
		this.charWidths[bidiUtil.R_H] = useragent.isChrome ? _bidiCharWidth : _bidiCharWidth * 0.45;
		this.charWidths[bidiUtil.B] = 0;

		if (this.currentRow !== null) {
			this.updateBidiMap();
		}
	};

	this.isCourierFontFace = function() {
		return this.isCourierFont;
	};

	this.getShowInvisibles = function() {
		return this.showInvisibles;
	};

	this.setShowInvisibles = function(showInvisibles) {
		this.showInvisibles = showInvisibles;
		if (this.currentRow !== null) {
			this.updateBidiMap();
		}
	};

	this.setEolChar = function(eolChar) {
		this.EOL = eolChar;
	};

	this.setTextDir = function(isRtlDir) {
		this.isRtlDir = isRtlDir;
	};

    /**
     * Returns offset of character at position defined by column.
     * @param {Number} the screen column position
     *
     * @return {int} horizontal pixel offset of given screen column
     **/
	this.getPosLeft = function(col) {
		col -= this.wrapIndent;
		var visualIdx = bidiUtil.getVisualFromLogicalIdx(col > 0 ? col - 1 : 0, this.bidiMap),
			levels = this.bidiMap.bidiLevels, left = 0;

		if (col === 0 && levels[visualIdx] % 2 !== 0)
			visualIdx++;

		for (var i = 0; i < visualIdx; i++) {
			left += this.charWidths[levels[i]];
		}

		if (col !== 0 && levels[visualIdx] % 2 === 0)
			left += this.charWidths[levels[visualIdx]];

		if (this.wrapIndent)
			left += this.wrapIndent * this.charWidths[bidiUtil.L];

		return left;
	};

    /**
     * Returns 'selections' - array of objects defining set of selection rectangles
     * @param {Number} the start column position
     * @param {Number} the end column position
     *
     * @return {Array of Objects} Each object contains 'left' and 'width' values defining selection rectangle.
     **/
	this.getSelections = function(startCol, endCol) {
		var map = this.bidiMap, levels = map.bidiLevels, level, offset = this.wrapIndent * this.charWidths[bidiUtil.L], selections = [],
			selColMin = Math.min(startCol, endCol) - this.wrapIndent, selColMax = Math.max(startCol, endCol) - this.wrapIndent,
				isSelected = false, isSelectedPrev = false, selectionStart = 0;

		for (var logIdx, visIdx = 0; visIdx < levels.length; visIdx++) {
			logIdx = map.logicalFromVisual[visIdx];
			level = levels[visIdx];
			isSelected = (logIdx >= selColMin) && (logIdx < selColMax);
			if (isSelected && !isSelectedPrev) {
				selectionStart = offset;
			} else if (!isSelected && isSelectedPrev) {
				selections.push({left: selectionStart, width: offset - selectionStart});
			}
			offset += this.charWidths[level];
			isSelectedPrev = isSelected;
		}

		if (isSelected && (visIdx === levels.length)) {
			selections.push({left: selectionStart, width: offset - selectionStart});
		}

		return selections;
	};

    /**
     * Converts character coordinates on the screen to respective document column number
     * @param {int} character horizontal offset
     *
     * @return {Number} screen column number corresponding to given pixel offset
     **/
	this.offsetToCol = function(posX) {
		var logicalIdx = 0, posX = Math.max(posX, 0),
			offset = 0, visualIdx = 0, levels = this.bidiMap.bidiLevels,
				charWidth = this.charWidths[levels[visualIdx]];

		if (this.wrapIndent) {
			posX -= this.wrapIndent * this.charWidths[bidiUtil.L];
		}

		while(posX > offset + charWidth/2) {
			offset += charWidth;
			if(visualIdx === levels.length - 1) {
				/* quit when we on the right of the last character, flag this by charWidth = 0 */
				charWidth = 0;
				break;
			}
			charWidth = this.charWidths[levels[++visualIdx]];
		}

		if (visualIdx > 0 && (levels[visualIdx - 1] % 2 !== 0) && (levels[visualIdx] % 2 === 0)){
		/* Bidi character on the left and None Bidi character on the right */
			if(posX < offset)
				visualIdx--;
			logicalIdx = this.bidiMap.logicalFromVisual[visualIdx];

		} else if (visualIdx > 0 && (levels[visualIdx - 1] % 2 === 0) && (levels[visualIdx] % 2 !== 0)){
		/* None Bidi character on the left and Bidi character on the right */
			logicalIdx = 1 + ((posX > offset) ? this.bidiMap.logicalFromVisual[visualIdx]
					: this.bidiMap.logicalFromVisual[visualIdx - 1]);

		} else if ((this.isRtlDir && visualIdx === levels.length - 1 && charWidth === 0 && (levels[visualIdx - 1] % 2 === 0))
				|| (!this.isRtlDir && visualIdx === 0 && (levels[visualIdx] % 2 !== 0))){
		/* To the right of last character, which is None Bidi, in RTL direction or */
		/* to the left of first Bidi character, in LTR direction */
			logicalIdx = 1 + this.bidiMap.logicalFromVisual[visualIdx];
		} else {
			/* Tweak visual position when Bidi character on the left in order to map it to corresponding logical position */
			if (visualIdx > 0 && (levels[visualIdx - 1] % 2 !== 0) && charWidth !== 0)
				visualIdx--;

			/* Regular case */
			logicalIdx = this.bidiMap.logicalFromVisual[visualIdx];
		}

		return (logicalIdx + this.wrapIndent);
	};

}).call(BidiHandler.prototype);

exports.BidiHandler = BidiHandler;
});
<commit_msg>fix width of EOL and more<commit_after>/* ***** BEGIN LICENSE BLOCK *****
 * Distributed under the BSD license:
 *
 * Copyright (c) 2010, Ajax.org B.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Ajax.org B.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {
"use strict";

var bidiUtil = require("./lib/bidiutil");
var lang = require("./lib/lang");
var useragent = require("./lib/useragent");

/**
 * This object is used to ensure Bi-Directional support (for languages with text flowing from right to left, like Arabic or Hebrew)
 * including correct caret positioning, text selection mouse and keyboard arrows functioning
 * @class BidiHandler
 **/

/**
 * Creates a new `BidiHandler` object
 * @param {EditSession} session The session to use
 *
 * @constructor
 **/
var BidiHandler = function(session) {
    this.session = session;
    this.bidiMap = {};
    /* current screen row */
    this.currentRow = null;
    this.bidiUtil = bidiUtil;
    /* Arabic/Hebrew character width differs from regular character width */
    this.charWidths = [];
    this.EOL;
    this.showInvisibles;
    this.isRtlDir = false;
    this.line = "";
    this.wrapIndent = 0;
    /* font face has been set to Courier to ensure monospaced Bidi characters in Chrome/Opera */
    this.isCourierFont = false;
    this.isLastRow = false;
    this.EOF = "\xB6";
};

(function() {
    /**
     * Returns 'true' if row contains Bidi characters, in such case
     * creates Bidi map to be used in operations related to selection
     * (keyboard arrays, mouse click, select)
     * @param {Number} the screen row to be checked
     * @param {Number} the document row to be checked [optional]
     * @param {Number} the wrapped screen line index [ optional]
     **/
	this.isBidiRow = function(screenRow, docRow, splitIndex) {
		if (screenRow !== this.currentRow) {
			this.currentRow = screenRow;
			this.updateRowLine(docRow, splitIndex);
			this.updateBidiMap();
		}
		return this.bidiMap.bidiLevels;
	};

	this.hasBidiCharacters = function(str) {
		return /[\u0591-\u06ff\ufb1d-\ufefc]/.test(str);
	};

	this.getDocumentRow = function() {
		var docRow = 0;
		var rowCache = this.session.$screenRowCache;
		if (rowCache.length) {
			var index = this.session.$getRowCacheIndex(rowCache, this.currentRow);
			if (index >= 0)
				docRow = this.session.$docRowCache[index];
		}

		return docRow;
	};

	this.getSplitIndex = function() {
		var splitIndex = 0;
		var rowCache = this.session.$screenRowCache;
		if (rowCache.length) {
			var currentIndex, prevIndex = this.session.$getRowCacheIndex(rowCache, this.currentRow);
			while (this.currentRow - splitIndex > 0) {
				currentIndex = this.session.$getRowCacheIndex(rowCache, this.currentRow - splitIndex - 1);
				if (currentIndex !== prevIndex)
					break;

				prevIndex = currentIndex;
				splitIndex++;
			}
		}

		return splitIndex;
	};

	this.updateRowLine = function(docRow, splitIndex) {
		if (docRow === undefined)
			docRow = this.getDocumentRow();

		this.wrapIndent = 0;
		this.isLastRow = (docRow === this.session.getLength() - 1);
		this.line = this.session.getLine(docRow);
		if (this.session.$useWrapMode) {
			var splits = this.session.$wrapData[docRow];
			if (splits) {
				if (splitIndex === undefined)
					splitIndex = this.getSplitIndex();

				if(splitIndex > 0 && splits.length) {
					this.wrapIndent = splits.indent;
					this.line = (splitIndex < splits.length) ?
						this.line.substring(splits[splitIndex - 1], splits[splits.length - 1]) :
							this.line.substring(splits[splits.length - 1]);
				} else {
					this.line = this.line.substring(0, splits[splitIndex]);
				}
			}
		}

		/* replace tab and wide characters by commensurate spaces */
		var session = this.session, shift = 0, size;
		this.line = this.line.replace(/\t|[\u1100-\u2029, \u202F-\uFFE6]/g, function(ch, i){
			if (ch === '\t' || session.isFullWidth(ch.charCodeAt(0))) {
				size = (ch === '\t') ? session.getScreenTabSize(i + shift) : 2;
				shift += size - 1;
				return lang.stringRepeat(bidiUtil.DOT, size);
			}
			return ch;
		});
	};

	this.updateBidiMap = function() {
		var textCharTypes = [], endOfLine = this.isLastRow ? this.EOF : this.EOL;
		var line = this.line + (this.showInvisibles ? endOfLine : bidiUtil.DOT);
		if (bidiUtil.hasBidiCharacters(line, textCharTypes)) {
			this.bidiMap = bidiUtil.doBidiReorder(line, textCharTypes, this.isRtlDir);
		} else {
			this.bidiMap = {};
		}
	};

    /**
     * Resets stored info related to current screen row
     **/
	this.markAsDirty = function() {
		this.currentRow = null;
	};

    /**
     * Updates array of character widths
     * @param {Object} font metrics
     *
     **/
	this.updateCharacterWidths = function($fontMetrics) {
		var _regularCharWidth = $fontMetrics.$measureCharWidth('a'), _bidiCharWidth = $fontMetrics.$measureCharWidth('\u05d4');
		/* on some Windows7 OS not 'Monaco' but 'Courier' family is true monospaced font for Bidi characters in Chrome based browsers */
		if (!useragent.isMac && useragent.isChrome && (_bidiCharWidth !== $fontMetrics.$measureCharWidth('\u05d5'))) {
			this.isCourierFont = true;
			$fontMetrics.$main.style.fontFamily = "Courier";
			_regularCharWidth = $fontMetrics.$measureCharWidth('a');
			_bidiCharWidth = $fontMetrics.$measureCharWidth('\u05d4');
			$fontMetrics.$main.style.fontFamily = "inherit";
		}

		this.charWidths[bidiUtil.L] = this.charWidths[bidiUtil.EN] = this.charWidths[bidiUtil.ON_R] = _regularCharWidth;
		this.charWidths[bidiUtil.R] = this.charWidths[bidiUtil.AN] = _bidiCharWidth;
		this.charWidths[bidiUtil.R_H] = useragent.isChrome ? _bidiCharWidth : _bidiCharWidth * 0.45;
		this.charWidths[bidiUtil.B] = 0;

		if (this.currentRow !== null) {
			this.updateBidiMap();
		}
	};

	this.isCourierFontFace = function() {
		return this.isCourierFont;
	};

	this.getShowInvisibles = function() {
		return this.showInvisibles;
	};

	this.setShowInvisibles = function(showInvisibles) {
		this.showInvisibles = showInvisibles;
		if (this.currentRow !== null) {
			this.updateBidiMap();
		}
	};

	this.setEolChar = function(eolChar) {
		this.EOL = eolChar;
	};

	this.setTextDir = function(isRtlDir) {
		this.isRtlDir = isRtlDir;
	};

    /**
     * Returns offset of character at position defined by column.
     * @param {Number} the screen column position
     *
     * @return {int} horizontal pixel offset of given screen column
     **/
	this.getPosLeft = function(col) {
		col -= this.wrapIndent;
		var visualIdx = bidiUtil.getVisualFromLogicalIdx(col > 0 ? col - 1 : 0, this.bidiMap),
			levels = this.bidiMap.bidiLevels, left = 0;

		if (col === 0 && levels[visualIdx] % 2 !== 0)
			visualIdx++;

		for (var i = 0; i < visualIdx; i++) {
			left += this.charWidths[levels[i]];
		}

		if (col !== 0 && levels[visualIdx] % 2 === 0)
			left += this.charWidths[levels[visualIdx]];

		if (this.wrapIndent)
			left += this.wrapIndent * this.charWidths[bidiUtil.L];

		return left;
	};

    /**
     * Returns 'selections' - array of objects defining set of selection rectangles
     * @param {Number} the start column position
     * @param {Number} the end column position
     *
     * @return {Array of Objects} Each object contains 'left' and 'width' values defining selection rectangle.
     **/
	this.getSelections = function(startCol, endCol) {
		var map = this.bidiMap, levels = map.bidiLevels, level, offset = this.wrapIndent * this.charWidths[bidiUtil.L], selections = [],
			selColMin = Math.min(startCol, endCol) - this.wrapIndent, selColMax = Math.max(startCol, endCol) - this.wrapIndent,
				isSelected = false, isSelectedPrev = false, selectionStart = 0;

		for (var logIdx, visIdx = 0; visIdx < levels.length; visIdx++) {
			logIdx = map.logicalFromVisual[visIdx];
			level = levels[visIdx];
			isSelected = (logIdx >= selColMin) && (logIdx < selColMax);
			if (isSelected && !isSelectedPrev) {
				selectionStart = offset;
			} else if (!isSelected && isSelectedPrev) {
				selections.push({left: selectionStart, width: offset - selectionStart});
			}
			offset += this.charWidths[level];
			isSelectedPrev = isSelected;
		}

		if (isSelected && (visIdx === levels.length)) {
			selections.push({left: selectionStart, width: offset - selectionStart});
		}

		return selections;
	};

    /**
     * Converts character coordinates on the screen to respective document column number
     * @param {int} character horizontal offset
     *
     * @return {Number} screen column number corresponding to given pixel offset
     **/
	this.offsetToCol = function(posX) {
		var logicalIdx = 0, posX = Math.max(posX, 0),
			offset = 0, visualIdx = 0, levels = this.bidiMap.bidiLevels,
				charWidth = this.charWidths[levels[visualIdx]];

		if (this.wrapIndent) {
			posX -= this.wrapIndent * this.charWidths[bidiUtil.L];
		}

		while(posX > offset + charWidth/2) {
			offset += charWidth;
			if(visualIdx === levels.length - 1) {
				/* quit when we on the right of the last character, flag this by charWidth = 0 */
				charWidth = 0;
				break;
			}
			charWidth = this.charWidths[levels[++visualIdx]];
		}

		if (visualIdx > 0 && (levels[visualIdx - 1] % 2 !== 0) && (levels[visualIdx] % 2 === 0)){
		/* Bidi character on the left and None Bidi character on the right */
			if(posX < offset)
				visualIdx--;
			logicalIdx = this.bidiMap.logicalFromVisual[visualIdx];

		} else if (visualIdx > 0 && (levels[visualIdx - 1] % 2 === 0) && (levels[visualIdx] % 2 !== 0)){
		/* None Bidi character on the left and Bidi character on the right */
			logicalIdx = 1 + ((posX > offset) ? this.bidiMap.logicalFromVisual[visualIdx]
					: this.bidiMap.logicalFromVisual[visualIdx - 1]);

		} else if ((this.isRtlDir && visualIdx === levels.length - 1 && charWidth === 0 && (levels[visualIdx - 1] % 2 === 0))
				|| (!this.isRtlDir && visualIdx === 0 && (levels[visualIdx] % 2 !== 0))){
		/* To the right of last character, which is None Bidi, in RTL direction or */
		/* to the left of first Bidi character, in LTR direction */
			logicalIdx = 1 + this.bidiMap.logicalFromVisual[visualIdx];
		} else {
			/* Tweak visual position when Bidi character on the left in order to map it to corresponding logical position */
			if (visualIdx > 0 && (levels[visualIdx - 1] % 2 !== 0) && charWidth !== 0)
				visualIdx--;

			/* Regular case */
			logicalIdx = this.bidiMap.logicalFromVisual[visualIdx];
		}

		return (logicalIdx + this.wrapIndent);
	};

}).call(BidiHandler.prototype);

exports.BidiHandler = BidiHandler;
});
<|endoftext|>


<commit_before>/**
 * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.
 * For licensing, see LICENSE.md or http://ckeditor.com/license
 */

CKEDITOR.editorConfig = function(config) {
	// Define changes to default configuration here. For example:
	// config.language = 'fr';
	// config.uiColor = '#AADC6E';
	config.height = '200px';
	config.disableNativeSpellChecker = false;
	config.scayt_autoStartup = true;
	CKEDITOR._scaytParams = {

		sLang: 'fr_FR'
	};

	//the next line add the new font to the combobox in CKEditor

	// The toolbar groups arrangement, optimized for two toolbar rows.
	config.contentsCss = '../../../app/styles/main.css';
	config.font_names = 'Dyslexic/opendyslexicregular;' + config.font_names;
	config.font_defaultLabel = 'Dyslexic';
	config.toolbarGroups = [
	//{ name: 'editing',     groups: [ 'spellchecker' ] },'find', 'selection',
	//{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
	//{ name: 'paragraph',   groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] },
	{
		name: 'editing',
		groups: ['spellchecker']
	}, {
		name: 'styles'
	}, {
		name: 'colors'
	}

	];

	config.toolbar_ComplexVersion = [
		['Bold', 'Italic', 'Underline', 'Strike']
	];

	config.toolbar_SimpleVersion = [
		['Source'],
		['Cut', 'Copy', 'Paste', 'PasteText'],
		['Bold', 'Italic', 'Underline', 'Strike']
	];

	config.toolbar_StyleVersion = [
		['Bold', 'Italic', 'Underline', 'Strike'],
		['Styles', 'Format', 'Font']
	];

	config.toolbar_OcrVersion = [
		['Scayt']
	];

	// Remove some buttons, provided by the standard plugins, which we don't
	// need to have in the Standard(s) toolbar.
	config.removeButtons = 'Underline,Subscript,Superscript';

	// Se the most common block elements.
	config.format_tags = 'p;h1;h2;h3;pre';

	// Make dialogs simpler.
	config.removeDialogTabs = 'image:advanced;link:advanced';

};<commit_msg>Ajouter langue française par défaut au WYSIWYG<commit_after>/**
 * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.
 * For licensing, see LICENSE.md or http://ckeditor.com/license
 */

CKEDITOR.editorConfig = function(config) {
	// Define changes to default configuration here. For example:
	// config.language = 'fr';
	// config.uiColor = '#AADC6E';
	config.height = '200px';
	config.disableNativeSpellChecker = false;
	config.scayt_autoStartup = true;
	CKEDITOR._scaytParams = {

		sLang: 'fr_FR'
	};

	//the next line add the new font to the combobox in CKEditor

	// The toolbar groups arrangement, optimized for two toolbar rows.
	config.contentsCss = '../../../app/styles/main.css';
	config.font_names = 'Dyslexic/opendyslexicregular;' + config.font_names;
	config.font_defaultLabel = 'Dyslexic';
	config.toolbarGroups = [
	//{ name: 'editing',     groups: [ 'spellchecker' ] },'find', 'selection',
	//{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
	//{ name: 'paragraph',   groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] },
	{
		name: 'editing',
		groups: ['spellchecker']
	}, {
		name: 'styles'
	}, {
		name: 'colors'
	}

	];

	config.toolbar_ComplexVersion = [
		['Bold', 'Italic', 'Underline', 'Strike']
	];

	config.toolbar_SimpleVersion = [
		['Source'],
		['Cut', 'Copy', 'Paste', 'PasteText'],
		['Bold', 'Italic', 'Underline', 'Strike']
	];

	config.toolbar_StyleVersion = [
		['Bold', 'Italic', 'Underline', 'Strike'],
		['Styles', 'Format', 'Font']
	];

	config.toolbar_OcrVersion = [
		['Scayt']
	];

	// Remove some buttons, provided by the standard plugins, which we don't
	// need to have in the Standard(s) toolbar.
	config.removeButtons = 'Underline,Subscript,Superscript';

	// Se the most common block elements.
	config.format_tags = 'p;h1;h2;h3;pre';

	// Make dialogs simpler.
	config.removeDialogTabs = 'image:advanced;link:advanced';
	config.scayt_sLang = 'fr_FR';

};
<|endoftext|>


<commit_before>import React from 'react';

export default class SecondaryNavBar extends React.Component {

  constructor(props)
  {
    super(props);
    this.onClickSaveDistrict = this.onClickSaveDistrict.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.state = {
      value: ''
    };

  }
  onClickSaveDistrict(districtid,name){
    console.log("on save district clicked");
    this.props.onModifyDistrict(districtid,name);

  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  render() {
  	var districtId = this.props.params.districtId;
  	var boundary = this.props.boundaryDetails[districtId];
  	var districtPath = "#" + boundary.path;
    var displayelement;
    this.state.value=boundary.name;
    if(sessionStorage.getItem('isAdmin'))
    {
      displayelement =

 Modify Details

                       <form className="form-horizontal" role="form">

                          District Name:

                            <input type="text" className="form-control" id="name" defaultValue={boundary.name} value={this.state.value} onChange={(event)=>{this.handleChange(event)}}/>

                        </form>

                          <button type="submit" className="btn btn-primary" onClick={() => {this.onClickSaveDistrict(districtId,"teset") }}>Save</button>
                          <button type="submit" className="btn btn-primary">Delete</button>

    }
    else
    {
      displayelement =

   Insufficient Permissions

You need administrator privileges to modify Boundary details.

 District Details

 Name: {boundary.name}

    }
    return(

{boundary.name}

    		{displayelement}

    );
  }
}

<commit_msg>access primaryDistrict using refs<commit_after>import React from 'react';

export default class SecondaryNavBar extends React.Component {

  constructor(props)
  {
    super(props);
    this.onClickSaveDistrict = this.onClickSaveDistrict.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.state = {
      value: ''
    };

  }
  onClickSaveDistrict(districtid,name){
    console.log("on save district clicked");
    console.log(this.districtName.value);
    this.props.onModifyDistrict(districtid,name);

  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  render() {
  	var districtId = this.props.params.districtId;
  	var boundary = this.props.boundaryDetails[districtId];
  	var districtPath = "#" + boundary.path;
    var displayelement;
    this.state.value=boundary.name;
    if(sessionStorage.getItem('isAdmin'))
    {
      displayelement =

 Modify Details

                       <form className="form-horizontal" role="form">

                          District Name:

                            <input type="text" ref={(ref) => this.districtName = ref} className="form-control" id="name" defaultValue={boundary.name} onChange={(event)=>{this.handleChange(event)}}/>

                        </form>

                          <button type="submit" className="btn btn-primary" onClick={() => {this.onClickSaveDistrict(districtId,"teset") }}>Save</button>
                          <button type="submit" className="btn btn-primary">Delete</button>

    }
    else
    {
      displayelement =

   Insufficient Permissions

You need administrator privileges to modify Boundary details.

 District Details

 Name: {boundary.name}

    }
    return(

{boundary.name}

    		{displayelement}

    );
  }
}

<|endoftext|>