<commit_before>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple, Inc. All rights reserved.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

/**
  @namespace

  Implements common selection management properties for controllers.

  Selection can be managed by any controller in your applications.  This
  mixin provides some common management features you might want such as
  disabling selection, or restricting empty or multiple selections.

  To use this mixin, simply add it to any controller you want to manage
  selection and call updateSelectionAfterContentChange()
  whenever your source content changes.  You can also override the properties
  defined below to configure how the selection management will treat your
  content.

  This mixin assumes the arrangedObjects property will return an SC.Array of
  content you want the selection to reflect.

  Add this mixin to any controller you want to manage selection.  It is
  already applied to the CollectionController and ArrayController.

  @author Charles Jolley
  @author Erich Ocean
  @version 1.0
  @since 0.9
*/
SC.SelectionSupport = {

  // ..........................................................
  // PROPERTIES
  //

  hasSelectionSupport: YES,

  /**
    If YES, selection is allowed. Default is YES.

    @type Boolean
  */
  allowsSelection: YES,

  /**
    If YES, multiple selection is allowed. Default is YES.

    @type Boolean
  */
  allowsMultipleSelection: YES,

  /**
    If YES, allow empty selection Default is YES.

    @type Boolean
  */
  allowsEmptySelection: YES,

  /**
    This is the current selection.  You can make this selection and another
    controller's selection work in concert by binding them together. You
    generally have a master selection that relays changes TO all the others.

    @property
    @type SC.SelectionSet
  */
  selection: function(key, value) {
    var content, empty;

    if (value !== undefined) {

      // are we even allowing selection at all?  Also, must be enumerable
      if (this.get('allowsSelection') && value && value.isEnumerable) {

        // ok, new decide if the *type* of selection is allowed...
        switch (value.get('length')) {

          // check to see if we're attempting to set an empty array
          // if that's not allowed, set to the first available item in
          // arrangedObjects
          case 0:
            empty   = this.get('allowsEmptySelection') ;
            content = this.get('arrangedObjects') ;
            if (!empty && content && content.get('length')>0) {
                value = SC.SelectionSet.create().add(content, 0).freeze() ;
            } else value = null ;
            break ;

          // single items are always allows
          case 1:
            break ;

          // fall through for >= 2, only allow if configured for multi-select
          default:
            if (!this.get('allowsMultipleSelection')) value = null ;
            break ;
        }
      } else value = null ;

      // always make selection into something then save
      if (!value) value = SC.SelectionSet.EMPTY ;
      this._scsel_selection = value ;

    // read only mode
    } else return this._scsel_selection ;

  }.property('arrangedObjects', 'allowsEmptySelection',
      'allowsMultipleSelection', 'allowsSelection').cacheable(),

  /**
    YES if the receiver currently has a non-zero selection.

    @property Boolean
  */
  hasSelection: function() {
    var sel = this.get('selection') ;
    return !!sel && (sel.get('length') > 0) ;
  }.property('selection').cacheable(),

  // ..........................................................
  // METHODS
  //

  /**
    Selects the passed objects in your content.  If you set "extend" to YES,
    then this will attempt to extend your selection as well.

    @param {SC.Enumerable} objects objects to select
    @param {Boolean} extend optionally set to YES to extend selection
    @returns {Object} receiver
  */
  selectObjects: function(objects, extend) {

    // handle passing an empty array
    if (!objects || objects.get('length')===0) {
      if (!extend) this.set('selection', SC.SelectionSet.EMPTY);
      return this;
    }

    var sel = this.get('selection');
    if (extend && sel) sel = sel.copy();
    else sel = SC.SelectionSet.create();

    sel.addObjects(objects).freeze();
    this.set('selection', sel);
    return this ;
  },

  /**
    Selects a single passed object in your content.  If you set "extend" to
    YES then this will attempt to extend your selection as well.

    @param {Object} object object to select
    @param {Boolean} extend optionally set to YES to extend selection
    @returns {Object} receiver
  */
  selectObject: function(object, extend) {
    if (object === null) {
      if (!extend) this.set('selection', null);
      return this ;

    } else return this.selectObjects([object], extend);
  },

  /**
    Deselects the passed objects in your content.

    @param {SC.Enumerable} objects objects to select
    @returns {Object} receiver
  */
  deselectObjects: function(objects) {

    if (!objects || objects.get('length')===0) return this; // nothing to do

    var sel = this.get('selection');
    if (!sel || sel.get('length')===0) return this; // nothing to do

    // find index for each and remove it
    sel = sel.copy().removeObjects(objects).freeze();
    this.set('selection', sel.freeze());
    return this ;
  },

  /**
    Deselects the passed object in your content.

    @param {SC.Object} object single object to select
    @returns {Object} receiver
  */
  deselectObject: function(object) {
    if (!object) return this; // nothing to do
    else return this.deselectObjects([object]);
  },

  /**
    Call this method whenever your source content changes to ensure the
    selection always remains up-to-date and valid.
  */
  updateSelectionAfterContentChange: function() {
    var content = this.get('arrangedObjects'),
        sel     = this.get('selection'),
        indexes, len, max, ret;

    if (!sel) sel = SC.SelectionSet.EMPTY ;

    // if selection is not allowed, just force to be empty.
    if (!this.get('allowsSelection') && sel.get('length')>0) {
      ret = SC.SelectionSet.EMPTY ;

    // selection is allowed, make sure it is valid
    } else {

      // remove from the sel any items selected beyond the length of the new
      // arrangedObjects
      indexes = content ? sel.indexSetForSource(content) : null ;
      len     = content ? content.get('length') : 0 ;
      max     = indexes ? indexes.get('max') : 0 ;
      if (max > len) { ret = sel.copy().constrain(content).freeze(); }
      else { ret = sel.copy().freeze(); }
    }

    if (ret) this.set('selection', ret) ;
    return this ;
  }

};
<commit_msg>Revert "fix selection support handling for allowsEmptySelection: NO" until unit tests can be added.<commit_after>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple, Inc. All rights reserved.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

/**
  @namespace

  Implements common selection management properties for controllers.

  Selection can be managed by any controller in your applications.  This
  mixin provides some common management features you might want such as
  disabling selection, or restricting empty or multiple selections.

  To use this mixin, simply add it to any controller you want to manage
  selection and call updateSelectionAfterContentChange()
  whenever your source content changes.  You can also override the properties
  defined below to configure how the selection management will treat your
  content.

  This mixin assumes the arrangedObjects property will return an SC.Array of
  content you want the selection to reflect.

  Add this mixin to any controller you want to manage selection.  It is
  already applied to the CollectionController and ArrayController.

  @author Charles Jolley
  @author Erich Ocean
  @version 1.0
  @since 0.9
*/
SC.SelectionSupport = {

  // ..........................................................
  // PROPERTIES
  //

  hasSelectionSupport: YES,

  /**
    If YES, selection is allowed. Default is YES.

    @type Boolean
  */
  allowsSelection: YES,

  /**
    If YES, multiple selection is allowed. Default is YES.

    @type Boolean
  */
  allowsMultipleSelection: YES,

  /**
    If YES, allow empty selection Default is YES.

    @type Boolean
  */
  allowsEmptySelection: YES,

  /**
    This is the current selection.  You can make this selection and another
    controller's selection work in concert by binding them together. You
    generally have a master selection that relays changes TO all the others.

    @property
    @type SC.SelectionSet
  */
  selection: function(key, value) {
    var content, empty;

    if (value !== undefined) {

      // are we even allowing selection at all?  Also, must be enumerable
      if (this.get('allowsSelection') && value && value.isEnumerable) {

        // ok, new decide if the *type* of selection is allowed...
        switch (value.get('length')) {

          // check to see if we're attempting to set an empty array
          // if that's not allowed, set to the first available item in
          // arrangedObjects
          case 0:
            empty   = this.get('allowsEmptySelection');
            content = this.get('arrangedObjects');
            if (empty && content && content.get('length')>0) {
                value = SC.SelectionSet.create().add(content, 0).freeze();
            } else value = null ;
            break;

          // single items are always allows
          case 1:
            break;

          // fall through for >= 2, only allow if configured for multi-select
          default:
            if (!this.get('allowsMultipleSelection')) value = null;
            break;
        }
      } else value = null;

      // always make selection into something then save
      if (!value) value = SC.SelectionSet.EMPTY;
      this._scsel_selection = value;

    // read only mode
    } else return this._scsel_selection ;

  }.property('arrangedObjects', 'allowsEmptySelection',
      'allowsMultipleSelection', 'allowsSelection').cacheable(),

  /**
    YES if the receiver currently has a non-zero selection.

    @property Boolean
  */
  hasSelection: function() {
    var sel = this.get('selection') ;
    return !!sel && (sel.get('length') > 0) ;
  }.property('selection').cacheable(),

  // ..........................................................
  // METHODS
  //

  /**
    Selects the passed objects in your content.  If you set "extend" to YES,
    then this will attempt to extend your selection as well.

    @param {SC.Enumerable} objects objects to select
    @param {Boolean} extend optionally set to YES to extend selection
    @returns {Object} receiver
  */
  selectObjects: function(objects, extend) {

    // handle passing an empty array
    if (!objects || objects.get('length')===0) {
      if (!extend) this.set('selection', SC.SelectionSet.EMPTY);
      return this;
    }

    var sel = this.get('selection');
    if (extend && sel) sel = sel.copy();
    else sel = SC.SelectionSet.create();

    sel.addObjects(objects).freeze();
    this.set('selection', sel);
    return this ;
  },

  /**
    Selects a single passed object in your content.  If you set "extend" to
    YES then this will attempt to extend your selection as well.

    @param {Object} object object to select
    @param {Boolean} extend optionally set to YES to extend selection
    @returns {Object} receiver
  */
  selectObject: function(object, extend) {
    if (object === null) {
      if (!extend) this.set('selection', null);
      return this ;

    } else return this.selectObjects([object], extend);
  },

  /**
    Deselects the passed objects in your content.

    @param {SC.Enumerable} objects objects to select
    @returns {Object} receiver
  */
  deselectObjects: function(objects) {

    if (!objects || objects.get('length')===0) return this; // nothing to do

    var sel = this.get('selection');
    if (!sel || sel.get('length')===0) return this; // nothing to do

    // find index for each and remove it
    sel = sel.copy().removeObjects(objects).freeze();
    this.set('selection', sel.freeze());
    return this ;
  },

  /**
    Deselects the passed object in your content.

    @param {SC.Object} object single object to select
    @returns {Object} receiver
  */
  deselectObject: function(object) {
    if (!object) return this; // nothing to do
    else return this.deselectObjects([object]);
  },

  /**
    Call this method whenever your source content changes to ensure the
    selection always remains up-to-date and valid.
  */
  updateSelectionAfterContentChange: function() {

    var content = this.get('arrangedObjects'),
        sel     = this.get('selection'),
        indexes, len, max, ret;

    if (!sel) sel = SC.SelectionSet.EMPTY;

    // if selection is not allowed, just force to be empty.
    if (!this.get('allowsSelection') && sel.get('length')>0) {
      ret = SC.SelectionSet.EMPTY;

    // selection is allowed, make sure it is valid
    } else {

      // remove from the sel any items selected beyond the length of the new
      // arrangedObjects
      indexes = content ? sel.indexSetForSource(content) : null;
      len     = content ? content.get('length') : 0;
      max     = indexes ? indexes.get('max') : 0;
      if (max > len) ret = sel.copy().constrain(content).freeze();

    }

    if (ret) this.set('selection', ret);
    return this ;
  }

};
<|endoftext|>


