<commit_before>import React, { Component } from 'react';
import { Linking, Alert, TouchableOpacity, Text, View, Image, TextInput, ListView, TouchableHighlight } from 'react-native';
import helpers from '../helpers/helpers.js';
import styles from '../styles.ios.js';
import AddRecipe from './AddRecipe.ios.js';
import ShareFacebook from './ShareFacebook.ios.js';
import { connect } from "react-redux";

class Recipe extends Component {
  constructor(props) {
    super(props);
    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    this.state = {
      steps: ds.cloneWithRows(props.recipe.analyzedInstructions[0].steps),
      ingredients: []
    }
  }

  componentWillMount() {
    this.renderIngredients();
    this.renderOptions();
  }

  goBack() {
    this.props.navigator.pop();
  }

  renderIngredients() {
    let ingredients = [];
    for (let ingredient of this.props.recipe.extendedIngredients) {
      ingredients.push(ingredient.originalString);
    }
    this.setState({
      ingredients: ingredients
    })
  }

  renderOptions() {
    const recipe = this.props.recipe;
    recipe.vegan = recipe.vegan ? '∙ Vegan' : false;
    recipe.glutenFree = recipe.glutenFree ? '∙ Gluten Free' : false;
    recipe.dairyFree = recipe.dairyFree ? '∙ Dairy Free' : false;
    if (!recipe.vegan && !recipe.glutenFree && !recipe.dairyFree) {
      recipe.dairyFree = 'No Dietary Restrictions';
    }
    recipe.cheap = recipe.cheap ? '$' : '$$$';
    recipe.sustainable = recipe.sustainable ? '∙ Recipe Sustainable' : false;
    recipe.servings = recipe.servings ? recipe.servings : 'Not available';
    recipe.healthscore = recipe.healthscore ? recipe.healthscore : 'Not available';
    // recipe.sourceUrl
  }

  //DRY click handler for social media deep linking
  shareToSocialMedia(deepLink, appName, storeURL) {
    //deep link into the provided app if installed, else direct user to the proper app store link
    Linking.canOpenURL(appName.toLowerCase() + '://').then(supported => {
      if (!supported) {
        Alert.alert(`You must install the ${appName} app in order to use this feature.`,
                    '',
                    [{text: `Install ${appName}`, onPress: () => Linking.openURL(storeURL)},
                     {text: 'Not Now'}]
        );
      } else {
        return Linking.openURL(deepLink);
      }
    }).catch(err => /*Alert.alert('Error; ', err), */console.error('Error: ', err));

  shareToInstagram() {
    //deep link into the instagram app if installed
    Linking.canOpenURL('instagram://camera').then(supported => {
      if (!supported) {
        Alert.alert('You must install the Instagram app in order to use this feature.',
                    '',
                    [{text: 'Install Instagram', onPress: () => Linking.openURL('https://itunes.apple.com/us/app/instagram/id389801252?mt=8')},
                     {text: 'Not Now'}]
        );
      } else {
        return Linking.openURL('instagram://camera');
      }
    }).catch(err => console.error('An error occurred', err));
  }

  shareToTwitter() {
    //deep link into the twitter app if installed
    Linking.canOpenURL('twitter://camera').then(supported => {
      if (!supported) {
        Alert.alert('You must install the Twitter app in order to use this feature.',
                    '',
                    [{text: 'Install Twitter', onPress: () => Linking.openURL('https://itunes.apple.com/us/app/twitter/id333903271?mt=8')},
                     {text: 'Not Now'}]
        );
      } else {
        return Linking.openURL(`twitter://post?message=I%20just%20made%20${this.props.food.split(' ').join('%20')}%20with%20a%20little%20help%20from%20the%20app%20Snacktime!`);
      }
    }).catch(err => console.error('An error occurred', err));
  }

  render() {
    console.log('this is the state for user', this.props.state.payload)
    const ingredients = this.state.ingredients.map((ingredient, i) => {
      return (
        <Text style = {styles.ingredientListText2} key={i}>∙ {ingredient} </Text>
      )
    });
    const recipe = this.props.recipe;
    return (
      <View style={styles.recipe}>
        <View style={styles.resultsTitle}>
          <TouchableHighlight style={styles.backButton} onPress={this.goBack.bind(this)}>
            <Image style={styles.backButtonImage} source={{uri: 'https://cdn0.iconfinder.com/data/icons/vector-basic-tab-bar-icons/48/back_button-128.png'}} />
          </TouchableHighlight>
          <Text style={styles.resultsTitleText}>Instructions for {this.props.food}:</Text>
        </View>
        <View style={styles.ingredientContainer}>
          <View style={styles.ingredientList}>
            <Text> {recipe.vegan} {recipe.glutenFree} {recipe.dairyFree}</Text>
            <Text> {recipe.cheap} {recipe.sustainable} Servings: {recipe.servings} Healthscore: {recipe.healthscore}</Text>
            <Text style={styles.recipeTitle2}>Ingredients:</Text>
            <Text style={styles.ingredientListText2}>{ingredients}</Text>
          </View>
          <View style={styles.recipeImageAndIcons}>
            <Image source={{uri: this.props.image}} style={styles.recipeImage} />
            <View style={styles.iconsContainer}>
              {
                this.props.state.isAuthenticated
                  ? (<ShareFacebook url={this.props.recipe.sourceUrl} />)
                  : (<Text></Text>)
              }
              <TouchableOpacity onPress={
                () => this.shareToSocialMedia('instagram://camera', 'Instagram', 'https://itunes.apple.com/us/app/instagram/id389801252?mt=8')
              }>
                <Image source={require('../public/instagram_icon.png')} style={styles.shareIcons} />
              </TouchableOpacity>
              <TouchableOpacity onPress={
                () => this.shareToSocialMedia(
                  `twitter://post?message=I%20just%20made%20${this.props.food.split(' ').join('%20')}%20(${this.props.food})%20with%20a%20little%20help%20from%20the%20app%20Snacktime!

                  ${this.props.recipe.sourceUrl}`,
                  'Twitter',
                  'https://itunes.apple.com/us/app/twitter/id333903271?mt=8'
                )
              }>
                <Image source={require('../public/twitter_icon.png')} style={styles.shareIcons} />
              </TouchableOpacity>
              <Text style={styles.recipeTitle2}>Directions:</Text>
              <AddRecipe info={this.props.recipe} ingredients={this.state.ingredients} userid={this.props.state.payload}/>
            </View>
          </View>
        </View>
        <View>
          <Text style={styles.recipeTitle2}>Directions:</Text>
        </View>
        <ListView
          style={styles.recipe}
          dataSource={this.state.steps}
          renderRow={(step, i) => {
            let image = step.ingredients[0] ? step.ingredients[0].image : 'https://s3-us-west-1.amazonaws.com/filmedin/food+(1).png';
            return (
            <View
              key={i}
              style={styles.recipeStep}
              underlayColor="grey"
            >
              <Image source={{uri: image}} style={styles.recipeImage} />
              <Text style={styles.recipeStepText}>step {step.number}: {step.step}</Text>
            </View>
            )}}
        />
      </View>
    )
  }
}

export default connect(state => ({
    state: state.facebook
  }), null
)(Recipe);
<commit_msg>add a save recipe<commit_after>import React, { Component } from 'react';
import { Linking, Alert, TouchableOpacity, Text, View, Image, TextInput, ListView, TouchableHighlight } from 'react-native';
import helpers from '../helpers/helpers.js';
import styles from '../styles.ios.js';
import AddRecipe from './AddRecipe.ios.js';
import ShareFacebook from './ShareFacebook.ios.js';
import { connect } from "react-redux";

class Recipe extends Component {
  constructor(props) {
    super(props);
    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    this.state = {
      steps: ds.cloneWithRows(props.recipe.analyzedInstructions[0].steps),
      ingredients: []
    }
  }

  componentWillMount() {
    this.renderIngredients();
    this.renderOptions();
  }

  goBack() {
    this.props.navigator.pop();
  }

  renderIngredients() {
    let ingredients = [];
    for (let ingredient of this.props.recipe.extendedIngredients) {
      ingredients.push(ingredient.originalString);
    }
    this.setState({
      ingredients: ingredients
    })
  }

  renderOptions() {
    const recipe = this.props.recipe;
    recipe.vegan = recipe.vegan ? '∙ Vegan' : false;
    recipe.glutenFree = recipe.glutenFree ? '∙ Gluten Free' : false;
    recipe.dairyFree = recipe.dairyFree ? '∙ Dairy Free' : false;
    if (!recipe.vegan && !recipe.glutenFree && !recipe.dairyFree) {
      recipe.dairyFree = 'No Dietary Restrictions';
    }
    recipe.cheap = recipe.cheap ? '$' : '$$$';
    recipe.sustainable = recipe.sustainable ? '∙ Recipe Sustainable' : false;
    recipe.servings = recipe.servings ? recipe.servings : 'Not available';
    recipe.healthscore = recipe.healthscore ? recipe.healthscore : 'Not available';
    // recipe.sourceUrl
  }

  //DRY click handler for social media deep linking
  shareToSocialMedia(deepLink, appName, storeURL) {
    //deep link into the provided app if installed, else direct user to the proper app store link
    Linking.canOpenURL(appName.toLowerCase() + '://').then(supported => {
      if (!supported) {
        Alert.alert(`You must install the ${appName} app in order to use this feature.`,
                    '',
                    [{text: `Install ${appName}`, onPress: () => Linking.openURL(storeURL)},
                     {text: 'Not Now'}]
        );
      } else {
        return Linking.openURL(deepLink);
      }
    }).catch(err => /*Alert.alert('Error; ', err), */console.error('Error: ', err));

  shareToInstagram() {
    //deep link into the instagram app if installed
    Linking.canOpenURL('instagram://camera').then(supported => {
      if (!supported) {
        Alert.alert('You must install the Instagram app in order to use this feature.',
                    '',
                    [{text: 'Install Instagram', onPress: () => Linking.openURL('https://itunes.apple.com/us/app/instagram/id389801252?mt=8')},
                     {text: 'Not Now'}]
        );
      } else {
        return Linking.openURL('instagram://camera');
      }
    }).catch(err => console.error('An error occurred', err));
  }

  shareToTwitter() {
    //deep link into the twitter app if installed
    Linking.canOpenURL('twitter://camera').then(supported => {
      if (!supported) {
        Alert.alert('You must install the Twitter app in order to use this feature.',
                    '',
                    [{text: 'Install Twitter', onPress: () => Linking.openURL('https://itunes.apple.com/us/app/twitter/id333903271?mt=8')},
                     {text: 'Not Now'}]
        );
      } else {
        return Linking.openURL(`twitter://post?message=I%20just%20made%20${this.props.food.split(' ').join('%20')}%20with%20a%20little%20help%20from%20the%20app%20Snacktime!`);
      }
    }).catch(err => console.error('An error occurred', err));
  }

  render() {
    const ingredients = this.state.ingredients.map((ingredient, i) => {
      return (
        <Text style = {styles.ingredientListText2} key={i}>∙ {ingredient} </Text>
      )
    });
    const recipe = this.props.recipe;
    return (
      <View style={styles.recipe}>
        <View style={styles.resultsTitle}>
          <TouchableHighlight style={styles.backButton} onPress={this.goBack.bind(this)}>
            <Image style={styles.backButtonImage} source={{uri: 'https://cdn0.iconfinder.com/data/icons/vector-basic-tab-bar-icons/48/back_button-128.png'}} />
          </TouchableHighlight>
          <Text style={styles.resultsTitleText}>Instructions for {this.props.food}:</Text>
        </View>
        <View style={styles.ingredientContainer}>
          <View style={styles.ingredientList}>
            <Text> {recipe.vegan} {recipe.glutenFree} {recipe.dairyFree}</Text>
            <Text> {recipe.cheap} {recipe.sustainable} Servings: {recipe.servings} Healthscore: {recipe.healthscore}</Text>
            <Text style={styles.recipeTitle2}>Ingredients:</Text>
            <Text style={styles.ingredientListText2}>{ingredients}</Text>
          </View>
          <View style={styles.recipeImageAndIcons}>
            <Image source={{uri: this.props.image}} style={styles.recipeImage} />
            <View style={styles.iconsContainer}>
              {
                this.props.state.isAuthenticated
                  ? (<ShareFacebook url={this.props.recipe.sourceUrl} />)
                  : (<Text></Text>)
              }
              <TouchableOpacity onPress={
                () => this.shareToSocialMedia('instagram://camera', 'Instagram', 'https://itunes.apple.com/us/app/instagram/id389801252?mt=8')
              }>
                <Image source={require('../public/instagram_icon.png')} style={styles.shareIcons} />
              </TouchableOpacity>
              <TouchableOpacity onPress={
                () => this.shareToSocialMedia(
                  `twitter://post?message=I%20just%20made%20${this.props.food.split(' ').join('%20')}%20(${this.props.food})%20with%20a%20little%20help%20from%20the%20app%20Snacktime!

                  ${this.props.recipe.sourceUrl}`,
                  'Twitter',
                  'https://itunes.apple.com/us/app/twitter/id333903271?mt=8'
                )
              }>
                <Image source={require('../public/twitter_icon.png')} style={styles.shareIcons} />
              </TouchableOpacity>
              <Text style={styles.recipeTitle2}>Directions:</Text>
              <AddRecipe info={this.props.recipe} ingredients={this.state.ingredients} userid={this.props.state.payload}/>
            </View>
          </View>
        </View>
        <View>
          <Text style={styles.recipeTitle2}>Directions:</Text>
        </View>
        <ListView
          style={styles.recipe}
          dataSource={this.state.steps}
          renderRow={(step, i) => {
            let image = step.ingredients[0] ? step.ingredients[0].image : 'https://s3-us-west-1.amazonaws.com/filmedin/food+(1).png';
            return (
            <View
              key={i}
              style={styles.recipeStep}
              underlayColor="grey"
            >
              <Image source={{uri: image}} style={styles.recipeImage} />
              <Text style={styles.recipeStepText}>step {step.number}: {step.step}</Text>
            </View>
            )}}
        />
      </View>
    )
  }
}

export default connect(state => ({
    state: state.facebook
  }), null
)(Recipe);
<|endoftext|>


<commit_before>// Karma configuration
// Generated on Thu Feb 12 2015 22:12:00 GMT+0100 (CET)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine', 'requirejs'],

    // list of files / patterns to load in the browser
    files: [
      {pattern: 'lib/**/*.js', included: false},
      //{pattern: 'recipes/**/*.js', included: false},
      {pattern: 'test/**/*spec.js', included: false},

      'test-main.js'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  });
};
<commit_msg>indenting of karma config file<commit_after>/*jshint strict: false*/
/*global module*/

// Karma configuration
// Generated on Thu Feb 12 2015 22:12:00 GMT+0100 (CET)

module.exports = function (config) {
    config.set({

        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',

        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine', 'requirejs'],

        // list of files / patterns to load in the browser
        files: [
            {pattern: 'lib/**/*.js', included: false},
            //{pattern: 'recipes/**/*.js', included: false},
            {pattern: 'test/**/*spec.js', included: false},

            'test-main.js'
        ],

        // list of files to exclude
        exclude: [
        ],

        // preprocess matching files before serving them to the browser
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        preprocessors: {
        },

        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['progress'],

        // web server port
        port: 9876,

        // enable / disable colors in the output (reporters and logs)
        colors: true,

        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,

        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,

        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['PhantomJS'],

        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: false
    });
};<|endoftext|>


<commit_before>// @flow

import {CLI} from 'cli-ux'
import Plugins from 'cli-engine/lib/plugins'
import type {Config} from 'cli-engine-config'
import type {PreRun} from 'cli-engine/lib/hooks'
import {AppCompletion, PipelineCompletion, SpaceCompletion, TeamCompletion} from 'cli-engine-heroku/lib/completions'

const debug = require('debug')('heroku:completions')

async function run (config: Config, opts: PreRun) {
  try {
    if (config.windows) {
      debug('skipping autocomplete on windows')
    } else {
      const cli = new CLI()
      const plugins = await (new Plugins(config)).list()
      const acPlugin = plugins.find(p => p.name === 'heroku-cli-autocomplete')
      if (acPlugin) {
        cli.action.start('Updating completions')
        let ac = await acPlugin.findCommand('autocomplete:init')
        if (ac) await ac.run(config)
        await AppCompletion.options({config})
        await PipelineCompletion.options({config})
        await SpaceCompletion.options({config})
        await TeamCompletion.options({config})
      } else {
        debug('skipping autocomplete, not installed')
      }
      cli.action.done()
    }
  } catch (err) {
    debug(err)
  }
}

module.exports = run
<commit_msg>fix completion done<commit_after>// @flow

import cli from 'cli-ux'
import Plugins from 'cli-engine/lib/plugins'
import type {Config} from 'cli-engine-config'
import type {PreRun} from 'cli-engine/lib/hooks'
import {AppCompletion, PipelineCompletion, SpaceCompletion, TeamCompletion} from 'cli-engine-heroku/lib/completions'

const debug = require('debug')('heroku:completions')

async function run (config: Config, opts: PreRun) {
  try {
    if (config.windows) {
      debug('skipping autocomplete on windows')
    } else {
      const plugins = await (new Plugins(config)).list()
      const acPlugin = plugins.find(p => p.name === 'heroku-cli-autocomplete')
      if (acPlugin) {
        cli.action.start('Updating completions')
        let ac = await acPlugin.findCommand('autocomplete:init')
        if (ac) await ac.run(config)
        await AppCompletion.options({config})
        await PipelineCompletion.options({config})
        await SpaceCompletion.options({config})
        await TeamCompletion.options({config})
      } else {
        debug('skipping autocomplete, not installed')
      }
      cli.done()
    }
  } catch (err) {
    debug(err)
  }
}

module.exports = run
<|endoftext|>


<commit_before>/*!
 * jQuery-ajaxTransport-XDomainRequest - v1.0.3 - 2014-06-06
 * https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest
 * Copyright (c) 2014 Jason Moon (@JSONMOON)
 * Licensed MIT (/blob/master/LICENSE.txt)
 */