<commit_before>'use strict';

var express = require('express'),
	bodyParser = require('body-parser'),
	cookieParser = require('cookie-parser'),
	logger = require('morgan'),
	port = process.env.PORT || 3000,
	global = require('./config/global'),
	local = require('./routes/local');

// Configure application
var app = express();
app.use(bodyParser());
app.use(cookieParser());
app.use(logger('dev'));
app.use(function(req, res, next){
	res.setHeader('X-Powered-By', global.APP_NAME + global.APP_VERSION);
	next();
});

// Register routes
app.use(global.LOCAL_PATH, local);

// Start server
app.listen(port);
console.log('Express server listening on port ' + port);<commit_msg>Added security header<commit_after>'use strict';

var express = require('express'),
	bodyParser = require('body-parser'),
	cookieParser = require('cookie-parser'),
	logger = require('morgan'),
	port = process.env.PORT || 3000,
	global = require('./config/global'),
	local = require('./routes/local');

// Configure application
var app = express();
app.use(bodyParser());
app.use(cookieParser());
app.use(logger('dev'));
app.use(function(req, res, next){
	res.setHeader('X-Powered-By', global.APP_NAME + global.APP_VERSION);
	next();
});
app.use(function(req, res, next){
	if (req.headers['x-requested-by'] !== 'blinkbox') {
		res.send(403);
	} else {
		next();
	}
});

// Register routes
app.use(global.LOCAL_PATH, local);

// Start server
app.listen(port);
console.log('Express server listening on port ' + port);<|endoftext|>


<commit_before>const { URL } = require(`url`)
const { createRemoteFileNode } = require(`gatsby-source-filesystem`)

const nodeFromData = (datum, createNodeId) => {
  const { attributes: { id: _attributes_id, ...attributes } = {} } = datum
  const preservedId =
    typeof _attributes_id !== `undefined` ? { _attributes_id } : {}
  return {
    id: createNodeId(datum.id),
    drupal_id: datum.id,
    parent: null,
    drupal_parent_menu_item: attributes.parent,
    children: [],
    ...attributes,
    ...preservedId,
    drupal_relationships: datum.relationships,
    relationships: {},
    internal: {
      type: datum.type.replace(/-|__|:|\.|\s/g, `_`),
    },
  }
}

exports.nodeFromData = nodeFromData

const isFileNode = node =>
  node.internal.type === `files` || node.internal.type === `file__file`

exports.isFileNode = isFileNode