(function(factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as anonymous module.
    define(['jquery'], factory);
  } else if (typeof exports === 'object') {
    // CommonJS
    module.exports = factory(require('jquery'));
  } else {
    // Browser globals.
    factory(jQuery);
  }
}(function($) {

// Only continue if we're on IE8/IE9 with jQuery 1.5+ (contains the ajaxTransport function)
if ($.support.cors || !$.ajaxTransport || !window.XDomainRequest) {
  return;
}

var httpRegEx = /^https?:\/\//i;
var getOrPostRegEx = /^get|post$/i;
var sameSchemeRegEx = new RegExp('^'+location.protocol, 'i');

// ajaxTransport exists in jQuery 1.5+
$.ajaxTransport('* text html xml json', function(options, userOptions, jqXHR) {

  // Only continue if the request is: asynchronous, uses GET or POST method, has HTTP or HTTPS protocol, and has the same scheme as the calling page
  if (!options.crossDomain || !options.async || !getOrPostRegEx.test(options.type) || !httpRegEx.test(options.url) || !sameSchemeRegEx.test(options.url)) {
    return;
  }

  var xdr = null;

  return {
    send: function(headers, complete) {
      var postData = '';
      var userType = (userOptions.dataType || '').toLowerCase();

      xdr = new XDomainRequest();
      if (/^\d+$/.test(userOptions.timeout)) {
        xdr.timeout = userOptions.timeout;
      }

      xdr.ontimeout = function() {
        complete(500, 'timeout');
      };

      xdr.onload = function() {
        var allResponseHeaders = 'Content-Length: ' + xdr.responseText.length + '\r\nContent-Type: ' + xdr.contentType;
        var status = {
          code: 200,
          message: 'success'
        };
        var responses = {
          text: xdr.responseText
        };
        try {
          if (userType === 'html' || /text\/html/i.test(xdr.contentType)) {
            responses.html = xdr.responseText;
          } else if (userType === 'json' || (userType !== 'text' && /\/json/i.test(xdr.contentType))) {
            try {
              responses.json = $.parseJSON(xdr.responseText);
            } catch(e) {
              status.code = 500;
              status.message = 'parseerror';
              //throw 'Invalid JSON: ' + xdr.responseText;
            }
          } else if (userType === 'xml' || (userType !== 'text' && /\/xml/i.test(xdr.contentType))) {
            var doc = new ActiveXObject('Microsoft.XMLDOM');
            doc.async = false;
            try {
              doc.loadXML(xdr.responseText);
            } catch(e) {
              doc = undefined;
            }
            if (!doc || !doc.documentElement || doc.getElementsByTagName('parsererror').length) {
              status.code = 500;
              status.message = 'parseerror';
              throw 'Invalid XML: ' + xdr.responseText;
            }
            responses.xml = doc;
          }
        } catch(parseMessage) {
          throw parseMessage;
        } finally {
          complete(status.code, status.message, responses, allResponseHeaders);
        }
      };

      // set an empty handler for 'onprogress' so requests don't get aborted
      xdr.onprogress = function(){};
      xdr.onerror = function() {
        complete(500, 'error', {
          text: xdr.responseText
        });
      };

      if (userOptions.data) {
        postData = ($.type(userOptions.data) === 'string') ? userOptions.data : $.param(userOptions.data);
      }
      xdr.open(options.type, options.url);
      xdr.send(postData);
    },
    abort: function() {
      if (xdr) {
        xdr.abort();
      }
    }
  };
});

}));
<commit_msg>Loop jQuery object trough<commit_after>/*!
 * jQuery-ajaxTransport-XDomainRequest - v1.0.3 - 2014-06-06
 * https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest
 * Copyright (c) 2014 Jason Moon (@JSONMOON)
 * Licensed MIT (/blob/master/LICENSE.txt)
 */
(function(factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as anonymous module.
    define(['jquery'], factory);
  } else if (typeof exports === 'object') {
    // CommonJS
    module.exports = factory(require('jquery'));
  } else {
    // Browser globals.
    factory(jQuery);
  }
}(function($) {

// Only continue if we're on IE8/IE9 with jQuery 1.5+ (contains the ajaxTransport function)
if ($.support.cors || !$.ajaxTransport || !window.XDomainRequest) {
  return $;
}

var httpRegEx = /^https?:\/\//i;
var getOrPostRegEx = /^get|post$/i;
var sameSchemeRegEx = new RegExp('^'+location.protocol, 'i');

// ajaxTransport exists in jQuery 1.5+
$.ajaxTransport('* text html xml json', function(options, userOptions, jqXHR) {

  // Only continue if the request is: asynchronous, uses GET or POST method, has HTTP or HTTPS protocol, and has the same scheme as the calling page
  if (!options.crossDomain || !options.async || !getOrPostRegEx.test(options.type) || !httpRegEx.test(options.url) || !sameSchemeRegEx.test(options.url)) {
    return;
  }

  var xdr = null;

  return {
    send: function(headers, complete) {
      var postData = '';
      var userType = (userOptions.dataType || '').toLowerCase();

      xdr = new XDomainRequest();
      if (/^\d+$/.test(userOptions.timeout)) {
        xdr.timeout = userOptions.timeout;
      }

      xdr.ontimeout = function() {
        complete(500, 'timeout');
      };

      xdr.onload = function() {
        var allResponseHeaders = 'Content-Length: ' + xdr.responseText.length + '\r\nContent-Type: ' + xdr.contentType;
        var status = {
          code: 200,
          message: 'success'
        };
        var responses = {
          text: xdr.responseText
        };
        try {
          if (userType === 'html' || /text\/html/i.test(xdr.contentType)) {
            responses.html = xdr.responseText;
          } else if (userType === 'json' || (userType !== 'text' && /\/json/i.test(xdr.contentType))) {
            try {
              responses.json = $.parseJSON(xdr.responseText);
            } catch(e) {
              status.code = 500;
              status.message = 'parseerror';
              //throw 'Invalid JSON: ' + xdr.responseText;
            }
          } else if (userType === 'xml' || (userType !== 'text' && /\/xml/i.test(xdr.contentType))) {
            var doc = new ActiveXObject('Microsoft.XMLDOM');
            doc.async = false;
            try {
              doc.loadXML(xdr.responseText);
            } catch(e) {
              doc = undefined;
            }
            if (!doc || !doc.documentElement || doc.getElementsByTagName('parsererror').length) {
              status.code = 500;
              status.message = 'parseerror';
              throw 'Invalid XML: ' + xdr.responseText;
            }
            responses.xml = doc;
          }
        } catch(parseMessage) {
          throw parseMessage;
        } finally {
          complete(status.code, status.message, responses, allResponseHeaders);
        }
      };

      // set an empty handler for 'onprogress' so requests don't get aborted
      xdr.onprogress = function(){};
      xdr.onerror = function() {
        complete(500, 'error', {
          text: xdr.responseText
        });
      };

      if (userOptions.data) {
        postData = ($.type(userOptions.data) === 'string') ? userOptions.data : $.param(userOptions.data);
      }
      xdr.open(options.type, options.url);
      xdr.send(postData);
    },
    abort: function() {
      if (xdr) {
        xdr.abort();
      }
    }
  };
});

return $;

}));
<|endoftext|>


<commit_before>import React, { Suspense, lazy } from 'react'
import PropTypes from 'prop-types'
import { withStyles } from '@material-ui/core/styles'
import Input from '@material-ui/core/Input'
import IconButton from '@material-ui/core/IconButton'
import InputAdornment from '@material-ui/core/InputAdornment'
import SearchIcon from '@material-ui/icons/Search'
import Tabs from '@material-ui/core/Tabs'
import Tab from '@material-ui/core/Tab'
import Paper from '@material-ui/core/Paper'
import Button from '@material-ui/core/Button'
import Typography from '@material-ui/core/Typography'
import { isSearchPageEnabled } from 'js/utils/feature-flags'
import {
  adblockerWhitelistingForSearchURL,
  dashboardURL,
  modifyURLParams,
} from 'js/navigation/navigation'
import { externalRedirect } from 'js/navigation/utils'
import LogoWithText from 'js/components/Logo/LogoWithText'
import { parseUrlSearchString } from 'js/utils/utils'
import SearchResults from 'js/components/Search/SearchResults'
import { isReactSnapClient } from 'js/utils/search-utils'
import SearchMenuQuery from 'js/components/Search/SearchMenuQuery'
import detectAdblocker from 'js/utils/detectAdblocker'
import Link from 'js/components/General/Link'

const Footer = lazy(() => import('js/components/General/Footer'))

const searchBoxBorderColor = '#ced4da'
const searchBoxBorderColorFocused = '#bdbdbd'

const styles = theme => ({
  inputRootStyle: {
    padding: 0,
    borderRadius: 28,
    backgroundColor: theme.palette.common.white,
    border: `1px solid ${searchBoxBorderColor}`,
    fontSize: 16,
    boxShadow: '0rem 0rem 0.02rem 0.02rem rgba(0, 0, 0, 0.1)',
    transition: theme.transitions.create(['border-color', 'box-shadow']),
    '&:hover': {
      borderColor: searchBoxBorderColorFocused,
      boxShadow: '0rem 0.05rem 0.2rem 0.05rem rgba(0, 0, 0, 0.1)',
    },
  },
  inputRootFocused: {
    borderColor: searchBoxBorderColorFocused,
    boxShadow: '0rem 0.05rem 0.2rem 0.05rem rgba(0, 0, 0, 0.1)',
  },
  inputStyle: {
    padding: '12px 16px',
  },
  tabsContainerRootStyle: {
    minHeight: 0,
  },
  tabRootStyle: {
    color: '#505050', // Same as search result description
    minHeight: 34,
    minWidth: 10,
  },
})

class SearchPage extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      isAdBlockerEnabled: false,
      query: '',
      searchFeatureEnabled: isSearchPageEnabled(),
      searchSource: null,
      searchText: '',
      showPlaceholderText: false,
    }
  }

  componentDidMount() {
    if (!this.state.searchFeatureEnabled) {
      // Cannot use pushState now that the apps are separate.
      externalRedirect(dashboardURL)
    }
    const { location } = this.props

    // Wait until after mount to update prerendered state.
    const query = parseUrlSearchString(location.search).q || ''
    this.setState({
      // We always derive the query and page values URL parameter
      // values. We keep tehse in state so that we update the
      // prerendered components after mount, because at prerender
      // time we do not know the query string. We can remove this
      // from state if we switch to server-side rendering.
      query: query,
      page: this.getPageNumberFromSearchString(location.search),
      showPlaceholderText: !isReactSnapClient(),
      searchSource: parseUrlSearchString(location.search).src || null,
      searchText: query,
    })

    // AdBlockerDetection
    detectAdblocker()
      .then(isEnabled => {
        this.setState({
          isAdBlockerEnabled: isEnabled,
        })
      })
      .catch(e => {
        console.error(e)
      })
  }

  componentDidUpdate(prevProps) {
    const { location } = this.props
    const currentQuery = parseUrlSearchString(location.search).q
    const prevQuery = parseUrlSearchString(prevProps.location.search).q
    if (currentQuery !== prevQuery) {
      this.setState({
        query: currentQuery,
        searchText: currentQuery,
      })
    }

    // Check if the page number has changed.
    const currentPage = this.getPageNumberFromSearchString(location.search)
    const prevPage = this.getPageNumberFromSearchString(
      prevProps.location.search
    )
    if (currentPage !== prevPage) {
      this.setState({
        page: currentPage,
      })
    }
  }

  /**
   * Take a search string, such as ?abc=hi&p=12, and return the
   * integer value of the "page" URL parameter. If the parameter is
   * not set or is not an integer, return 1.
   * @param {String} searchStr - The URL parameter string,
   *   such as '?myParam=foo&another=bar'
   * @return {Number} The search results page inded
   */
  getPageNumberFromSearchString(searchStr) {
    return parseInt(parseUrlSearchString(searchStr).page, 10) || 1
  }

  search() {
    const newQuery = this.state.searchText
    if (newQuery) {
      modifyURLParams({
        page: 1,
        q: newQuery,
        src: 'self',
      })
      this.setState({
        searchSource: 'self',
      })
    }
  }

  onSearchTextChange(e) {
    this.setState({
      searchText: e.target.value,
    })
  }

  render() {
    const { classes } = this.props
    const {
      isAdBlockerEnabled,
      page,
      query,
      searchSource,
      searchText,
    } = this.state
    const queryEncoded = query ? encodeURI(query) : ''
    const searchResultsPaddingLeft = 170
    if (!this.state.searchFeatureEnabled) {
      return null
    }
    return (
      <div
        data-test-id={'search-page'}
        style={{
          backgroundColor: '#fff',
          // For demonstration only
          minWidth: 1100,
          // minWidth: '100vw',
          minHeight: '100vh',
          display: 'flex',
          flexDirection: 'column',
        }}
      >
        <div
          style={{
            backgroundColor: '#fff',
            borderBottom: '1px solid #e4e4e4',
          }}
        >
          <div
            style={{
              padding: '20px 18px 6px',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'flex-start',
            }}
          >
            <LogoWithText
              style={{
                width: 100,
                height: 36,
              }}
            />
            <div
              style={{
                maxWidth: 600,
                marginLeft: 30,
                flex: 1,
              }}
            >
              <Input
                id="search-input"
                type={'text'}
                value={searchText}
                onChange={this.onSearchTextChange.bind(this)}
                onKeyPress={e => {
                  if (e.key === 'Enter') {
                    this.search()
                  }
                }}
                placeholder={
                  // Don't immediately render the placeholder text because
                  // we may rapidly replace it with the query on first render.
                  this.state.showPlaceholderText
                    ? 'Search to raise money for charity...'
                    : ''
                }
                disableUnderline
                fullWidth
                classes={{
                  root: classes.inputRootStyle,
                  input: classes.inputStyle,
                  focused: classes.inputRootFocused,
                }}
                endAdornment={
                  <InputAdornment position="end">
                    <IconButton
                      aria-label="Search button"
                      onClick={this.search.bind(this)}
                    >
                      <SearchIcon
                        style={{ color: searchBoxBorderColorFocused }}
                      />
                    </IconButton>
                  </InputAdornment>
                }
              />
            </div>
            <SearchMenuQuery
              style={{
                marginLeft: 'auto',
              }}
            />
          </div>
          <Tabs
            value={0}
            indicatorColor={'secondary'}
            style={{
              marginTop: 8,
              marginLeft: 149,
            }}
            classes={{
              root: classes.tabsContainerRootStyle,
            }}
          >
            <Tab
              label="Web"
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="Images"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/search?q=${queryEncoded}&tbm=isch`
                  : 'https://images.google.com'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="News"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/search?q=${queryEncoded}&tbm=nws`
                  : 'https://www.google.com'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="Videos"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/search?q=${queryEncoded}&tbm=vid`
                  : 'https://www.google.com'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="Maps"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/maps/?q=${queryEncoded}`
                  : 'https://www.google.com/maps'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
          </Tabs>
        </div>
        <div
          style={{
            display: 'flex',
          }}
        >
          <div
            style={{
              marginLeft: searchResultsPaddingLeft,
              marginTop: 20,
              width: 600,
            }}
          >
            {isAdBlockerEnabled ? (
              <div
                data-test-id={'search-prevented-warning'}
                style={{
                  marginBottom: 20,
                  width: '100%',
                }}
              >
                <Paper
                  elevation={1}
                  style={{
                    padding: '10px 18px',
                    backgroundColor: 'rgb(242, 222, 222)',
                  }}
                >
                  <span
                    style={{
                      display: 'flex',
                      flexDirection: 'column',
                      alignItems: 'flex-start',
                    }}
                  >
                    <Typography
                      style={{
                        color: 'rgb(169, 68, 66)',
                        fontWeight: 'bold',
                        marginBottom: 8,
                        marginTop: 8,
                      }}
                      variant={'h6'}
                    >
                      Please disable your ad blocker
                    </Typography>
                    <Typography variant={'body2'}>
                      We use search ads to raise money for charity. You'll
                      likely need to whitelist Search for a Cause for search
                      results to show.
                    </Typography>
                    <div
                      style={{
                        display: 'flex',
                        alignSelf: 'flex-end',
                        marginTop: 10,
                      }}
                    >
                      <Link
                        to={adblockerWhitelistingForSearchURL}
                        target="_blank"
                        rel="noopener noreferrer"
                      >
                        <Button color={'default'}>Show me how</Button>
                      </Link>
                    </div>
                  </span>
                </Paper>
              </div>
            ) : null}
            <SearchResults
              query={query}
              page={page}
              onPageChange={newPageIndex => {
                modifyURLParams({
                  page: newPageIndex,
                })
              }}
              isAdBlockerEnabled={isAdBlockerEnabled}
              searchSource={searchSource}
              style={{
                maxWidth: 600,
                marginBottom: 40,
              }}
            />
          </div>
          <div
            data-test-id={'search-sidebar'}
            style={{
              display: 'flex',
              flexDirection: 'column',
              margin: '20px 40px',
              width: '100%',
              boxSizing: 'border-box',
              maxWidth: 410,
              minWidth: 300,
            }}
          >
            <Paper
              elevation={1}
              style={{
                width: '100%',
                boxSizing: 'border-box',
                padding: '10px 18px',
                marginBottom: 20,
              }}
            >
              <span
                style={{
                  display: 'flex',
                  flexDirection: 'column',
                  alignItems: 'flex-start',
                }}
              >
                <Typography
                  variant={'h6'}
                  style={{ marginTop: 8, marginBottom: 8 }}
                >
                  Your searches do good :)
                </Typography>
                <Typography variant={'body2'}>
                  When you search, you raise money for charity! The money comes
                  from the ads in search results, and you decide where the money
                  goes by donating your hearts to your favorite nonprofit.
                </Typography>
                <div
                  style={{
                    display: 'flex',
                    alignSelf: 'flex-end',
                    marginTop: 10,
                  }}
                >
                  <Button color={'primary'}>Got it!</Button>
                </div>
              </span>
            </Paper>
          </div>
        </div>
        <Suspense fallback={null}>
          <Footer
            style={{
              marginTop: 'auto',
            }}
          />
        </Suspense>
      </div>
    )
  }
}

SearchPage.propTypes = {
  classes: PropTypes.object.isRequired,
  location: PropTypes.shape({
    search: PropTypes.string.isRequired,
  }),
  theme: PropTypes.object.isRequired,
}

SearchPage.defaultProps = {}

export default withStyles(styles, { withTheme: true })(SearchPage)
<commit_msg>Change button copy<commit_after>import React, { Suspense, lazy } from 'react'
import PropTypes from 'prop-types'
import { withStyles } from '@material-ui/core/styles'
import Input from '@material-ui/core/Input'
import IconButton from '@material-ui/core/IconButton'
import InputAdornment from '@material-ui/core/InputAdornment'
import SearchIcon from '@material-ui/icons/Search'
import Tabs from '@material-ui/core/Tabs'
import Tab from '@material-ui/core/Tab'
import Paper from '@material-ui/core/Paper'
import Button from '@material-ui/core/Button'
import Typography from '@material-ui/core/Typography'
import { isSearchPageEnabled } from 'js/utils/feature-flags'
import {
  adblockerWhitelistingForSearchURL,
  dashboardURL,
  modifyURLParams,
} from 'js/navigation/navigation'
import { externalRedirect } from 'js/navigation/utils'
import LogoWithText from 'js/components/Logo/LogoWithText'
import { parseUrlSearchString } from 'js/utils/utils'
import SearchResults from 'js/components/Search/SearchResults'
import { isReactSnapClient } from 'js/utils/search-utils'
import SearchMenuQuery from 'js/components/Search/SearchMenuQuery'
import detectAdblocker from 'js/utils/detectAdblocker'
import Link from 'js/components/General/Link'

const Footer = lazy(() => import('js/components/General/Footer'))

const searchBoxBorderColor = '#ced4da'
const searchBoxBorderColorFocused = '#bdbdbd'

const styles = theme => ({
  inputRootStyle: {
    padding: 0,
    borderRadius: 28,
    backgroundColor: theme.palette.common.white,
    border: `1px solid ${searchBoxBorderColor}`,
    fontSize: 16,
    boxShadow: '0rem 0rem 0.02rem 0.02rem rgba(0, 0, 0, 0.1)',
    transition: theme.transitions.create(['border-color', 'box-shadow']),
    '&:hover': {
      borderColor: searchBoxBorderColorFocused,
      boxShadow: '0rem 0.05rem 0.2rem 0.05rem rgba(0, 0, 0, 0.1)',
    },
  },
  inputRootFocused: {
    borderColor: searchBoxBorderColorFocused,
    boxShadow: '0rem 0.05rem 0.2rem 0.05rem rgba(0, 0, 0, 0.1)',
  },
  inputStyle: {
    padding: '12px 16px',
  },
  tabsContainerRootStyle: {
    minHeight: 0,
  },
  tabRootStyle: {
    color: '#505050', // Same as search result description
    minHeight: 34,
    minWidth: 10,
  },
})