exports.downloadFile = async (
  { node, store, cache, createNode, createNodeId, getCache, reporter },
  { basicAuth, baseUrl }
) => {
  // handle file downloads
  if (isFileNode(node)) {
    let fileNode
    let fileType

    try {
      let fileUrl = node.url
      if (typeof node.uri === `object`) {
        // Support JSON API 2.x file URI format https://www.drupal.org/node/2982209
        fileUrl = node.uri.url
        // get file type from uri prefix ("S3:", "public:", etc.)
        const uri_prefix = node.uri.value.match(/^\w*:/)
        fileType = uri_prefix ? uri_prefix[0] : null
      }
      // Resolve w/ baseUrl if node.uri isn't absolute.
      const url = new URL(fileUrl, baseUrl)
      // If we have basicAuth credentials, add them to the request.
      const basicAuthFileSystems = [`public:`, `private:`, `temporary:`]
      const auth =
        typeof basicAuth === `object` && basicAuthFileSystems.includes(fileType)
          ? {
              htaccess_user: basicAuth.username,
              htaccess_pass: basicAuth.password,
            }
          : {}
      fileNode = await createRemoteFileNode({
        url: url.href,
        store,
        cache,
        createNode,
        createNodeId,
        getCache,
        parentNodeId: node.id,
        auth,
        reporter,
      })
    } catch (e) {
      // Ignore
    }
    if (fileNode) {
      node.localFile___NODE = fileNode.id
    }
  }
}
<commit_msg>fix(gatsby-source-drupal): Do not swallow errors (#28549)<commit_after>const { URL } = require(`url`)
const { createRemoteFileNode } = require(`gatsby-source-filesystem`)

const nodeFromData = (datum, createNodeId) => {
  const { attributes: { id: _attributes_id, ...attributes } = {} } = datum
  const preservedId =
    typeof _attributes_id !== `undefined` ? { _attributes_id } : {}
  return {
    id: createNodeId(datum.id),
    drupal_id: datum.id,
    parent: null,
    drupal_parent_menu_item: attributes.parent,
    children: [],
    ...attributes,
    ...preservedId,
    drupal_relationships: datum.relationships,
    relationships: {},
    internal: {
      type: datum.type.replace(/-|__|:|\.|\s/g, `_`),
    },
  }
}

exports.nodeFromData = nodeFromData

const isFileNode = node =>
  node.internal.type === `files` || node.internal.type === `file__file`

exports.isFileNode = isFileNode

exports.downloadFile = async (
  { node, store, cache, createNode, createNodeId, getCache, reporter },
  { basicAuth, baseUrl }
) => {
  // handle file downloads
  if (isFileNode(node)) {
    let fileNode
    let fileType

    let fileUrl = node.url
    if (typeof node.uri === `object`) {
      // Support JSON API 2.x file URI format https://www.drupal.org/node/2982209
      fileUrl = node.uri.url
      // get file type from uri prefix ("S3:", "public:", etc.)
      const uri_prefix = node.uri.value.match(/^\w*:/)
      fileType = uri_prefix ? uri_prefix[0] : null
    }
    // Resolve w/ baseUrl if node.uri isn't absolute.
    const url = new URL(fileUrl, baseUrl)
    // If we have basicAuth credentials, add them to the request.
    const basicAuthFileSystems = [`public:`, `private:`, `temporary:`]
    const auth =
      typeof basicAuth === `object` && basicAuthFileSystems.includes(fileType)
        ? {
            htaccess_user: basicAuth.username,
            htaccess_pass: basicAuth.password,
          }
        : {}
    fileNode = await createRemoteFileNode({
      url: url.href,
      store,
      cache,
      createNode,
      createNodeId,
      getCache,
      parentNodeId: node.id,
      auth,
      reporter,
    })
    if (fileNode) {
      node.localFile___NODE = fileNode.id
    }
  }
}
<|endoftext|>


<commit_before>const http = require('http');
const express = require('express');
const fs = require('fs');
const path = require('path');
const compression = require('compression');
const favicon = require('serve-favicon');
const {createBundleRenderer} = require('vue-server-renderer');
const serverBundleJSON = require('../dist/vue-ssr-server-bundle.json');
const clientManifestJSON = require('../dist/vue-ssr-client-manifest.json');
const template = fs.readFileSync('./src/index.html', 'utf-8');
const app = express();
const production = process.env.NODE_ENV === 'production';
const devServer = require('./dev-server');

const serve = (path, tocache) => express.static(path, {
  maxAge: (tocache && production) ? 31536000000 : 0
});

app.use('/sw.js', serve('./dist/sw.js', false));
app.use('/dist', serve('./dist', true));
app.use('/manifest.json', serve('./manifest.json', true));
app.use('/icons', serve('./src/icons', true));
app.use(favicon(path.resolve(__dirname, '../src/icons/icon-72x72.png')));

app.use(compression());

function doSSR(req, res) {
  const ctx = {url : req.url};
  renderer.renderToString(ctx, (err, html) => {
    if (err) {
      throw err;
    }
    res.end(html);
  });
}

function bundleRenderer(bundle, options) {
  return createBundleRenderer(bundle, Object.assign({}, options, {
    runInNewContext: false, // recommended
    template // optional
  }))
}

let renderer;
let ready;
if (production) {
  renderer = bundleRenderer(serverBundleJSON, {
    clientManifest: clientManifestJSON
  });
} else {
  // dev server
  ready = devServer(app, (bundle, options) => {
    renderer = bundleRenderer(bundle, options);
  });
}

app.get('*', production ? doSSR : (req, res) => {
  ready.then(_ => doSSR(req, res));
});

http.createServer(app).listen(8080, _ => {
  console.log('listening on http://localhost:8080');
});
<commit_msg>Update server.js<commit_after>const http = require('http');
const express = require('express');
const fs = require('fs');
const path = require('path');
const compression = require('compression');
const favicon = require('serve-favicon');
const {createBundleRenderer} = require('vue-server-renderer');
const serverBundleJSON = require('../dist/vue-ssr-server-bundle.json');
const clientManifestJSON = require('../dist/vue-ssr-client-manifest.json');
const template = fs.readFileSync('./src/index.html', 'utf-8');
const app = express();
const production = process.env.NODE_ENV === 'production';
const devServer = require('./dev-server');
const PORT = process.env.PORT || 5000;

const serve = (path, tocache) => express.static(path, {
  maxAge: (tocache && production) ? 31536000000 : 0
});

app.use('/sw.js', serve('./dist/sw.js', false));
app.use('/dist', serve('./dist', true));
app.use('/manifest.json', serve('./manifest.json', true));
app.use('/icons', serve('./src/icons', true));
app.use(favicon(path.resolve(__dirname, '../src/icons/icon-72x72.png')));

app.use(compression());

function doSSR(req, res) {
  const ctx = {url : req.url};
  renderer.renderToString(ctx, (err, html) => {
    if (err) {
      throw err;
    }
    res.end(html);
  });
}

function bundleRenderer(bundle, options) {
  return createBundleRenderer(bundle, Object.assign({}, options, {
    runInNewContext: false, // recommended
    template // optional
  }))
}

let renderer;
let ready;
if (production) {
  renderer = bundleRenderer(serverBundleJSON, {
    clientManifest: clientManifestJSON
  });
} else {
  // dev server
  ready = devServer(app, (bundle, options) => {
    renderer = bundleRenderer(bundle, options);
  });
}

app.get('*', production ? doSSR : (req, res) => {
  ready.then(_ => doSSR(req, res));
});

http.createServer(app).listen(PORT, _ => {
  console.log('listening on http://localhost:8080');
});
<|endoftext|>


<commit_before><commit_msg>Add solution and reflection<commit_after><|endoftext|>


<commit_before>define(["knockout",
        "app/util/form",
        "app/viewmodel/abstract",
        "app/model/models",
        "app/store/stores",
        "app/api/api"],
    function (ko, formutils, abstractVM, models, stores, API) {

        var vm = function () {
            var self = this;

            self.stackActions = ['Stop', 'Terminate', 'Start', 'Launch', 'Delete'];
            self.stackHostActions = ['Stop', 'Terminate', 'Start'];
            self.selectedProfile = null;
            self.selectedAccount = null;

            // Only show spot instance price box if the spot instance checkbox is checked
            self.isSpotInstance = ko.observable(false);
            $('#spot_instance').click(function () {
                self.isSpotInstance(this.checked);
            });

            // This builds the HTML for the stack history popover element
            self.popoverBuilder = function (stack) {
                return stack.history.map(function (h) {
                    var content = [];

                    content.push("
");
                    content.push("
');
                    content.push(h.status);
                    content.push('
');
                    content.push("
");
                    content.push(moment(h.created).fromNow());
                    content.push('
');
                    content.push('
');

                    return content.join('');

                }).join('');
            };

            self.doStackAction = function (action, evt, stack) {
                var data = JSON.stringify({
                    action: action.toLowerCase()
                });

                if (action !== 'Delete') {
                    $.ajax({
                        url: '/api/stacks/' + stack.id + '/',
                        type: 'PUT',
                        data: data,
                        headers: {
                            "X-CSRFToken": stackdio.settings.csrftoken,
                            "Accept": "application/json",
                            "Content-Type": "application/json"
                        },
                        success: function (response) {
                            console.log(response);
                            API.Stacks.load();
                        }
                    });
                } else {
                    $.ajax({
                        url: '/api/stacks/' + stack.id + '/',
                        type: 'DELETE',
                        headers: {
                            "X-CSRFToken": stackdio.settings.csrftoken,
                            "Accept": "application/json",
                            "Content-Type": "application/json"
                        },
                        success: function (response) {
                            console.log(response);
                            API.Stacks.load();
                        }
                    });

                }

            };

            self.doStackHostAction = function (action, evt, host) {
                var data = JSON.stringify({
                    action: action.toLowerCase()
                });

                // $.ajax({
                //     url: '/api/stacks/' + stack.id + '/',
                //     type: 'PUT',
                //     data: data,
                //     headers: {
                //         "X-CSRFToken": stackdio.settings.csrftoken,
                //         "Accept": "application/json",
                //         "Content-Type": "application/json"
                //     },
                //     success: function (response) {
                //         console.log(response);
                //         API.Stacks.load();
                //     }
                // });
            };

            self.saveStack = function (autoLaunch) {
                var h, host, hosts = stores.NewHosts();

                var stack = {
                    cloud_provider: self.selectedAccount.id,
                    auto_launch: autoLaunch === true,
                    title: document.getElementById('stack_title').value,
                    description: document.getElementById('stack_purpose').value,
                    hosts: hosts
                };

                API.Stacks.save(stack)
                    .then(function () {
                        stores.NewHosts.removeAll();

                        // Clear the stack form
                        $('#stack_title').val('');
                        $('#stack_purpose').val('');

                        // Hide the stack form
                        $( "#stack-form-container" ).dialog("close");

                        self.showSuccess();
                    });
            }

            self.launchStack = function (model, evt) {
                self.saveStack(true);
            };

            self.showStackDetails = function (stack) {
                API.StackHosts.load(stack)
                    .then(function (response) {
                        self.showStackHosts();
                        $('select').selectpicker();
                    });
                return;
            };

            //
            //      S T A C K   H O S T S
            //
            self.showHostMetadata = function (host) {
                stores.HostMetadata.removeAll();
                _.each(host.ec2_metadata, function (v, k, l) {
                    if (typeof v !== "object") {
                        stores.HostMetadata.push({ key: k, value: v });
                    }
                });

                $("#host-metadata-container").dialog("open");
            };

            self.closeHostMetadata = function () {
                $( "#host-metadata-container" ).dialog("close");
            };

            //
            //      N E W   H O S T S
            //
            self.addHost = function (model, evt) {
                var record = formutils.collectFormFields(evt.target.form);
                var v, vol;

                // Create a new host definition
                var host = new models.NewHost().create({
                    id: '',
                    host_count: record.host_count.value,
                    host_pattern: record.host_hostname.value,
                    host_size: record.host_instance_size.value,
                    cloud_profile: self.selectedProfile.id,
                    roles: record.host_roles,
                    availability_zone: record.availability_zone.value,
                    host_security_groups: record.host_security_groups.value
                });

                host.salt_roles = _.map(host.roles, function (r) { return r.value; });

                // Add the chosen instance size to the host definition
                host.size = _.find(stores.InstanceSizes(), function (i) {
                    return i.id === parseInt(record.host_instance_size.value, 10);
                });

                // Add some HTML to display for the chosen roles
                host.flat_roles = _.map(host.roles, function (r) {
                    return '
' + r.text + '
';
                }).join('');

                // Add spot instance config
                if (record.spot_instance_price.value !== '') {
                    host.spot_config = {};
                    host.spot_config.spot_price = record.spot_instance_price.value;
                }

                // Add volumes to the host
                host.volumes = [];
                for (v in stores.HostVolumes()) {
                    vol = stores.HostVolumes()[v];
                    host.volumes.push({
                        snapshot: vol.snapshot.id,
                        device: vol.device,
                        mount_point: vol.mount_point
                    });
                }

                console.log('new host', host);
                stores.NewHosts.push(host);

                // Clear out the spot instance bid price field
                document.getElementById('spot_instance_price').value = "";
            };

            self.removeHost = function (host) {
                stores.NewHosts.remove(host);
            };

            self.showStackHosts = function () {
                $( "#stack-details-container" ).dialog("open");
            };

            self.closeStackHosts = function () {
                $( "#stack-details-container" ).dialog("close");
            };

            self.showStackForm = function (account) {
                self.selectedAccount = account;
                $( "#stack-form-container" ).dialog("open");
            };

            self.closeStackForm = function () {
                $( "#stack-form-container" ).dialog("close");
            };

            self.showHostForm = function (profile) {
                self.selectedProfile = profile;

                // Choose the default instance size assigned to the chosen profile
                $('#host_instance_size').selectpicker('val', profile.default_instance_size);

                // Choose the default zone assigned to the chosen account
                $('#availability_zone').selectpicker('val', self.selectedAccount.default_availability_zone);

                $( "#host-form-container" ).dialog("open");
            };

            self.closeHostForm = function () {
                formutils.clearForm('host-form');
                $( "#host-form-container" ).dialog("close");
            };

            /*
             *  ==================================================================================
             *  D I A L O G   E L E M E N T S
             *  ==================================================================================
             */
            $("#stack-details-container").dialog({
                autoOpen: false,
                width: 650,
                modal: false
            });

            $("#host-metadata-container").dialog({
                autoOpen: false,
                width: 500,
                height: 600,
                modal: true
            });

            $("#host-form-container").dialog({
                position: [(window.innerWidth / 2) - 275,50],
                autoOpen: false,
                width: 600,
                modal: true
            });

            $("#stack-form-container").dialog({
                autoOpen: false,
                width: window.innerWidth - 225,
                height: 500,
                position: [200,50],
                modal: false
            });
        };

        vm.prototype = new abstractVM();

        return vm;
});<commit_msg>PI-96 - Adding ability to delete a stack<commit_after>define(["knockout",
        "app/util/form",
        "app/viewmodel/abstract",
        "app/model/models",
        "app/store/stores",
        "app/api/api"],
    function (ko, formutils, abstractVM, models, stores, API) {

        var vm = function () {
            var self = this;

            self.stackActions = ['Stop', 'Terminate', 'Start', 'Launch', 'Delete'];
            self.stackHostActions = ['Stop', 'Terminate', 'Start'];
            self.selectedProfile = null;
            self.selectedAccount = null;

            // Only show spot instance price box if the spot instance checkbox is checked
            self.isSpotInstance = ko.observable(false);
            $('#spot_instance').click(function () {
                self.isSpotInstance(this.checked);
            });

            // This builds the HTML for the stack history popover element
            self.popoverBuilder = function (stack) {
                return stack.history.map(function (h) {
                    var content = [];

                    content.push("
");
                    content.push("
');
                    content.push(h.status);
                    content.push('
');
                    content.push("
");
                    content.push(moment(h.created).fromNow());
                    content.push('
');
                    content.push('
');

                    return content.join('');

                }).join('');
            };

            self.doStackAction = function (action, evt, stack) {
                var data = JSON.stringify({
                    action: action.toLowerCase()
                });

                if (action !== 'Delete') {
                    $.ajax({
                        url: '/api/stacks/' + stack.id + '/',
                        type: 'PUT',
                        data: data,
                        headers: {
                            "X-CSRFToken": stackdio.settings.csrftoken,
                            "Accept": "application/json",
                            "Content-Type": "application/json"
                        },
                        success: function (response) {
                            API.Stacks.load();
                        }
                    });
                } else {
                    $.ajax({
                        url: '/api/stacks/' + stack.id + '/',
                        type: 'DELETE',
                        headers: {
                            "X-CSRFToken": stackdio.settings.csrftoken,
                            "Accept": "application/json",
                            "Content-Type": "application/json"
                        },
                        success: function (response) {
                            stores.Stacks.remove(function (s) {
                                return s.id === stack.id;
                            });
                        }
                    });

                }

            };

            self.doStackHostAction = function (action, evt, host) {
                var data = JSON.stringify({
                    action: action.toLowerCase()
                });

                // $.ajax({
                //     url: '/api/stacks/' + stack.id + '/',
                //     type: 'PUT',
                //     data: data,
                //     headers: {
                //         "X-CSRFToken": stackdio.settings.csrftoken,
                //         "Accept": "application/json",
                //         "Content-Type": "application/json"
                //     },
                //     success: function (response) {
                //         console.log(response);
                //         API.Stacks.load();
                //     }
                // });
            };

            self.saveStack = function (autoLaunch) {
                var h, host, hosts = stores.NewHosts();

                var stack = {
                    cloud_provider: self.selectedAccount.id,
                    auto_launch: autoLaunch === true,
                    title: document.getElementById('stack_title').value,
                    description: document.getElementById('stack_purpose').value,
                    hosts: hosts
                };

                API.Stacks.save(stack)
                    .then(function () {
                        stores.NewHosts.removeAll();

                        // Clear the stack form
                        $('#stack_title').val('');
                        $('#stack_purpose').val('');

                        // Hide the stack form
                        $( "#stack-form-container" ).dialog("close");

                        self.showSuccess();
                    });
            }

            self.launchStack = function (model, evt) {
                self.saveStack(true);
            };

            self.showStackDetails = function (stack) {
                API.StackHosts.load(stack)
                    .then(function (response) {
                        self.showStackHosts();
                        $('select').selectpicker();
                    });
                return;
            };

            //
            //      S T A C K   H O S T S
            //
            self.showHostMetadata = function (host) {
                stores.HostMetadata.removeAll();
                _.each(host.ec2_metadata, function (v, k, l) {
                    if (typeof v !== "object") {
                        stores.HostMetadata.push({ key: k, value: v });
                    }
                });

                $("#host-metadata-container").dialog("open");
            };

            self.closeHostMetadata = function () {
                $( "#host-metadata-container" ).dialog("close");
            };

            //
            //      N E W   H O S T S
            //
            self.addHost = function (model, evt) {
                var record = formutils.collectFormFields(evt.target.form);
                var v, vol;

                // Create a new host definition
                var host = new models.NewHost().create({
                    id: '',
                    host_count: record.host_count.value,
                    host_pattern: record.host_hostname.value,
                    host_size: record.host_instance_size.value,
                    cloud_profile: self.selectedProfile.id,
                    roles: record.host_roles,
                    availability_zone: record.availability_zone.value,
                    host_security_groups: record.host_security_groups.value
                });

                host.salt_roles = _.map(host.roles, function (r) { return r.value; });

                // Add the chosen instance size to the host definition
                host.size = _.find(stores.InstanceSizes(), function (i) {
                    return i.id === parseInt(record.host_instance_size.value, 10);
                });

                // Add some HTML to display for the chosen roles
                host.flat_roles = _.map(host.roles, function (r) {
                    return '
' + r.text + '
';
                }).join('');

                // Add spot instance config
                if (record.spot_instance_price.value !== '') {
                    host.spot_config = {};
                    host.spot_config.spot_price = record.spot_instance_price.value;
                }

                // Add volumes to the host
                host.volumes = [];
                for (v in stores.HostVolumes()) {
                    vol = stores.HostVolumes()[v];
                    host.volumes.push({
                        snapshot: vol.snapshot.id,
                        device: vol.device,
                        mount_point: vol.mount_point
                    });
                }

                console.log('new host', host);
                stores.NewHosts.push(host);

                // Clear out the spot instance bid price field
                document.getElementById('spot_instance_price').value = "";
            };

            self.removeHost = function (host) {
                stores.NewHosts.remove(host);
            };

            self.showStackHosts = function () {
                $( "#stack-details-container" ).dialog("open");
            };

            self.closeStackHosts = function () {
                $( "#stack-details-container" ).dialog("close");
            };

            self.showStackForm = function (account) {
                self.selectedAccount = account;
                $( "#stack-form-container" ).dialog("open");
            };

            self.closeStackForm = function () {
                $( "#stack-form-container" ).dialog("close");
            };

            self.showHostForm = function (profile) {
                self.selectedProfile = profile;

                // Choose the default instance size assigned to the chosen profile
                $('#host_instance_size').selectpicker('val', profile.default_instance_size);

                // Choose the default zone assigned to the chosen account
                $('#availability_zone').selectpicker('val', self.selectedAccount.default_availability_zone);

                $( "#host-form-container" ).dialog("open");
            };

            self.closeHostForm = function () {
                formutils.clearForm('host-form');
                $( "#host-form-container" ).dialog("close");
            };

            /*
             *  ==================================================================================
             *  D I A L O G   E L E M E N T S
             *  ==================================================================================
             */
            $("#stack-details-container").dialog({
                autoOpen: false,
                width: 650,
                modal: false
            });

            $("#host-metadata-container").dialog({
                autoOpen: false,
                width: 500,
                height: 600,
                modal: true
            });

            $("#host-form-container").dialog({
                position: [(window.innerWidth / 2) - 275,50],
                autoOpen: false,
                width: 600,
                modal: true
            });

            $("#stack-form-container").dialog({
                autoOpen: false,
                width: window.innerWidth - 225,
                height: 500,
                position: [200,50],
                modal: false
            });
        };

        vm.prototype = new abstractVM();

        return vm;
});<|endoftext|>


<commit_before>(function($) {
    $.isScrollToFixed = function(el) {
        return $(el).data('ScrollToFixed') !== undefined;
    };

    $.ScrollToFixed = function(el, options) {
        // To avoid scope issues, use 'base' instead of 'this' to reference this
        // class from internal events and functions.
        var base = this;

        // Access to jQuery and DOM versions of element.
        base.$el = $(el);
        base.el = el;

        // Add a reverse reference to the DOM object.
        base.$el.data('ScrollToFixed', base);

        // A flag so we know if the scroll has been reset.
        var isReset = false;

        // The element that was given to us to fix if scrolled above the top of
        // the page.
        var target = base.$el;

        var position;

        // The offset top of the element when resetScroll was called. This is
        // used to determine if we have scrolled past the top of the element.
        var offsetTop = 0;

        // The offset left of the element when resetScroll was called. This is
        // used to move the element left or right relative to the horizontal
        // scroll.
        var offsetLeft = 0;
        var originalOffsetLeft = -1;

        // This last offset used to move the element horizontally. This is used
        // to determine if we need to move the element because we would not want
        // to do that for no reason.
        var lastOffsetLeft = -1;

        // This is the element used to fill the void left by the target element
        // when it goes fixed; otherwise, everything below it moves up the page.
        var spacer = null;

        var spacerClass;

        // Capture the original offsets for the target element. This needs to be
        // called whenever the page size changes or when the page is first
        // scrolled. For some reason, calling this before the page is first
        // scrolled causes the element to become fixed too late.
        function resetScroll() {
            // Set the element to it original positioning.
            target.trigger('preUnfixed');
            setUnfixed();
            target.trigger('unfixed');

            // Reset the last offset used to determine if the page has moved
            // horizontally.
            lastOffsetLeft = -1;

            // Capture the offset top of the target element.
            offsetTop = target.offset().top;

            // Capture the offset left of the target element.
            offsetLeft = target.offset().left;

            // If the offsets option is on, alter the left offset.
            if (base.options.offsets) {
                offsetLeft += (target.offset().left - target.position().left);
            }

            if (originalOffsetLeft == -1) {
                originalOffsetLeft = offsetLeft;
            }

            position = target.css('position');

            // Set that this has been called at least once.
            isReset = true;

            if (base.options.bottom != -1) {
                target.trigger('preFixed');
                setFixed();
                target.trigger('fixed');
            }
        }

        function getLimit() {
            var limit = base.options.limit;
            if (!limit) return 0;

            if (typeof(limit) === 'function') {
                return limit();
            }
            return limit;
        }

        // Returns whether the target element is fixed or not.
        function isFixed() {
            return position === 'fixed';
        }

        // Returns whether the target element is absolute or not.
        function isAbsolute() {
            return position === 'absolute';
        }

        function isUnfixed() {
            return !(isFixed() || isAbsolute());
        }

        // Sets the target element to fixed. Also, sets the spacer to fill the
        // void left by the target element.
        function setFixed() {
            // Only fix the target element and the spacer if we need to.
            if (!isFixed()) {
                // Set the spacer to fill the height and width of the target
                // element, then display it.
                spacer.css({
                    'display' : target.css('display'),
                    'width' : target.outerWidth(true),
                    'height' : target.outerHeight(true),
                    'float' : target.css('float')
                });

                // Set the target element to fixed and set its width so it does
                // not fill the rest of the page horizontally. Also, set its top
                // to the margin top specified in the options.
                target.css({
                    'width' : target.width(),
                    'position' : 'fixed',
                    'top' : base.options.bottom == -1?getMarginTop():'',
                    'bottom' : base.options.bottom == -1?'':base.options.bottom
                });

                position = 'fixed';
            }
        }

        function setAbsolute() {
            target.css({
                'width' : target.width(),
                'position' : 'absolute',
                'top' : getLimit(),
                'left' : offsetLeft
            });

            position = 'absolute';
        }

        // Sets the target element back to unfixed. Also, hides the spacer.
        function setUnfixed() {
            // Only unfix the target element and the spacer if we need to.
            if (!isUnfixed()) {
                lastOffsetLeft = -1;

                // Hide the spacer now that the target element will fill the
                // space.
                spacer.css('display', 'none');

                // Remove the style attributes that were added to the target.
                // This will reverse the target back to the its original style.
                target.css({
                    'width' : '',
                    'position' : '',
                    'left' : '',
                    'top' : ''
                });

                position = null;
            }
        }

        // Moves the target element left or right relative to the horizontal
        // scroll position.
        function setLeft(x) {
            // Only if the scroll is not what it was last time we did this.
            if (x != lastOffsetLeft) {
                // Move the target element horizontally relative to its original
                // horizontal position.
                target.css('left', offsetLeft - x);

                // Hold the last horizontal position set.
                lastOffsetLeft = x;
            }
        }

        function getMarginTop() {
            return base.options.marginTop;
        }

        // Checks to see if we need to do something based on new scroll position
        // of the page.
        function checkScroll() {
            var wasReset = isReset;

            // If resetScroll has not yet been called, call it. This only
            // happens once.
            if (!isReset) {
                resetScroll();
            }

            // Grab the current horizontal scroll position.
            var x = $(window).scrollLeft();

            // Grab the current vertical scroll position.
            var y = $(window).scrollTop();

            // Get the limit, if there is one.
            var limit = getLimit();

            // If the vertical scroll position, plus the optional margin, would
            // put the target element at the specified limit, set the target
            // element to absolute.
            if (base.options.minWidth && $(window).width() < base.options.minWidth) {
                if (!isUnfixed() || !wasReset) {
                    postPosition();
                    target.trigger('preUnfixed');
                    setUnfixed();
                    target.trigger('unfixed');
                }
            } else if (base.options.bottom == -1) {
                // If the vertical scroll position, plus the optional margin, would
                // put the target element at the specified limit, set the target
                // element to absolute.
                if (limit > 0 && y >= limit - getMarginTop()) {
                    if (!isAbsolute() || !wasReset) {
                        postPosition();
                        target.trigger('preAbsolute');
                        setAbsolute();
                        target.trigger('unfixed');
                    }
                // If the vertical scroll position, plus the optional margin, would
                // put the target element above the top of the page, set the target
                // element to fixed.
                } else if (y >= offsetTop - getMarginTop()) {
                    if (!isFixed() || !wasReset) {
                        postPosition();
                        target.trigger('preFixed');

                        // Set the target element to fixed.
                        setFixed();

                        // Reset the last offset left because we just went fixed.
                        lastOffsetLeft = -1;

                        target.trigger('fixed');
                    }
                    // If the page has been scrolled horizontally as well, move the
                    // target element accordingly.
                    setLeft(x);
                } else {
                    // Set the target element to unfixed, placing it where it was
                    // before.
                    if (!isUnfixed() || !wasReset) {
                        postPosition();
                        target.trigger('preUnfixed');
                        setUnfixed();
                        target.trigger('unfixed');
                    }
                }
            } else {
                if (limit > 0) {
                    if (y + $(window).height() - target.outerHeight(true) >= limit - getMarginTop()) {
                        if (isFixed()) {
                            postPosition();
                            target.trigger('preUnfixed');
                            setUnfixed();
                            target.trigger('unfixed');
                        }
                    } else {
                        if (!isFixed()) {
                            postPosition();
                            target.trigger('preFixed');
                            setFixed();
                        }
                        setLeft(x);
                        target.trigger('fixed');
                    }
                } else {
                    setLeft(x);
                }
            }
        }

        function postPosition() {
            var position = target.css('position');

            if (position == 'absolute') {
                target.trigger('postAbsolute');
            } else if (position == 'fixed') {
                target.trigger('postFixed');
            } else {
                target.trigger('postUnfixed');
            }
        }

        var windowResize = function(event) {
            // Check if the element is visible before updating it's position, which
            // improves behavior with responsive designs where this element is hidden.
            if(target.is(':visible')) {
                isReset = false;
                checkScroll();
			}
        }

        var windowScroll = function(event) {
            checkScroll();
        }

        // Initializes this plugin. Captures the options passed in, turns this
        // off for iOS, adds the spacer, and binds to the window scroll and
        // resize events.
        base.init = function() {
            // Capture the options for this plugin.
            base.options = $
                    .extend({}, $.ScrollToFixed.defaultOptions, options);

            // Turn off this functionality for iOS devices until we figure out
            // what to do with them, or until iOS5 comes out which is supposed
            // to support position:fixed.
            if (navigator.platform === 'iPad' || navigator.platform === 'iPhone' || navigator.platform === 'iPod') {
                if (!navigator.userAgent.match(/OS 5_.*\ like Mac OS X/i)) {
                    return;
                }
            }

            // Put the target element on top of everything that could be below
            // it. This reduces flicker when the target element is transitioning
            // to fixed.
            base.$el.css('z-index', base.options.zIndex);

            // Create a spacer element to fill the void left by the target
            // element when it goes fixed.
            spacer = $('<div />');

            position = target.css('position');

            // Place the spacer right after the target element.
            if (isUnfixed()) base.$el.after(spacer);

            // Reset the target element offsets when the window is resized, then
            // check to see if we need to fix or unfix the target element.
            $(window).bind('resize.ScrollToFixed', windowResize);

            // When the window scrolls, check to see if we need to fix or unfix
            // the target element.
            $(window).bind('scroll.ScrollToFixed', windowScroll);

            if (base.options.preFixed) {
                target.bind('preFixed.ScrollToFixed', base.options.preFixed);
            }
            if (base.options.postFixed) {
                target.bind('postFixed.ScrollToFixed', base.options.postFixed);
            }
            if (base.options.preUnfixed) {
                target.bind('preUnfixed.ScrollToFixed', base.options.preUnfixed);
            }
            if (base.options.postUnfixed) {
                target.bind('postUnfixed.ScrollToFixed', base.options.postUnfixed);
            }
            if (base.options.preAbsolute) {
                target.bind('preAbsolute.ScrollToFixed', base.options.preAbsolute);
            }
            if (base.options.postAbsolute) {
                target.bind('postAbsolute.ScrollToFixed', base.options.postAbsolute);
            }
            if (base.options.fixed) {
                target.bind('fixed.ScrollToFixed', base.options.fixed);
            }
            if (base.options.unfixed) {
                target.bind('unfixed.ScrollToFixed', base.options.unfixed);
            }

            if (base.options.spacerClass) {
                spacer.addClass(base.options.spacerClass);
            }

            target.bind('resize.ScrollToFixed', function() {
                spacer.height(target.height());
            });

            target.bind('scroll.ScrollToFixed', function() {
                target.trigger('preUnfixed');
                setUnfixed();
                target.trigger('unfixed');
                checkScroll();
            });

            target.bind('remove.ScrollToFixed', function() {
                target.trigger('preUnfixed');
                setUnfixed();
                target.trigger('unfixed');

                $(window).unbind('resize.ScrollToFixed', windowResize);
                $(window).unbind('scroll.ScrollToFixed', windowScroll);

                target.unbind('.ScrollToFixed');
                base.$el.removeData('ScrollToFixed');
            });

            // Reset everything.
            windowResize();
        };

        // Initialize the plugin.
        base.init();
    };

    // Sets the option defaults.
    $.ScrollToFixed.defaultOptions = {
        marginTop : 0,
        limit : 0,
        bottom : -1,
        zIndex : 1000
    };

    // Returns enhanced elements that will fix to the top of the page when the
    // page is scrolled.
    $.fn.scrollToFixed = function(options) {
        return this.each(function() {
            (new $.ScrollToFixed(this, options));
        });
    };
})(jQuery);<commit_msg>does not checkScroll() when plugin is already removed from element<commit_after>(function($) {
    $.isScrollToFixed = function(el) {
        return $(el).data('ScrollToFixed') !== undefined;
    };

    $.ScrollToFixed = function(el, options) {
        // To avoid scope issues, use 'base' instead of 'this' to reference this
        // class from internal events and functions.
        var base = this;

        // Access to jQuery and DOM versions of element.
        base.$el = $(el);
        base.el = el;

        // Add a reverse reference to the DOM object.
        base.$el.data('ScrollToFixed', base);

        // A flag so we know if the scroll has been reset.
        var isReset = false;

        // The element that was given to us to fix if scrolled above the top of
        // the page.
        var target = base.$el;

        var position;

        // The offset top of the element when resetScroll was called. This is
        // used to determine if we have scrolled past the top of the element.
        var offsetTop = 0;

        // The offset left of the element when resetScroll was called. This is
        // used to move the element left or right relative to the horizontal
        // scroll.
        var offsetLeft = 0;
        var originalOffsetLeft = -1;

        // This last offset used to move the element horizontally. This is used
        // to determine if we need to move the element because we would not want
        // to do that for no reason.
        var lastOffsetLeft = -1;

        // This is the element used to fill the void left by the target element
        // when it goes fixed; otherwise, everything below it moves up the page.
        var spacer = null;

        var spacerClass;

        // Capture the original offsets for the target element. This needs to be
        // called whenever the page size changes or when the page is first
        // scrolled. For some reason, calling this before the page is first
        // scrolled causes the element to become fixed too late.
        function resetScroll() {
            // Set the element to it original positioning.
            target.trigger('preUnfixed');
            setUnfixed();
            target.trigger('unfixed');

            // Reset the last offset used to determine if the page has moved
            // horizontally.
            lastOffsetLeft = -1;

            // Capture the offset top of the target element.
            offsetTop = target.offset().top;

            // Capture the offset left of the target element.
            offsetLeft = target.offset().left;

            // If the offsets option is on, alter the left offset.
            if (base.options.offsets) {
                offsetLeft += (target.offset().left - target.position().left);
            }

            if (originalOffsetLeft == -1) {
                originalOffsetLeft = offsetLeft;
            }

            position = target.css('position');

            // Set that this has been called at least once.
            isReset = true;

            if (base.options.bottom != -1) {
                target.trigger('preFixed');
                setFixed();
                target.trigger('fixed');
            }
        }

        function getLimit() {
            var limit = base.options.limit;
            if (!limit) return 0;

            if (typeof(limit) === 'function') {
                return limit();
            }
            return limit;
        }

        // Returns whether the target element is fixed or not.
        function isFixed() {
            return position === 'fixed';
        }

        // Returns whether the target element is absolute or not.
        function isAbsolute() {
            return position === 'absolute';
        }

        function isUnfixed() {
            return !(isFixed() || isAbsolute());
        }

        // Sets the target element to fixed. Also, sets the spacer to fill the
        // void left by the target element.
        function setFixed() {
            // Only fix the target element and the spacer if we need to.
            if (!isFixed()) {
                // Set the spacer to fill the height and width of the target
                // element, then display it.
                spacer.css({
                    'display' : target.css('display'),
                    'width' : target.outerWidth(true),
                    'height' : target.outerHeight(true),
                    'float' : target.css('float')
                });

                // Set the target element to fixed and set its width so it does
                // not fill the rest of the page horizontally. Also, set its top
                // to the margin top specified in the options.
                target.css({
                    'width' : target.width(),
                    'position' : 'fixed',
                    'top' : base.options.bottom == -1?getMarginTop():'',
                    'bottom' : base.options.bottom == -1?'':base.options.bottom
                });

                position = 'fixed';
            }
        }

        function setAbsolute() {
            target.css({
                'width' : target.width(),
                'position' : 'absolute',
                'top' : getLimit(),
                'left' : offsetLeft
            });

            position = 'absolute';
        }

        // Sets the target element back to unfixed. Also, hides the spacer.
        function setUnfixed() {
            // Only unfix the target element and the spacer if we need to.
            if (!isUnfixed()) {
                lastOffsetLeft = -1;

                // Hide the spacer now that the target element will fill the
                // space.
                spacer.css('display', 'none');

                // Remove the style attributes that were added to the target.
                // This will reverse the target back to the its original style.
                target.css({
                    'width' : '',
                    'position' : '',
                    'left' : '',
                    'top' : ''
                });

                position = null;
            }
        }

        // Moves the target element left or right relative to the horizontal
        // scroll position.
        function setLeft(x) {
            // Only if the scroll is not what it was last time we did this.
            if (x != lastOffsetLeft) {
                // Move the target element horizontally relative to its original
                // horizontal position.
                target.css('left', offsetLeft - x);

                // Hold the last horizontal position set.
                lastOffsetLeft = x;
            }
        }

        function getMarginTop() {
            return base.options.marginTop;
        }

        // Checks to see if we need to do something based on new scroll position
        // of the page.
        function checkScroll() {
            if (!$.isScrollToFixed(target)) return;
            var wasReset = isReset;

            // If resetScroll has not yet been called, call it. This only
            // happens once.
            if (!isReset) {
                resetScroll();
            }

            // Grab the current horizontal scroll position.
            var x = $(window).scrollLeft();

            // Grab the current vertical scroll position.
            var y = $(window).scrollTop();

            // Get the limit, if there is one.
            var limit = getLimit();

            // If the vertical scroll position, plus the optional margin, would
            // put the target element at the specified limit, set the target
            // element to absolute.
            if (base.options.minWidth && $(window).width() < base.options.minWidth) {
                if (!isUnfixed() || !wasReset) {
                    postPosition();
                    target.trigger('preUnfixed');
                    setUnfixed();
                    target.trigger('unfixed');
                }
            } else if (base.options.bottom == -1) {
                // If the vertical scroll position, plus the optional margin, would
                // put the target element at the specified limit, set the target
                // element to absolute.
                if (limit > 0 && y >= limit - getMarginTop()) {
                    if (!isAbsolute() || !wasReset) {
                        postPosition();
                        target.trigger('preAbsolute');
                        setAbsolute();
                        target.trigger('unfixed');
                    }
                // If the vertical scroll position, plus the optional margin, would
                // put the target element above the top of the page, set the target
                // element to fixed.
                } else if (y >= offsetTop - getMarginTop()) {
                    if (!isFixed() || !wasReset) {
                        postPosition();
                        target.trigger('preFixed');

                        // Set the target element to fixed.
                        setFixed();

                        // Reset the last offset left because we just went fixed.
                        lastOffsetLeft = -1;

                        target.trigger('fixed');
                    }
                    // If the page has been scrolled horizontally as well, move the
                    // target element accordingly.
                    setLeft(x);
                } else {
                    // Set the target element to unfixed, placing it where it was
                    // before.
                    if (!isUnfixed() || !wasReset) {
                        postPosition();
                        target.trigger('preUnfixed');
                        setUnfixed();
                        target.trigger('unfixed');
                    }
                }
            } else {
                if (limit > 0) {
                    if (y + $(window).height() - target.outerHeight(true) >= limit - getMarginTop()) {
                        if (isFixed()) {
                            postPosition();
                            target.trigger('preUnfixed');
                            setUnfixed();
                            target.trigger('unfixed');
                        }
                    } else {
                        if (!isFixed()) {
                            postPosition();
                            target.trigger('preFixed');
                            setFixed();
                        }
                        setLeft(x);
                        target.trigger('fixed');
                    }
                } else {
                    setLeft(x);
                }
            }
        }

        function postPosition() {
            var position = target.css('position');

            if (position == 'absolute') {
                target.trigger('postAbsolute');
            } else if (position == 'fixed') {
                target.trigger('postFixed');
            } else {
                target.trigger('postUnfixed');
            }
        }

        var windowResize = function(event) {
            // Check if the element is visible before updating it's position, which
            // improves behavior with responsive designs where this element is hidden.
            if(target.is(':visible')) {
                isReset = false;
                checkScroll();
			}
        }

        var windowScroll = function(event) {
            checkScroll();
        }

        // Initializes this plugin. Captures the options passed in, turns this
        // off for iOS, adds the spacer, and binds to the window scroll and
        // resize events.
        base.init = function() {
            // Capture the options for this plugin.
            base.options = $
                    .extend({}, $.ScrollToFixed.defaultOptions, options);

            // Turn off this functionality for iOS devices until we figure out
            // what to do with them, or until iOS5 comes out which is supposed
            // to support position:fixed.
            if (navigator.platform === 'iPad' || navigator.platform === 'iPhone' || navigator.platform === 'iPod') {
                if (!navigator.userAgent.match(/OS 5_.*\ like Mac OS X/i)) {
                    return;
                }
            }

            // Put the target element on top of everything that could be below
            // it. This reduces flicker when the target element is transitioning
            // to fixed.
            base.$el.css('z-index', base.options.zIndex);

            // Create a spacer element to fill the void left by the target
            // element when it goes fixed.
            spacer = $('<div />');

            position = target.css('position');

            // Place the spacer right after the target element.
            if (isUnfixed()) base.$el.after(spacer);

            // Reset the target element offsets when the window is resized, then
            // check to see if we need to fix or unfix the target element.
            $(window).bind('resize.ScrollToFixed', windowResize);

            // When the window scrolls, check to see if we need to fix or unfix
            // the target element.
            $(window).bind('scroll.ScrollToFixed', windowScroll);

            if (base.options.preFixed) {
                target.bind('preFixed.ScrollToFixed', base.options.preFixed);
            }
            if (base.options.postFixed) {
                target.bind('postFixed.ScrollToFixed', base.options.postFixed);
            }
            if (base.options.preUnfixed) {
                target.bind('preUnfixed.ScrollToFixed', base.options.preUnfixed);
            }
            if (base.options.postUnfixed) {
                target.bind('postUnfixed.ScrollToFixed', base.options.postUnfixed);
            }
            if (base.options.preAbsolute) {
                target.bind('preAbsolute.ScrollToFixed', base.options.preAbsolute);
            }
            if (base.options.postAbsolute) {
                target.bind('postAbsolute.ScrollToFixed', base.options.postAbsolute);
            }
            if (base.options.fixed) {
                target.bind('fixed.ScrollToFixed', base.options.fixed);
            }
            if (base.options.unfixed) {
                target.bind('unfixed.ScrollToFixed', base.options.unfixed);
            }

            if (base.options.spacerClass) {
                spacer.addClass(base.options.spacerClass);
            }

            target.bind('resize.ScrollToFixed', function() {
                spacer.height(target.height());
            });

            target.bind('scroll.ScrollToFixed', function() {
                target.trigger('preUnfixed');
                setUnfixed();
                target.trigger('unfixed');
                checkScroll();
            });

            target.bind('remove.ScrollToFixed', function() {
                target.trigger('preUnfixed');
                setUnfixed();
                target.trigger('unfixed');

                $(window).unbind('resize.ScrollToFixed', windowResize);
                $(window).unbind('scroll.ScrollToFixed', windowScroll);

                target.unbind('.ScrollToFixed');
                base.$el.removeData('ScrollToFixed');
            });

            // Reset everything.
            windowResize();
        };

        // Initialize the plugin.
        base.init();
    };

    // Sets the option defaults.
    $.ScrollToFixed.defaultOptions = {
        marginTop : 0,
        limit : 0,
        bottom : -1,
        zIndex : 1000
    };

    // Returns enhanced elements that will fix to the top of the page when the
    // page is scrolled.
    $.fn.scrollToFixed = function(options) {
        return this.each(function() {
            (new $.ScrollToFixed(this, options));
        });
    };
})(jQuery);
<|endoftext|>


<commit_before>
import { Link } from "react-router";
import backgrounds from "../../util/backgrounds";
import contentHelper from "../../util/content";
import categories from "../../util/categories";

const hasImage = (content) =>
  return content.content.images.length > 0 ||
    (
      content.parent &&
      content.parent.content &&
      content.parent.content.images.length > 0
    );

// XXX right now this uses the content prop for everything
// it should less intelligent and use the other props directly
const MiniCard = ({ link, title, icon, type, images, description, content }) => (
  <Link to={contentHelper.links(content)} className="plain">

        <div className={`card__item soft push-half-ends ${hasImage(content) ? "two-thirds" : "one-whole"}`} style={{ verticalAlign: "middle" }}>

{title}

          {(() => {
            // if (!content) return;
            if (!description) return;
            return
{description}
;
          })()}

            {(() => {
              if (!content) return;
              // if (!icon) return;
              return <span className={`${categories.icon(content)} text-dark-tertiary`} />;
            })()}

            {(() => {
              if (!content) return;
              // if (!type) return;
              return <h7 className="text-dark-tertiary soft-half-left">{categories.name(content)}</h7>;
            })()}

        <div
          className="locked-ends locked-right card__image one-third background--fill"
          style={{
            verticalAlign: "middle",
            backgroundImage: `url('${backgrounds.image(content, { label: "2:1" })}')`,
          }}
        />

  </Link>
);

export default MiniCard;
<commit_msg>lint cards.MiniCard<commit_after>import PropTypes from "react";
import { Link } from "react-router";
import backgrounds from "../../util/backgrounds";
import contentHelper from "../../util/content";
import categories from "../../util/categories";

const hasImage = content =>
  content.content.images.length > 0 ||
    (
      content.parent &&
      content.parent.content &&
      content.parent.content.images.length > 0
    );

// XXX right now this uses the content prop for everything
// it should less intelligent and use the other props directly
// Unused props: icon, link, type, images
const MiniCard = ({ title, description, content }) => (
  <Link to={contentHelper.links(content)} className="plain">

      <div className={`card__item soft push-half-ends ${hasImage(content) ? "two-thirds" : "one-whole"}`} style={{ verticalAlign: "middle" }}>

{title}

        {(() => {
          // if (!content) return;
          if (!description) return undefined;
          return
{description}
;
        })()}

          {(() => {
            if (!content) return undefined;
            // if (!icon) return;
            return <span className={`${categories.icon(content)} text-dark-tertiary`} />;
          })()}

          {(() => {
            if (!content) return undefined;
            // if (!type) return;
            return <h7 className="text-dark-tertiary soft-half-left">{categories.name(content)}</h7>;
          })()}

      <div
        className="locked-ends locked-right card__image one-third background--fill"
        style={{
          verticalAlign: "middle",
          backgroundImage: `url('${backgrounds.image(content, { label: "2:1" })}')`,
        }}
      />

  </Link>
);

MiniCard.propTypes = {
  title: PropTypes.string,
  description: PropTypes.string,
  content: PropTypes.object.isRequired, // eslint-disable-line
};

export default MiniCard;
<|endoftext|>


<commit_before>/* Global variables */
var thomson = "Thomson_DCI1500GK";
var yamaha = "Yamaha_RAX23_WV50020";
var samsung = "Samsung_00084Q";
var pages = [];
var postText = "-content";

// TV varibales
var activeTV = 0; // Active favorite
var favTV; // External favorites list
var linesTV = []; // Internal favorites list

// Radio varibales
var activeRadio = 0; // Active favorite
var favRadio; // External favorites list
var linesRadio = []; // Internal favorites list

// Check Bootstrap state
var size = findBootstrapEnvironment();

// Hide Jquery Mobile loading message
$.mobile.loading().hide();

/* General Scripts */
$('.remote-screen-swipe').each(function() {
  var id = this.id;
  pages.push(id.substr(0,id.length-postText.length));
});

$(".remote-screen a").click(function(event) {
  $.ajax({
    url: $(this).attr("href"),
  });
  event.preventDefault();
  return true;
});

function sendKey(remote_name, key_name) {
  $.ajax({
    url: "/send/"+remote_name+"/"+key_name,
  });
}

/* Top Navigation */
$(".nav-btn").click(function(event) {
  $(".remote-screen").hide();
  var code = $(this).attr('id');
  $("#"+code+"-content").show();
});

/* Multi-Buttons */
$("#"+yamaha+"-KEY_VOLUMEUP-8x").click(function() {
  for (var i = 8; i > 0; i--) {
    sendKey(yamaha,"KEY_VOLUMEUP");
  };
});

$("#"+yamaha+"-KEY_VOLUMEDOWN-8x").click(function() {
  for (var i = 8; i > 0; i--) {
    sendKey(yamaha,"KEY_VOLUMEDOWN");
  };
});

$("#ALLDEVICES-KEY_POWER-ON").click(function() {
  sendKey(thomson,"KEY_POWER");
  setTimeout(function(){sendKey(samsung,"KEY_POWER");}, 2000);
  setTimeout(function(){sendKey(yamaha,"KEY_POWER");}, 4000);
});

$("#ALLDEVICES-KEY_POWER-OFF").click(function() {
  sendKey(yamaha,"KEY_POWER");
  setTimeout(function(){sendKey(samsung,"KEY_POWER");}, 2000);
  setTimeout(function(){sendKey(thomson,"KEY_POWER");}, 4000);
});

/* Favorites List & Buttons */
$(document).ready(function() {
  // Get TV favs
  $.ajax({
    type: "GET",
    url: "favs.txt",
    dataType: "text",
    success: function(data) {
      favTV = data;
      processData(favTV,true);
    }
  });

  // Get Radio favs
  $.ajax({
    type: "GET",
    url: "favs-radio.txt",
    dataType: "text",
    success: function(data) {
      favRadio = data;
      processData(favRadio,false);
    }
  });
});

function processData(data,tv) {
  // Setup variables
  var allTextLines = data.split(/\r\n|\n/);
  var lines = [];

  // Read fav file content (skip first line with headers)
  for (var i=0; i<allTextLines.length; i++) {
    if (allTextLines[i]!="" && allTextLines[i][0]!="#") { // Ignore comments and emtpy
      var data = allTextLines[i].split(',');
      lines.push(data);
    }
  }

  if(tv)
    linesTV = lines;
  else
    linesRadio = lines;

  // Populate favriable-list container
  for (var i=0; i<lines.length; i++) {
    // Highlight first TV button
    var btnType = "btn-default";
    if (i==0 && tv) btnType = "btn-primary";

    // Create HTML code for fav button
    var code;
    if (tv)
      code = "TV";
    else
      code = "RA";

    var buttonHTML = "";
    // Add image if URL existent
    if (lines[i].length == 3) {
      buttonHTML = buttonHTML.concat("");
    };
    buttonHTML = buttonHTML.concat(lines[i][0] // Channel Name
      +"");

    // Append a button for favorite
    var appendTo;
    if (tv)
      appendTo = "#favorites-list";
    else
      appendTo = "#favorites-list-radio";
    $(appendTo).append(buttonHTML);

    // Append spacer on every second button if Bootstrap is xsmall
    if(!(size=="xs" && i%2!=0))
      $(appendTo).append("

");
  };

  // Populate placeholder text for editing favorites
  if (tv)
    $("#fav-list-edit").append(favTV);
  else
    $("#fav-list-radio-edit").append(favRadio);

  // Activate favorite buttons
  $("."+code).click(function() {
    var id = $(this).attr('id');
    var tv = true;
    if (id.substr(0,2) != "TV")
      tv = false;
    changeFavorite(id.substr(2),tv);
  });
}

// React to favorite saved changes
$("#save-button").click(function(){
  $.ajax({
    type: "post",
    url: "/save",
    data: {
      "tv":$("textarea#fav-list-edit").val(),
      "rad":$("textarea#fav-list-radio-edit").val(),
    },
    success: function(){
      // Reload
      location.reload(true);
    },
    error: function(){
      alert("ERROR: Could not save changes.");
    }
  });
});

// Favorites buttons logic and next function
$("#NEXT_FAVORITE").click(function() {
  changeFavorite(activeTV+1,true);
});

$("#LAST_FAVORITE").click(function() {
  changeFavorite(activeTV-1,true);
});

/* Change active favorite button and send digits */
function changeFavorite(listNumber,tv) {
  // Define mode
  var lines, fav, active;
  if (tv) {
    lines = linesTV;
    fav = "TV";
    active = activeTV;
  }
  else {
    lines = linesRadio;
    fav = "RA";
    active = activeRadio;
  }

  // Ensure active remains integer
  var num = parseInt(listNumber);

  // Continuously run through favs
  if (num < 0) num = lines.length - 1;
  if (num == lines.length) num = 0;

  // Change buttons and active
  $("#"+fav+active).removeClass("btn-primary").addClass("btn-default");
  active = num;
  $("#"+fav+active).removeClass("btn-default").addClass("btn-primary");

  // Send each digit of active
  var channelNumber = String(lines[active][1]);
  i = 0;
  function sendDigit() {
    sendKey(thomson,"KEY_"+channelNumber[i]);
    setTimeout(function() {
      i++;
      if (i < channelNumber.length)
        sendDigit(); // recursive
      else
        i=0;
    }, 200); // 200ms wait between digits
  };
  sendDigit();

  if (tv)
    activeTV = active;
  else
    activeRadio = active;
}

/* Help function to detect bootstrap change */
function findBootstrapEnvironment() {
  var envs = ["xs", "sm", "md", "lg"],
      doc = window.document,
      temp = doc.createElement("div");

  doc.body.appendChild(temp);

  for (var i = envs.length - 1; i >= 0; i--) {
      var env = envs[i];

      temp.className = "hidden-" + env;

      if (temp.offsetParent === null) {
          doc.body.removeChild(temp);
          return env;
      }
  }
  return "";
}

/* Enable swipe actions */
$("body").on("swipeleft", function() {
  var activePage = findActivePage();
  var newPage = activePage + 1;
  if (newPage < pages.length)
    showNewPage(newPage);
});

$("body").on("swiperight", function() {
  var activePage = findActivePage();
  var newPage = activePage - 1;
  if (newPage > -1)
    showNewPage(newPage);
});

$("#edit-fav-content").on('swipeleft swiperight', function(e){});

function findActivePage() {
  var code;
  $(".remote-screen").each(function() {
    if ($(this).is(":visible"))
      code = $(this).attr('id');
  });
  code = code.substr(0,code.length-postText.length);
  return pages.indexOf(code);
};

function showNewPage(i) {
  $(".remote-screen").hide();
  $("#"+pages[i]+"-content").show();
};

$.event.special.swipe.horizontalDistanceThreshold = (screen.availWidth) / 10;<commit_msg>loading issue 3<commit_after>/* Global variables */
var thomson = "Thomson_DCI1500GK";
var yamaha = "Yamaha_RAX23_WV50020";
var samsung = "Samsung_00084Q";
var pages = [];
var postText = "-content";

// TV varibales
var activeTV = 0; // Active favorite
var favTV; // External favorites list
var linesTV = []; // Internal favorites list

// Radio varibales
var activeRadio = 0; // Active favorite
var favRadio; // External favorites list
var linesRadio = []; // Internal favorites list

// Check Bootstrap state
var size = findBootstrapEnvironment();

// Hide Jquery Mobile loading message
//$.mobile.loading().hide();

/* General Scripts */
$('.remote-screen-swipe').each(function() {
  var id = this.id;
  pages.push(id.substr(0,id.length-postText.length));
});

$(".remote-screen a").click(function(event) {
  $.ajax({
    url: $(this).attr("href"),
  });
  event.preventDefault();
  return true;
});

function sendKey(remote_name, key_name) {
  $.ajax({
    url: "/send/"+remote_name+"/"+key_name,
  });
}

/* Top Navigation */
$(".nav-btn").click(function(event) {
  $(".remote-screen").hide();
  var code = $(this).attr('id');
  $("#"+code+"-content").show();
});

/* Multi-Buttons */
$("#"+yamaha+"-KEY_VOLUMEUP-8x").click(function() {
  for (var i = 8; i > 0; i--) {
    sendKey(yamaha,"KEY_VOLUMEUP");
  };
});

$("#"+yamaha+"-KEY_VOLUMEDOWN-8x").click(function() {
  for (var i = 8; i > 0; i--) {
    sendKey(yamaha,"KEY_VOLUMEDOWN");
  };
});

$("#ALLDEVICES-KEY_POWER-ON").click(function() {
  sendKey(thomson,"KEY_POWER");
  setTimeout(function(){sendKey(samsung,"KEY_POWER");}, 2000);
  setTimeout(function(){sendKey(yamaha,"KEY_POWER");}, 4000);
});

$("#ALLDEVICES-KEY_POWER-OFF").click(function() {
  sendKey(yamaha,"KEY_POWER");
  setTimeout(function(){sendKey(samsung,"KEY_POWER");}, 2000);
  setTimeout(function(){sendKey(thomson,"KEY_POWER");}, 4000);
});

/* Favorites List & Buttons */
$(document).ready(function() {
  // Get TV favs
  $.ajax({
    type: "GET",
    url: "favs.txt",
    dataType: "text",
    success: function(data) {
      favTV = data;
      processData(favTV,true);
    }
  });

  // Get Radio favs
  $.ajax({
    type: "GET",
    url: "favs-radio.txt",
    dataType: "text",
    success: function(data) {
      favRadio = data;
      processData(favRadio,false);
    }
  });
});

function processData(data,tv) {
  // Setup variables
  var allTextLines = data.split(/\r\n|\n/);
  var lines = [];

  // Read fav file content (skip first line with headers)
  for (var i=0; i<allTextLines.length; i++) {
    if (allTextLines[i]!="" && allTextLines[i][0]!="#") { // Ignore comments and emtpy
      var data = allTextLines[i].split(',');
      lines.push(data);
    }
  }

  if(tv)
    linesTV = lines;
  else
    linesRadio = lines;

  // Populate favriable-list container
  for (var i=0; i<lines.length; i++) {
    // Highlight first TV button
    var btnType = "btn-default";
    if (i==0 && tv) btnType = "btn-primary";

    // Create HTML code for fav button
    var code;
    if (tv)
      code = "TV";
    else
      code = "RA";

    var buttonHTML = "";
    // Add image if URL existent
    if (lines[i].length == 3) {
      buttonHTML = buttonHTML.concat("");
    };
    buttonHTML = buttonHTML.concat(lines[i][0] // Channel Name
      +"");

    // Append a button for favorite
    var appendTo;
    if (tv)
      appendTo = "#favorites-list";
    else
      appendTo = "#favorites-list-radio";
    $(appendTo).append(buttonHTML);

    // Append spacer on every second button if Bootstrap is xsmall
    if(!(size=="xs" && i%2!=0))
      $(appendTo).append("

");
  };

  // Populate placeholder text for editing favorites
  if (tv)
    $("#fav-list-edit").append(favTV);
  else
    $("#fav-list-radio-edit").append(favRadio);

  // Activate favorite buttons
  $("."+code).click(function() {
    var id = $(this).attr('id');
    var tv = true;
    if (id.substr(0,2) != "TV")
      tv = false;
    changeFavorite(id.substr(2),tv);
  });
}

// React to favorite saved changes
$("#save-button").click(function(){
  $.ajax({
    type: "post",
    url: "/save",
    data: {
      "tv":$("textarea#fav-list-edit").val(),
      "rad":$("textarea#fav-list-radio-edit").val(),
    },
    success: function(){
      // Reload
      location.reload(true);
    },
    error: function(){
      alert("ERROR: Could not save changes.");
    }
  });
});

// Favorites buttons logic and next function
$("#NEXT_FAVORITE").click(function() {
  changeFavorite(activeTV+1,true);
});

$("#LAST_FAVORITE").click(function() {
  changeFavorite(activeTV-1,true);
});

/* Change active favorite button and send digits */
function changeFavorite(listNumber,tv) {
  // Define mode
  var lines, fav, active;
  if (tv) {
    lines = linesTV;
    fav = "TV";
    active = activeTV;
  }
  else {
    lines = linesRadio;
    fav = "RA";
    active = activeRadio;
  }

  // Ensure active remains integer
  var num = parseInt(listNumber);

  // Continuously run through favs
  if (num < 0) num = lines.length - 1;
  if (num == lines.length) num = 0;

  // Change buttons and active
  $("#"+fav+active).removeClass("btn-primary").addClass("btn-default");
  active = num;
  $("#"+fav+active).removeClass("btn-default").addClass("btn-primary");

  // Send each digit of active
  var channelNumber = String(lines[active][1]);
  i = 0;
  function sendDigit() {
    sendKey(thomson,"KEY_"+channelNumber[i]);
    setTimeout(function() {
      i++;
      if (i < channelNumber.length)
        sendDigit(); // recursive
      else
        i=0;
    }, 200); // 200ms wait between digits
  };
  sendDigit();

  if (tv)
    activeTV = active;
  else
    activeRadio = active;
}

/* Help function to detect bootstrap change */
function findBootstrapEnvironment() {
  var envs = ["xs", "sm", "md", "lg"],
      doc = window.document,
      temp = doc.createElement("div");

  doc.body.appendChild(temp);

  for (var i = envs.length - 1; i >= 0; i--) {
      var env = envs[i];

      temp.className = "hidden-" + env;

      if (temp.offsetParent === null) {
          doc.body.removeChild(temp);
          return env;
      }
  }
  return "";
}

/* Enable swipe actions */
$("body").on("swipeleft", function() {
  var activePage = findActivePage();
  var newPage = activePage + 1;
  if (newPage < pages.length)
    showNewPage(newPage);
});

$("body").on("swiperight", function() {
  var activePage = findActivePage();
  var newPage = activePage - 1;
  if (newPage > -1)
    showNewPage(newPage);
});

$("#edit-fav-content").on('swipeleft swiperight', function(e){});

function findActivePage() {
  var code;
  $(".remote-screen").each(function() {
    if ($(this).is(":visible"))
      code = $(this).attr('id');
  });
  code = code.substr(0,code.length-postText.length);
  return pages.indexOf(code);
};

function showNewPage(i) {
  $(".remote-screen").hide();
  $("#"+pages[i]+"-content").show();
};

$.event.special.swipe.horizontalDistanceThreshold = (screen.availWidth) / 10;<|endoftext|>