class SearchPage extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      isAdBlockerEnabled: false,
      query: '',
      searchFeatureEnabled: isSearchPageEnabled(),
      searchSource: null,
      searchText: '',
      showPlaceholderText: false,
    }
  }

  componentDidMount() {
    if (!this.state.searchFeatureEnabled) {
      // Cannot use pushState now that the apps are separate.
      externalRedirect(dashboardURL)
    }
    const { location } = this.props

    // Wait until after mount to update prerendered state.
    const query = parseUrlSearchString(location.search).q || ''
    this.setState({
      // We always derive the query and page values URL parameter
      // values. We keep tehse in state so that we update the
      // prerendered components after mount, because at prerender
      // time we do not know the query string. We can remove this
      // from state if we switch to server-side rendering.
      query: query,
      page: this.getPageNumberFromSearchString(location.search),
      showPlaceholderText: !isReactSnapClient(),
      searchSource: parseUrlSearchString(location.search).src || null,
      searchText: query,
    })

    // AdBlockerDetection
    detectAdblocker()
      .then(isEnabled => {
        this.setState({
          isAdBlockerEnabled: isEnabled,
        })
      })
      .catch(e => {
        console.error(e)
      })
  }

  componentDidUpdate(prevProps) {
    const { location } = this.props
    const currentQuery = parseUrlSearchString(location.search).q
    const prevQuery = parseUrlSearchString(prevProps.location.search).q
    if (currentQuery !== prevQuery) {
      this.setState({
        query: currentQuery,
        searchText: currentQuery,
      })
    }

    // Check if the page number has changed.
    const currentPage = this.getPageNumberFromSearchString(location.search)
    const prevPage = this.getPageNumberFromSearchString(
      prevProps.location.search
    )
    if (currentPage !== prevPage) {
      this.setState({
        page: currentPage,
      })
    }
  }

  /**
   * Take a search string, such as ?abc=hi&p=12, and return the
   * integer value of the "page" URL parameter. If the parameter is
   * not set or is not an integer, return 1.
   * @param {String} searchStr - The URL parameter string,
   *   such as '?myParam=foo&another=bar'
   * @return {Number} The search results page inded
   */
  getPageNumberFromSearchString(searchStr) {
    return parseInt(parseUrlSearchString(searchStr).page, 10) || 1
  }

  search() {
    const newQuery = this.state.searchText
    if (newQuery) {
      modifyURLParams({
        page: 1,
        q: newQuery,
        src: 'self',
      })
      this.setState({
        searchSource: 'self',
      })
    }
  }

  onSearchTextChange(e) {
    this.setState({
      searchText: e.target.value,
    })
  }

  render() {
    const { classes } = this.props
    const {
      isAdBlockerEnabled,
      page,
      query,
      searchSource,
      searchText,
    } = this.state
    const queryEncoded = query ? encodeURI(query) : ''
    const searchResultsPaddingLeft = 170
    if (!this.state.searchFeatureEnabled) {
      return null
    }
    return (
      <div
        data-test-id={'search-page'}
        style={{
          backgroundColor: '#fff',
          // For demonstration only
          minWidth: 1100,
          // minWidth: '100vw',
          minHeight: '100vh',
          display: 'flex',
          flexDirection: 'column',
        }}
      >
        <div
          style={{
            backgroundColor: '#fff',
            borderBottom: '1px solid #e4e4e4',
          }}
        >
          <div
            style={{
              padding: '20px 18px 6px',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'flex-start',
            }}
          >
            <LogoWithText
              style={{
                width: 100,
                height: 36,
              }}
            />
            <div
              style={{
                maxWidth: 600,
                marginLeft: 30,
                flex: 1,
              }}
            >
              <Input
                id="search-input"
                type={'text'}
                value={searchText}
                onChange={this.onSearchTextChange.bind(this)}
                onKeyPress={e => {
                  if (e.key === 'Enter') {
                    this.search()
                  }
                }}
                placeholder={
                  // Don't immediately render the placeholder text because
                  // we may rapidly replace it with the query on first render.
                  this.state.showPlaceholderText
                    ? 'Search to raise money for charity...'
                    : ''
                }
                disableUnderline
                fullWidth
                classes={{
                  root: classes.inputRootStyle,
                  input: classes.inputStyle,
                  focused: classes.inputRootFocused,
                }}
                endAdornment={
                  <InputAdornment position="end">
                    <IconButton
                      aria-label="Search button"
                      onClick={this.search.bind(this)}
                    >
                      <SearchIcon
                        style={{ color: searchBoxBorderColorFocused }}
                      />
                    </IconButton>
                  </InputAdornment>
                }
              />
            </div>
            <SearchMenuQuery
              style={{
                marginLeft: 'auto',
              }}
            />
          </div>
          <Tabs
            value={0}
            indicatorColor={'secondary'}
            style={{
              marginTop: 8,
              marginLeft: 149,
            }}
            classes={{
              root: classes.tabsContainerRootStyle,
            }}
          >
            <Tab
              label="Web"
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="Images"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/search?q=${queryEncoded}&tbm=isch`
                  : 'https://images.google.com'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="News"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/search?q=${queryEncoded}&tbm=nws`
                  : 'https://www.google.com'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="Videos"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/search?q=${queryEncoded}&tbm=vid`
                  : 'https://www.google.com'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="Maps"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/maps/?q=${queryEncoded}`
                  : 'https://www.google.com/maps'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
          </Tabs>
        </div>
        <div
          style={{
            display: 'flex',
          }}
        >
          <div
            style={{
              marginLeft: searchResultsPaddingLeft,
              marginTop: 20,
              width: 600,
            }}
          >
            {isAdBlockerEnabled ? (
              <div
                data-test-id={'search-prevented-warning'}
                style={{
                  marginBottom: 20,
                  width: '100%',
                }}
              >
                <Paper
                  elevation={1}
                  style={{
                    padding: '10px 18px',
                    backgroundColor: 'rgb(242, 222, 222)',
                  }}
                >
                  <span
                    style={{
                      display: 'flex',
                      flexDirection: 'column',
                      alignItems: 'flex-start',
                    }}
                  >
                    <Typography
                      style={{
                        color: 'rgb(169, 68, 66)',
                        fontWeight: 'bold',
                        marginBottom: 8,
                        marginTop: 8,
                      }}
                      variant={'h6'}
                    >
                      Please disable your ad blocker
                    </Typography>
                    <Typography variant={'body2'}>
                      We use search ads to raise money for charity. You'll
                      likely need to whitelist Search for a Cause for search
                      results to show.
                    </Typography>
                    <div
                      style={{
                        display: 'flex',
                        alignSelf: 'flex-end',
                        marginTop: 10,
                      }}
                    >
                      <Link
                        to={adblockerWhitelistingForSearchURL}
                        target="_blank"
                        rel="noopener noreferrer"
                      >
                        <Button color={'default'}>Show me how</Button>
                      </Link>
                    </div>
                  </span>
                </Paper>
              </div>
            ) : null}
            <SearchResults
              query={query}
              page={page}
              onPageChange={newPageIndex => {
                modifyURLParams({
                  page: newPageIndex,
                })
              }}
              isAdBlockerEnabled={isAdBlockerEnabled}
              searchSource={searchSource}
              style={{
                maxWidth: 600,
                marginBottom: 40,
              }}
            />
          </div>
          <div
            data-test-id={'search-sidebar'}
            style={{
              display: 'flex',
              flexDirection: 'column',
              margin: '20px 40px',
              width: '100%',
              boxSizing: 'border-box',
              maxWidth: 410,
              minWidth: 300,
            }}
          >
            <Paper
              elevation={1}
              style={{
                width: '100%',
                boxSizing: 'border-box',
                padding: '10px 18px',
                marginBottom: 20,
              }}
            >
              <span
                style={{
                  display: 'flex',
                  flexDirection: 'column',
                  alignItems: 'flex-start',
                }}
              >
                <Typography
                  variant={'h6'}
                  style={{ marginTop: 8, marginBottom: 8 }}
                >
                  Your searches do good :)
                </Typography>
                <Typography variant={'body2'}>
                  When you search, you raise money for charity! The money comes
                  from the ads in search results, and you decide where the money
                  goes by donating your hearts to your favorite nonprofit.
                </Typography>
                <div
                  style={{
                    display: 'flex',
                    alignSelf: 'flex-end',
                    marginTop: 10,
                  }}
                >
                  <Button color={'primary'}>Great!</Button>
                </div>
              </span>
            </Paper>
          </div>
        </div>
        <Suspense fallback={null}>
          <Footer
            style={{
              marginTop: 'auto',
            }}
          />
        </Suspense>
      </div>
    )
  }
}

SearchPage.propTypes = {
  classes: PropTypes.object.isRequired,
  location: PropTypes.shape({
    search: PropTypes.string.isRequired,
  }),
  theme: PropTypes.object.isRequired,
}

SearchPage.defaultProps = {}

export default withStyles(styles, { withTheme: true })(SearchPage)
<|endoftext|>


<commit_before>// Requires kalendae.js and moment.js

var module = angular.module('Kalendae', []);

module.factory('kal', ['$window', function($window) {
    return $window.Kalendae;
}]);

module.factory('Moment', ['$window', function($window) {
    return $window.moment;
}]);

module.directive('kalendae', ['$parse', 'kal', 'KalendaeAPI', function($parse, kal, KalendaeAPI) {
    return {
        restrict: 'EA',
        link: function(scope, element, attrs) {
            var model      = $parse(attrs.ngModel);
            var blackout   = scope.$eval(attrs.blackout);

            var type       = attrs.kalendae === 'dropdown' ? kal.Input : kal;
            var datePicker = new type(element[0], {
                mode       : attrs.mode        || 'single',
                months     : attrs.months      || 1,
                useYearNav : attrs.useYearNav  || true,
                direction  : attrs.direction   || 'any',
                closeButton: attrs.closeButton || false,
                blackout   : blackout          || false
            });

            KalendaeAPI.setDatePicker(datePicker);

            // Requires animate.css in order to animate.
            angular.element(datePicker.container).addClass('animated fadeInDown');

            datePicker.subscribe('date-clicked', function(date) {
                var selectedDate = KalendaeAPI.setDate(date);

                model.assign(scope, selectedDate);
                scope.$apply(function() {
                    scope.$eval(attrs.callback);
                });

                // When using the dropdown datepicker, hide after selecting a date.
                attrs.kalendae === 'dropdown' && element[0].blur();
            });
        }
    };
}]);

module.factory('KalendaeAPI', ['Moment', function(Moment) {
    return {
        datePicker: null,
        values:null,
        dates:[],
        setDatePicker: function(datePicker) {
            return this.datePicker = datePicker
        },
        setSelected: function(date) {
            this.datePicker.setSelected(date);
        },
        setBlackOutDates: function() {

        },
        setValues: function(values) {
            this.values = values;
        },
        setDate: function(date) {
            return Moment(date).format('MM/DD/YYYY');
        },
        setDates: function(date) {
            this.dates = [];
            for(var key in dates) {
                var date = Moment(dates[key]).format('MM/DD/YYYY');

                this.dates.push(date);
            }

            return this.dates;
        },
        getDates: function() {
            return this.dates;
        },
        getValueByDate: function(date) {
            var valueByDate;
            var date = Moment(date);

            for(var key in this.values) {
                var min_date = this.values[key].min;
                var max_date = this.values[key].max;
                var value    = this.values[key].value;

                if(date.isBefore(max_date) && date.isAfter(min_date)) {
                    valueByDate = value;
                    break;
                }
            }

            return valueByDate;
        }
    };
}]);<commit_msg>Added setting of default date based on model when initilizing the directive.<commit_after>// Requires kalendae.js and moment.js

var module = angular.module('Kalendae', []);

module.factory('kal', ['$window', function($window) {
    return $window.Kalendae;
}]);

module.factory('Moment', ['$window', function($window) {
    return $window.moment;
}]);

module.directive('kalendae', ['$parse', 'kal', 'KalendaeAPI', function($parse, kal, KalendaeAPI) {
    return {
        restrict: 'EA',
        link: function(scope, element, attrs) {
            var model      = $parse(attrs.ngModel);
            var blackout   = scope.$eval(attrs.blackout);

            var type       = attrs.kalendae === 'dropdown' ? kal.Input : kal;
            var datePicker = new type(element[0], {
                mode       : attrs.mode        || 'single',
                months     : attrs.months      || 1,
                useYearNav : attrs.useYearNav  || true,
                direction  : attrs.direction   || 'any',
                closeButton: attrs.closeButton || false,
                blackout   : blackout          || false
            });

            KalendaeAPI.setDatePicker(datePicker);

            // Requires animate.css in order to animate.
            angular.element(datePicker.container).addClass('animated fadeInDown');

            datePicker.subscribe('date-clicked', function(date) {
                var selectedDate = KalendaeAPI.setDate(date);
                model.assign(scope, selectedDate);
                scope.$apply(function() {
                    scope.$eval(attrs.callback);
                });

                // When using the dropdown datepicker, hide after selecting a date.
                attrs.kalendae === 'dropdown' && element[0].blur();
            });

            // On init set date to the current model value.
            var defaultDate = scope.$eval(attrs.ngModel);

            if(defaultDate) {
                KalendaeAPI.setSelected(defaultDate);
                scope.$eval(attrs.callback);
            }

            // When using the dropdown datepicker, hide after selecting a date.
            attrs.kalendae === 'dropdown' && element[0].blur();
        }
    };
}]);

module.factory('KalendaeAPI', ['Moment', function(Moment) {
    return {
        datePicker: null,
        values:null,
        dates:[],
        setDatePicker: function(datePicker) {
            return this.datePicker = datePicker
        },
        setSelected: function(date) {
            this.datePicker.setSelected(date);
        },
        setBlackOutDates: function() {

        },
        setValues: function(values) {
            this.values = values;
        },
        setDate: function(date) {
            return Moment(date).format('MM/DD/YYYY');
        },
        setDates: function(date) {
            this.dates = [];
            for(var key in dates) {
                var date = Moment(dates[key]).format('MM/DD/YYYY');

                this.dates.push(date);
            }

            return this.dates;
        },
        getDates: function() {
            return this.dates;
        },
        getValueByDate: function(date) {
            var valueByDate;
            var date = Moment(date);

            for(var key in this.values) {
                var min_date = this.values[key].min;
                var max_date = this.values[key].max;
                var value    = this.values[key].value;

                if(date.isBefore(max_date) && date.isAfter(min_date)) {
                    valueByDate = value;
                    break;
                }
            }

            return valueByDate;
        }
    };
}]);<|endoftext|>


<commit_before>import postcss from 'postcss';
import getRulesMatcher from './rulesMatcher';
import getReset from './resetRules';
import createResetRule from './createResetRule';

const contains = (array, item) => array.indexOf(item) !== -1;

export default postcss.plugin('postcss-autoreset', (opts = {})=> {
  opts.rulesMatcher = opts.rulesMatcher || 'bem';
  opts.reset = opts.reset || 'initial';
  const rulesMatcher = getRulesMatcher(opts.rulesMatcher);
  const reset = getReset(opts.reset);
  return (css) => {
    const matchedSelectors = [];
    css.walkRules((rule)=> {
      const {selector} = rule;
      if (rule.parent.name === 'keyframes') return;
      if (!contains(matchedSelectors, selector) && rulesMatcher(rule)) {
        matchedSelectors.push(selector);
      }
    });
    if (!matchedSelectors.length) return;
    css.prepend(
      createResetRule(matchedSelectors, reset)
    );
  };
});
<commit_msg>Changed keyframe test to also ignore keyframes with vendor prefixes<commit_after>import postcss from 'postcss';
import getRulesMatcher from './rulesMatcher';
import getReset from './resetRules';
import createResetRule from './createResetRule';

const contains = (array, item) => array.indexOf(item) !== -1;

export default postcss.plugin('postcss-autoreset', (opts = {})=> {
  opts.rulesMatcher = opts.rulesMatcher || 'bem';
  opts.reset = opts.reset || 'initial';
  const rulesMatcher = getRulesMatcher(opts.rulesMatcher);
  const reset = getReset(opts.reset);
  return (css) => {
    const matchedSelectors = [];
    css.walkRules((rule)=> {
      const {selector} = rule;
      if (/^(-(webkit|moz|ms|o)-)?keyframes$/.test(rule.parent.name)) return;
      if (!contains(matchedSelectors, selector) && rulesMatcher(rule)) {
        matchedSelectors.push(selector);
      }
    });
    if (!matchedSelectors.length) return;
    css.prepend(
      createResetRule(matchedSelectors, reset)
    );
  };
});
<|endoftext|>


<commit_before>var DeveloperPlayground = DeveloperPlayground || {};

DeveloperPlayground.StartQueryBuilderView = Backbone.View.extend({
    el: "#developer-playground",

    template: _.template($('#playground-template').html()),

    events: {
        'change .form-control#entity': 'changeParam',
        'change .form-control#parameter': 'changeFilter',
        'click .btn#san-francisco': 'changeMapSF',
        'click .btn#new-york': 'changeMapNY',
        'click .btn#run-query-btn' : 'submit'
    },

    initialize: function () {
        this.operators = new DeveloperPlayground.Operators();
        this.stops = new DeveloperPlayground.Stops();
        this.routes = new DeveloperPlayground.Routes();
        this.render();
    },

    render: function() {
        this.$el.html(this.template());
        $(".form-control#name").hide();
        $(".btn#new-york").hide();
        $(".btn#san-francisco").hide();

        if($("#nameMenu").hasClass("dropdown")) $("#nameMenu").removeClass("dropdown");
        this.mapview = new DeveloperPlayground.MapView();
        this.mapview.render();
        this.downloadview = new DeveloperPlayground.DownloadView();
        this.downloadview.render();
        return this;
    },

    changeMapSF: function(){
        if($(".btn#new-york").hasClass("active")) $(".btn#new-york").removeClass("active");
        $(".btn#san-francisco").addClass("active");
        this.mapview.setMapviewSF();
    },

    changeMapNY: function(){
        $(".btn#new-york").addClass("active");
        if($(".btn#san-francisco").hasClass("active")) $(".btn#san-francisco").removeClass("active");
        this.mapview.setMapviewNY();
    },

    changeParam: function() {

        $(".form-control#name").hide();
        $(".btn#new-york").hide();
        $(".btn#san-francisco").hide();
        if($(".btn#new-york").hasClass("active")) $(".btn#new-york").removeClass("active");
        if($(".btn#new-york").hasClass("active")) $(".btn#new-york").removeClass("active");

        if($("#nameMenu").hasClass("dropdown")) $("#nameMenu").removeClass("dropdown");

        var $entitySelect = $('select.form-control#entity');
        var $parameterSelect = $('select.form-control#parameter');
        var selectValues = {
            "base": {
                " ": "",
            },
            "stops": {
                " ": "",
                "map view": "",
                "operator": "",
            },
            "operators": {
                " ": "",
                "map view": "",
                "name": "",
                // "mode": "",
            },
            "routes": {
                " ": "",
                "map view": "",
                "operator": "",
                // "route number": "",
            }
        };

        $parameterSelect.empty().append(function() {
            var output = '';
            $.each(selectValues[$entitySelect.val()], function(key, value) {
                output += '<option>' + key + '</option>';
            });
            return output;
        });

        return this;
    },

    changeFilter: function() {
        var $parameterSelect = $('select.form-control#parameter');

        if($parameterSelect.val() == "name" || $parameterSelect.val() == "operator") {
            collection = this.operators;
            $(".form-control#name").show();
            // commenting out for current version
            $(".btn#new-york").hide();
            $(".btn#san-francisco").hide();

            if(!$("#nameMenu").hasClass("dropdown")) $("#nameMenu").addClass("dropdown");
            if ('undefined' !== typeof this.nameListView) {
                this.nameListView.close();
                this.nameListView = new DeveloperPlayground.NameListView({collection: collection});
            } else {
                this.nameListView = new DeveloperPlayground.NameListView({collection: collection});
            }
            this.operators.setQueryParameters({
                    url: API_HOST+'/api/v1/operators.json'
                });
            collection.fetch();
            return this;

        } else {
            $(".form-control#name").hide();
            $(".btn#san-francisco").addClass("active");
            $(".btn#new-york").show();
            $(".btn#san-francisco").show();

            // commenting out?
            // if ('undefined' !== typeof this.locationListView) {
            //     this.locationListView.close();
            //     this.locationListView = new DeveloperPlayground.LocationListView();
            // } else {
            //     this.locationListView = new DeveloperPlayground.LocationListView();
            // }

            if($("#nameMenu").hasClass("dropdown")) $("#nameMenu").removeClass("dropdown");
        }

    },

    submit: function() {
        var $entitySelect = $('select.form-control#entity');
        var $parameterSelect = $('select.form-control#parameter');
        var $nameSelect = $('select.form-control#name');
        var bounds = this.mapview.getBounds();
        var identifier = $nameSelect.val();

        var shouldFetchAndResetCollection = true;

        // FOR STOP QUERIES

        if ($entitySelect.val() == "stops") {
            // for search by map view
            if($parameterSelect.val() == "map view") {
            collection = this.stops;
            this.stops.setQueryParameters({
                    url: API_HOST+'/api/v1/'+$entitySelect.val()+'.json?bbox='+bounds
                });
            // for search by operator name
            } else if($parameterSelect.val() == "operator") {
                collection = this.stops;
                this.stops.setQueryParameters({
                    url: API_HOST+'/api/v1/'+$entitySelect.val()+'.json?servedBy='+identifier,
                });
            }

        // FOR OPERATOR QUERIES

        } else if ($entitySelect.val() == "operators") {

            if($parameterSelect.val() == "map view") {
                collection = this.operators;
                this.operators.setQueryParameters({
                    url: API_HOST+'/api/v1/'+$entitySelect.val()+'.json?bbox='+bounds
                });
            } else if($parameterSelect.val() == "name") {
                this.operators.hideAll();
                this.operators.get(identifier).set({ display: true });
                shouldFetchAndResetCollection = false;
            } else {
                alert("Please select either map view or name.");
            }

        //  FOR ROUTE QUERIES

        } else if ($entitySelect.val() == "routes") {
            if($parameterSelect.val() == "map view") {
                collection = this.routes;
                this.routes.setQueryParameters({
                    url: API_HOST+'/api/v1/'+$entitySelect.val()+'.json?bbox='+bounds
                });
            } else if($parameterSelect.val() == "operator") {
                collection = this.routes;
                this.routes.setQueryParameters({
                    url: API_HOST+'/api/v1/'+$entitySelect.val()+'.json?operatedBy='+identifier,
                });
            } else if($parameterSelect.val() == "route number") {
                collection = this.routes;
                alert("routes by route number not yet functional");
            }
        } else {
            alert("Please select a parameter.");
        }

        if (shouldFetchAndResetCollection) {
            collection.reset();
        }

        this.mapview.markerclustergroup.clearLayers();
        this.mapview.clearCollection();
        this.mapview.setCollection({collection: collection});
        this.mapview.initialize({collection: collection});
        this.downloadview.setCollection({collection: collection});

        if ('undefined' !== typeof this.gridview) this.gridview.close();
        this.gridview = new DeveloperPlayground.GridView({collection: collection});

        if (shouldFetchAndResetCollection) {
            collection.fetch();
        }

    },
});<commit_msg>remove active state on location toggle buttons when changeFilter is run<commit_after>var DeveloperPlayground = DeveloperPlayground || {};

DeveloperPlayground.StartQueryBuilderView = Backbone.View.extend({
    el: "#developer-playground",

    template: _.template($('#playground-template').html()),

    events: {
        'change .form-control#entity': 'changeParam',
        'change .form-control#parameter': 'changeFilter',
        'click .btn#san-francisco': 'changeMapSF',
        'click .btn#new-york': 'changeMapNY',
        'click .btn#run-query-btn' : 'submit'
    },

    initialize: function () {
        this.operators = new DeveloperPlayground.Operators();
        this.stops = new DeveloperPlayground.Stops();
        this.routes = new DeveloperPlayground.Routes();
        this.render();
    },

    render: function() {
        this.$el.html(this.template());
        $(".form-control#name").hide();
        $(".btn#new-york").hide();
        $(".btn#san-francisco").hide();

        if($("#nameMenu").hasClass("dropdown")) $("#nameMenu").removeClass("dropdown");
        this.mapview = new DeveloperPlayground.MapView();
        this.mapview.render();
        this.downloadview = new DeveloperPlayground.DownloadView();
        this.downloadview.render();
        return this;
    },

    changeMapSF: function(){
        if($(".btn#new-york").hasClass("active")) $(".btn#new-york").removeClass("active");
        $(".btn#san-francisco").addClass("active");
        this.mapview.setMapviewSF();
    },

    changeMapNY: function(){
        $(".btn#new-york").addClass("active");
        if($(".btn#san-francisco").hasClass("active")) $(".btn#san-francisco").removeClass("active");
        this.mapview.setMapviewNY();
    },

    changeParam: function() {

        $(".form-control#name").hide();
        $(".btn#new-york").hide();
        $(".btn#san-francisco").hide();
        if($(".btn#new-york").hasClass("active")) $(".btn#new-york").removeClass("active");
        if($(".btn#new-york").hasClass("active")) $(".btn#new-york").removeClass("active");

        if($("#nameMenu").hasClass("dropdown")) $("#nameMenu").removeClass("dropdown");

        var $entitySelect = $('select.form-control#entity');
        var $parameterSelect = $('select.form-control#parameter');
        var selectValues = {
            "base": {
                " ": "",
            },
            "stops": {
                " ": "",
                "map view": "",
                "operator": "",
            },
            "operators": {
                " ": "",
                "map view": "",
                "name": "",
                // "mode": "",
            },
            "routes": {
                " ": "",
                "map view": "",
                "operator": "",
                // "route number": "",
            }
        };

        $parameterSelect.empty().append(function() {
            var output = '';
            $.each(selectValues[$entitySelect.val()], function(key, value) {
                output += '<option>' + key + '</option>';
            });
            return output;
        });

        return this;
    },

    changeFilter: function() {
        var $parameterSelect = $('select.form-control#parameter');

        if($parameterSelect.val() == "name" || $parameterSelect.val() == "operator") {
            collection = this.operators;
            $(".form-control#name").show();
            // commenting out for current version
            $(".btn#new-york").hide();
            $(".btn#san-francisco").hide();
            if($(".btn#new-york").hasClass("active")) $(".btn#new-york").removeClass("active");
            if($(".btn#new-york").hasClass("active")) $(".btn#new-york").removeClass("active");

            if(!$("#nameMenu").hasClass("dropdown")) $("#nameMenu").addClass("dropdown");
            if ('undefined' !== typeof this.nameListView) {
                this.nameListView.close();
                this.nameListView = new DeveloperPlayground.NameListView({collection: collection});
            } else {
                this.nameListView = new DeveloperPlayground.NameListView({collection: collection});
            }
            this.operators.setQueryParameters({
                    url: API_HOST+'/api/v1/operators.json'
                });
            collection.fetch();
            return this;

        } else {
            $(".form-control#name").hide();
            $(".btn#san-francisco").addClass("active");
            $(".btn#new-york").show();
            $(".btn#san-francisco").show();

            // commenting out?
            // if ('undefined' !== typeof this.locationListView) {
            //     this.locationListView.close();
            //     this.locationListView = new DeveloperPlayground.LocationListView();
            // } else {
            //     this.locationListView = new DeveloperPlayground.LocationListView();
            // }

            if($("#nameMenu").hasClass("dropdown")) $("#nameMenu").removeClass("dropdown");
        }

    },

    submit: function() {
        var $entitySelect = $('select.form-control#entity');
        var $parameterSelect = $('select.form-control#parameter');
        var $nameSelect = $('select.form-control#name');
        var bounds = this.mapview.getBounds();
        var identifier = $nameSelect.val();

        var shouldFetchAndResetCollection = true;

        // FOR STOP QUERIES

        if ($entitySelect.val() == "stops") {
            // for search by map view
            if($parameterSelect.val() == "map view") {
            collection = this.stops;
            this.stops.setQueryParameters({
                    url: API_HOST+'/api/v1/'+$entitySelect.val()+'.json?bbox='+bounds
                });
            // for search by operator name
            } else if($parameterSelect.val() == "operator") {
                collection = this.stops;
                this.stops.setQueryParameters({
                    url: API_HOST+'/api/v1/'+$entitySelect.val()+'.json?servedBy='+identifier,
                });
            }

        // FOR OPERATOR QUERIES

        } else if ($entitySelect.val() == "operators") {

            if($parameterSelect.val() == "map view") {
                collection = this.operators;
                this.operators.setQueryParameters({
                    url: API_HOST+'/api/v1/'+$entitySelect.val()+'.json?bbox='+bounds
                });
            } else if($parameterSelect.val() == "name") {
                this.operators.hideAll();
                this.operators.get(identifier).set({ display: true });
                shouldFetchAndResetCollection = false;
            } else {
                alert("Please select either map view or name.");
            }

        //  FOR ROUTE QUERIES

        } else if ($entitySelect.val() == "routes") {
            if($parameterSelect.val() == "map view") {
                collection = this.routes;
                this.routes.setQueryParameters({
                    url: API_HOST+'/api/v1/'+$entitySelect.val()+'.json?bbox='+bounds
                });
            } else if($parameterSelect.val() == "operator") {
                collection = this.routes;
                this.routes.setQueryParameters({
                    url: API_HOST+'/api/v1/'+$entitySelect.val()+'.json?operatedBy='+identifier,
                });
            } else if($parameterSelect.val() == "route number") {
                collection = this.routes;
                alert("routes by route number not yet functional");
            }
        } else {
            alert("Please select a parameter.");
        }

        if (shouldFetchAndResetCollection) {
            collection.reset();
        }

        this.mapview.markerclustergroup.clearLayers();
        this.mapview.clearCollection();
        this.mapview.setCollection({collection: collection});
        this.mapview.initialize({collection: collection});
        this.downloadview.setCollection({collection: collection});

        if ('undefined' !== typeof this.gridview) this.gridview.close();
        this.gridview = new DeveloperPlayground.GridView({collection: collection});

        if (shouldFetchAndResetCollection) {
            collection.fetch();
        }

    },
});<|endoftext|>


<commit_before>/*
 * (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

(function () {
  "use strict";

  describe('st-magic-search directive', function () {
    var $element, $scope, $timeout;

    beforeEach(module('templates'));
    beforeEach(module('smart-table'));
    beforeEach(module('horizon.framework.widgets'));
    beforeEach(module('MagicSearch'));

    beforeEach(inject(function ($injector) {
      var $compile = $injector.get('$compile');
      $scope = $injector.get('$rootScope').$new();
      $timeout = $injector.get('$timeout');

      $scope.rows = [
        { name: 'instance one', status: 'active', flavor: 'm1.tiny' },
        { name: 'instance two', status: 'active', flavor: 'm1.small' },
        { name: 'instance three', status: 'shutdown', flavor: 'm1.tiny' },
        { name: 'instance four', status: 'shutdown', flavor: 'm1.small' },
        { name: 'instance five', status: 'error', flavor: 'm1.tiny' },
        { name: 'instance six', status: 'error', flavor: 'm1.small' }
      ];

      $scope.filterStrings = {
        cancel: gettext('Cancel'),
        prompt: gettext('Prompt'),
        remove: gettext('Remove'),
        text: gettext('Text')
      };

      $scope.filterFacets = [
        {
          name: 'name',
          label: gettext('Name'),
          singleton: true
        },
        {
          name: 'status',
          label: gettext('Status'),
          options: [
            { key: 'active', label: gettext('Active') },
            { key: 'shutdown', label: gettext('Shutdown') },
            { key: 'error', label: gettext('Error') }
          ]
        },
        {
          name: 'flavor',
          label: gettext('Flavor'),
          singleton: true,
          options: [
            { key: 'm1.tiny', label: gettext('m1.tiny') },
            { key: 'm1.small', label: gettext('m1.small') }
          ]
        }
      ];

      /* eslint-disable angular/ng_window_service */
      var msTemplate = window.STATIC_URL + 'framework/widgets/magic-search/magic-search.html';
      /* eslint-enable angular/ng_window_service */
      var stMagicSearch =
        '<st-magic-search>' +
        '  <magic-search ' +
        '    template="' + msTemplate + '"' +
        '    strings="filterStrings" ' +
        '    facets="{{ filterFacets }}">' +
        '  </magic-search>' +
        '</st-magic-search>';
      var markup = '
' +
                   '
' +
                   '
' +
                   '   ' + stMagicSearch + '	' +
                   '
' +
                   '
' +
                   '
' +
                   '
' +
                   '    {{ row.name }}	' +
                   '
' +
                   '
' +
                   '
';

      $element = $compile(angular.element(markup))($scope);

      $scope.$apply();
    }));

    it('should filter table to two rows if text searching with "active"', function () {
      $scope.$broadcast('textSearch', 'active');
      $timeout.flush();
      expect($element.find('tbody tr').length).toBe(2);
    });

    it('should filter table to two rows if facet with static === "shutdown"', function () {
      $scope.$broadcast('searchUpdated', 'status=shutdown');
      $timeout.flush();
      expect($element.find('tbody tr').length).toBe(2);
    });
  });
})();
<commit_msg>Mock $window for magic-search tests<commit_after>/*
 * (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

(function () {
  "use strict";

  describe('st-magic-search directive', function () {
    var $element, $scope, $timeout;

    beforeEach(module('templates'));
    beforeEach(module('smart-table'));
    beforeEach(module('horizon.framework.widgets'));
    beforeEach(module('MagicSearch'));
    beforeEach(module(function ($provide) {
      $provide.value('$window', {
        location: {
          search: '',
          href: ''
        },
        history: {
          pushState: angular.noop
        }
      });
    }));

    beforeEach(inject(function ($injector) {
      var $compile = $injector.get('$compile');
      $scope = $injector.get('$rootScope').$new();
      $timeout = $injector.get('$timeout');

      $scope.rows = [
        { name: 'instance one', status: 'active', flavor: 'm1.tiny' },
        { name: 'instance two', status: 'active', flavor: 'm1.small' },
        { name: 'instance three', status: 'shutdown', flavor: 'm1.tiny' },
        { name: 'instance four', status: 'shutdown', flavor: 'm1.small' },
        { name: 'instance five', status: 'error', flavor: 'm1.tiny' },
        { name: 'instance six', status: 'error', flavor: 'm1.small' }
      ];

      $scope.filterStrings = {
        cancel: gettext('Cancel'),
        prompt: gettext('Prompt'),
        remove: gettext('Remove'),
        text: gettext('Text')
      };

      $scope.filterFacets = [
        {
          name: 'name',
          label: gettext('Name'),
          singleton: true
        },
        {
          name: 'status',
          label: gettext('Status'),
          options: [
            { key: 'active', label: gettext('Active') },
            { key: 'shutdown', label: gettext('Shutdown') },
            { key: 'error', label: gettext('Error') }
          ]
        },
        {
          name: 'flavor',
          label: gettext('Flavor'),
          singleton: true,
          options: [
            { key: 'm1.tiny', label: gettext('m1.tiny') },
            { key: 'm1.small', label: gettext('m1.small') }
          ]
        }
      ];

      /* eslint-disable angular/ng_window_service */
      var msTemplate = window.STATIC_URL + 'framework/widgets/magic-search/magic-search.html';
      /* eslint-enable angular/ng_window_service */
      var stMagicSearch =
        '<st-magic-search>' +
        '  <magic-search ' +
        '    template="' + msTemplate + '"' +
        '    strings="filterStrings" ' +
        '    facets="{{ filterFacets }}">' +
        '  </magic-search>' +
        '</st-magic-search>';
      var markup = '
' +
                   '
' +
                   '
' +
                   '   ' + stMagicSearch + '	' +
                   '
' +
                   '
' +
                   '
' +
                   '
' +
                   '    {{ row.name }}	' +
                   '
' +
                   '
' +
                   '
';

      $element = $compile(angular.element(markup))($scope);

      $scope.$apply();
    }));

    it('should filter table to two rows if text searching with "active"', function () {
      $scope.$broadcast('textSearch', 'active');
      $timeout.flush();
      expect($element.find('tbody tr').length).toBe(2);
    });

    it('should filter table to two rows if facet with static === "shutdown"', function () {
      $scope.$broadcast('searchUpdated', 'status=shutdown');
      $timeout.flush();
      expect($element.find('tbody tr').length).toBe(2);
    });
  });
})();
<|endoftext|>


<commit_before>(function (jStorage, undefined) {

    jStorage.providers.localstorage = jStorage.providers.prototype = {
        init: function (wrapper, config) {
            var self = this;
            this._config = config;

        },
        get: function (name, callback) {
            var self = this;

        },
        set: function (name, content, callback) {
            var self = this;

        },
        del: function (name, callback) {
            var self = this;

        },
        list: function (name, callback) {
            var self = this;

        },
        exists: function (name, callback) {
            var self = this;

        }
    };
})(jStorage);<commit_msg>work on localStorage<commit_after>(function (jStorage, undefined) {

    jStorage.providers.localstorage = jStorage.providers.prototype = {
        init: function (wrapper, config) {
            var self = this;
            this._config = config;

            // TODO: Verify that browser support localStorage
            // TODO: call the callback function with result
        },
        get: function (name, callback) {
            var self = this;
            var content = window.localStorage.getItem(name);

            // TODO: call the callback function with result
        },
        set: function (name, content, callback) {
            var self = this;

            window.localStorage.setItem(name, content);

            // TODO: call the callback function with result
        },
        del: function (name, callback) {
            var self = this;

            window.localStorage.removeItem(name);

            // TODO: call the callback function with result
        },
        list: function (name, callback) {
            var self = this;

            // TODO: Add logic to see if key is in "virtual" folder and then return it if it is..
            for (var key in window.localStorage) {
                //console.log(key);
            }

            // TODO: call the callback function with result
        },
        exists: function (name, callback) {
            var self = this;

            var exists = name in window.localStorage;
            // TODO: call the callback function with result
        }
    };
})(jStorage);<|endoftext|>