<commit_before>// Copyright 2019 Google LLC
//
// 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
//
//     https://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.
/* eslint-disable no-unused-vars */

// This file will be included only on the 'feed' (find-events) page,
// and contains items specific to the map and posts on that page.

//
// Types
//

/**
 * An object containing information about a location,
 * whether an entire college, building, or street.
 * @typedef {Object} LocationInfo
 * @property {string} name - The name of the location
 * @property {number} lat - The latitude of the location
 * @property {number} long - The longitude of the location
 */

/**
 * An object containing information about a post.
 * @typedef {Object} PostInfo
 * @property {number} id - The unique id of the post.
 * @property {string} organizationName - The name of the organization making the post.
 * @property {Date} postDateTime - The date that the post was made.
 * @property {Date} eventStartTime - The starting time of the event described in the post.
 * @property {Date} eventEndTime - The ending time of the event described in the post.
 * @property {LocationInfo} location - The location of the event.
 * @property {number} numOfPeopleFoodWillFeed - The number of people the event's food can feed.
 * @property {string} foodType - A short 1-3 word description of the type of food served.
 * @property {string} description - A longer description of the entire event.
 */

//
// Global objects
//

/** @type {LocationInfo} */
let collegeLocation = null;

/** @type {Array<PostInfo>} */
let posts = null;

/** @type {google.maps.Map} */
let map;

//
// Constants
//

const MARKER_WIDTH_MINMAX = {min: 28, max: 70};
const MARKER_HEIGHT_MINMAX = {min: 45, max: 113};

//
// Event listener registration
//

// Hooks the onLoad function to the DOMContentLoaded event.
document.addEventListener('DOMContentLoaded', onLoad);

//
// Functions
//

/**
 * Fires as soon as the DOM is loaded.
 */
function onLoad() {
  // In the future, there will be real GET requests here, but for now, just fake ones.
  // These global variables will be assigned here and never assigned again.
  posts = fetchFakePosts();
  collegeLocation = fetchFakeCollegeLocation();

  if (document.getElementById('all-posts')) {
    addPosts(posts);
  }

  // Add the embedded map to the page.
  getSecretFor('javascript-maps-api').then((key) => {
    // TODO: If the key returns null, we should show a placeholder div with error text.

    const script = document.createElement('script');
    script.src = `https://maps.googleapis.com/maps/api/js?key=${key}&callback=initMap`;
    script.defer = true;
    script.async = true;
    window.initMap = initMap;
    document.head.appendChild(script);
  });
}

/**
 * Gets the secret value corresponding to a secret ID from GCP secrets store.
 * @param {string} secretid - The secret's id, as defined in the secrets store.
 */
async function getSecretFor(secretid) {
  try {
    const response = await fetch('/secretsManager?id=' + secretid, {method: 'POST'});
    if (!response.ok) {
      throw new Error(response.status);
    } else {
      return await response.text();
    }
  } catch (err) {
    console.warn(err);
    return;
  }
}

/**
 * Translates a location from its name to a pair of latitude and longitudes.
 * @param {string} address - The address to translate to lat/long.
 * @return {LocationInfo} or null if no such location exists or an error occurs.
 */
async function translateLocationToLatLong(address) {
  try {
    const response = await fetch('/translateLocation', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
      },
      body: createSearchParamsFromObject({location: address}),
    });

    // If we get a non-200 status response, then fail.
    if (!response.ok) {
      const error = await response.text();
      throw new Error(response.status + ' ' + error);
    }

    const result = await response.json();
    console.log(result);

    // If our result is empty, then we didn't have any results at all.
    if (Object.keys(result).length === 0) {
      throw new Error('No results returned with query');
    }

    // If our result doesn't have certain properties, then there's no valid lat/long.
    if (!('geometry' in result) || !('location' in result.geometry)) {
      throw new Error('No latitude/longitude was returned with that query.');
    }

    // If all of the above checks passed, we can return the lat/long to the caller.
    return {
      name: 'formattedAddress' in result ? result.formattedAddress : address,
      lat: result.geometry.location.lat,
      long: result.geometry.location.lng,
    };
  } catch (err) {
    console.warn(err);
    return null;
  }
}

/**
 * Creates search params for any object, to send fetch requests with.
 * @param {any} obj - The generic object to create seachparams for.
 * @return {string} - The searchparams.
 */
function createSearchParamsFromObject(obj) {
  return Object.keys(obj).map((key) => {
    return encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]);
  }).join('&');
}

/**
 * A fake implementation of a GET request for the college of the page we are on.
 * This will be removed once our backend has actual college information.
 * @return {LocationInfo} - The college's location and information.
 */
function fetchFakeCollegeLocation() {
  const newLocation = {
    name: 'Santa Clara University',
    lat: 37.348545,
    long: -121.9386406,
  };
  return newLocation;
}

/**
 * A fake implementation of a GET request that fetches all posts.
 * One post's event hasn't started yet, one has ended, and the
 * other three are in various stages of being completed.
 * This will be removed once our backend has actual posts.
 * @return {array} - The posts.
 */
function fetchFakePosts() {
  const fakePosts = [];
  for (let i = 0; i < 5; i++) {
    const post = {
      id: i*1000 + i*50 + i*2 + i,
      organizationName: `Organization ${i}`,
      postDateTime: new Date(new Date().setHours(new Date().getHours() - i)),
      eventStartTime: new Date(new Date().setMinutes(new Date().getMinutes() + (i-3)*8)),
      eventEndTime: new Date(new Date().setMinutes(new Date().getMinutes() + (i-0.5)*10)),
      location: {
        name: `Office ${i}`,
        lat: 37.348545 + (i + Math.random()*10 - 5) / 5000,
        long: -121.9386406 + (i + Math.random()*10 - 5)/ 5000,
      },
      numOfPeopleFoodWillFeed: (30 - i*5),
      foodType: 'Thai Food',
      description: 'Come join the ACM for free burritos and to learn more ' +
        'about what our club does! All are welcome to join the club happenings, ' +
        'regardless of major or year. ' +
        'We have vegatarian and halal options available.',
    };
    fakePosts.push(post);
  }
  return fakePosts;
}

/**
 * Initializes the embedded Google Maps map.
 */
function initMap() {
  /* eslint-disable no-undef */
  // Get the college of the page and center the map on it.
  map = new google.maps.Map(document.getElementById('map'),
      {
        center: {lat: collegeLocation.lat, lng: collegeLocation.long},
        zoom: 17,
      },
  );

  // Get all posts on the page and show them as markers.
  posts.forEach((post) => {
    const width = getMapMarkerIconSize(post.numOfPeopleFoodWillFeed, 'width');
    const height = getMapMarkerIconSize(post.numOfPeopleFoodWillFeed, 'height');
    const icon = {
      url: getMapMarkerIconUrl(post.eventStartTime),
      scaledSize: new google.maps.Size(width, height),
      origin: new google.maps.Point(0, 0),
    };

    const marker = new google.maps.Marker({
      position: {lat: post.location.lat, lng: post.location.long},
      map: map,
      title: post.organizationName,
      opacity: getMapMarkerOpacity(post.eventStartTime, post.eventEndTime),
      icon: icon,
    });

    marker.addListener('click', function() {
      const postElement = document.getElementById(post.id);
      postElement.scrollIntoView({block: 'center'});
      postElement.style.boxShadow = '0 1px 20px #939393, 0 -1px 20px #939393';

      // JS does not have native sleep(), so we can spoof the behavior with Promises.
      new Promise((r) => setTimeout(r, 1000)).then(() => {
        postElement.style.boxShadow = '0 1px 10px lightgrey, 0 -1px 10px lightgrey';
      });
    });
  });

/* eslint-enable no-undef */
}

/**
 * Calculates the opacity of a given map marker.
 * @param {Date} eventStartTime - The start time of the event.
 * @param {Date} eventEndTime - The end time of the event.
 * @return {number} - The opacity to show the marker as.
 */
function getMapMarkerOpacity(eventStartTime, eventEndTime) {
  const now = new Date();
  const startTime = eventStartTime;
  const endTime = eventEndTime;

  // If the date is in the future, our marker should have full opacity.
  if (startTime > now) {
    return 1;
  // If the event is over, our marker should not be on the map at all.
  } else if (endTime <= now) {
    return 0;
  }

  // Else, we know that the event is currently happening. In that case, we need to map
  // the range between 1 to 0 to the range of time between eventStartTime and eventEndTime.
  const adjusted = 1 - ((now - startTime) / (endTime - startTime));
  return adjusted;
}

/**
 * Returns the appropriate marker icon for a marker.
 * Grey if the event hasn't started yet, red otherwise.
 * @param {Date} eventStartTime - The start time of the event.
 * @return {string} - The location of the marker icon.
 */
function getMapMarkerIconUrl(eventStartTime) {
  const now = new Date();

  if (eventStartTime > now) {
    return './assets/greymarker.svg';
  }

  return './assets/redmarker.svg';
}

/**
 * Returns the appropriate marker size for a single dimension of a marker, based on the number
 * of people the food will feed. Uses a logistic function so that incredibly large and small
 * numbers still have reasonable icon sizes.
 * @param {number} numOfPeopleFoodWillFeed - The number of people the event's food will feed.
 * @param {string} dimensionType - The dimension type, either 'width' or 'height'.
 * @return {number} - The marker's dimension size.
 */
function getMapMarkerIconSize(numOfPeopleFoodWillFeed, dimensionType) {
  // Sanity check input for invalid values.
  if (dimensionType !== 'width' && dimensionType !== 'height') {
    return null;
  }

  // Apply the logistic function to the input.
  // The number of people is the x-value, and the bounds for the height
  // and width determine the logistic function's horizontal asymptotes.
  if (dimensionType === 'width') {
    return applyLogisticFunction(numOfPeopleFoodWillFeed, MARKER_WIDTH_MINMAX);
  } else {
    return applyLogisticFunction(numOfPeopleFoodWillFeed, MARKER_HEIGHT_MINMAX);
  }
}

/**
 * Applies a logistic function to the input. The function is designed such that x-values
 * over 40 level out to bounds.max, and x-values under 5 level out to bounds.min.
 * @param {number} xValue - The x-value to evaluate the function at.
 * @param {{min: number, max: number}} bounds - The asymptotes of the logistic function.
 * @return {number} - The output of the logistic function (i.e. the y-value).
 */
function applyLogisticFunction(xValue, bounds) {
  return Math.round((bounds.max - bounds.min)/
      (Math.exp(-((xValue - 25)/6)) + 1) + bounds.min);
}

/**
 * Adds posts to the page (uses mock data).
 * @param {array} posts
 */
function addPosts(posts) {
  const allPosts = document.getElementById('all-posts');
  posts.forEach((post) => {
    const titleText = post.organizationName + ' @ ' + post.location.name;
    const subtitleText = post.foodType + ' | ' + post.eventStartTime.toLocaleTimeString('en-US') +
      '-' + post.eventEndTime.toLocaleTimeString('en-US');
    const descriptionText = post.description;

    // Create card.
    const postCard = document.createElement('div');
    postCard.setAttribute('class', 'post-card');
    postCard.setAttribute('id', post.id);

    // Create and add title.
    const title = document.createElement('h2');
    title.setAttribute('class', 'card-title');
    title.innerText = titleText;
    postCard.appendChild(title);

    // Create and add subtitle.
    const subtitle = document.createElement('h3');
    subtitle.setAttribute('class', 'card-subtitle');
    subtitle.innerText = subtitleText;
    postCard.appendChild(subtitle);

    // Create and add description.
    const description = document.createElement('p');
    description.setAttribute('class', 'card-description');
    description.innerText = descriptionText;
    postCard.appendChild(description);

    // Add card to the page.
    allPosts.append(postCard);
  });
}
<commit_msg>Fix erroneous spacing<commit_after>// Copyright 2019 Google LLC
//
// 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
//
//     https://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.
/* eslint-disable no-unused-vars */

// This file will be included only on the 'feed' (find-events) page,
// and contains items specific to the map and posts on that page.

//
// Types
//

/**
 * An object containing information about a location,
 * whether an entire college, building, or street.
 * @typedef {Object} LocationInfo
 * @property {string} name - The name of the location
 * @property {number} lat - The latitude of the location
 * @property {number} long - The longitude of the location
 */

/**
 * An object containing information about a post.
 * @typedef {Object} PostInfo
 * @property {number} id - The unique id of the post.
 * @property {string} organizationName - The name of the organization making the post.
 * @property {Date} postDateTime - The date that the post was made.
 * @property {Date} eventStartTime - The starting time of the event described in the post.
 * @property {Date} eventEndTime - The ending time of the event described in the post.
 * @property {LocationInfo} location - The location of the event.
 * @property {number} numOfPeopleFoodWillFeed - The number of people the event's food can feed.
 * @property {string} foodType - A short 1-3 word description of the type of food served.
 * @property {string} description - A longer description of the entire event.
 */

//
// Global objects
//

/** @type {LocationInfo} */
let collegeLocation = null;

/** @type {Array<PostInfo>} */
let posts = null;

/** @type {google.maps.Map} */
let map;

//
// Constants
//

const MARKER_WIDTH_MINMAX = {min: 28, max: 70};
const MARKER_HEIGHT_MINMAX = {min: 45, max: 113};

//
// Event listener registration
//

// Hooks the onLoad function to the DOMContentLoaded event.
document.addEventListener('DOMContentLoaded', onLoad);

//
// Functions
//

/**
 * Fires as soon as the DOM is loaded.
 */
function onLoad() {
  // In the future, there will be real GET requests here, but for now, just fake ones.
  // These global variables will be assigned here and never assigned again.
  posts = fetchFakePosts();
  collegeLocation = fetchFakeCollegeLocation();

  if (document.getElementById('all-posts')) {
    addPosts(posts);
  }

  // Add the embedded map to the page.
  getSecretFor('javascript-maps-api').then((key) => {
    // TODO: If the key returns null, we should show a placeholder div with error text.

    const script = document.createElement('script');
    script.src = `https://maps.googleapis.com/maps/api/js?key=${key}&callback=initMap`;
    script.defer = true;
    script.async = true;
    window.initMap = initMap;
    document.head.appendChild(script);
  });
}

/**
 * Gets the secret value corresponding to a secret ID from GCP secrets store.
 * @param {string} secretid - The secret's id, as defined in the secrets store.
 */
async function getSecretFor(secretid) {
  try {
    const response = await fetch('/secretsManager?id=' + secretid, {method: 'POST'});
    if (!response.ok) {
      throw new Error(response.status);
    } else {
      return await response.text();
    }
  } catch (err) {
    console.warn(err);
    return;
  }
}

/**
 * Translates a location from its name to a pair of latitude and longitudes.
 * @param {string} address - The address to translate to lat/long.
 * @return {LocationInfo} or null if no such location exists or an error occurs.
 */
async function translateLocationToLatLong(address) {
  try {
    const response = await fetch('/translateLocation', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
      },
      body: createSearchParamsFromObject({location: address}),
    });

    // If we get a non-200 status response, then fail.
    if (!response.ok) {
      const error = await response.text();
      throw new Error(response.status + ' ' + error);
    }

    const result = await response.json();
    console.log(result);

    // If our result is empty, then we didn't have any results at all.
    if (Object.keys(result).length === 0) {
      throw new Error('No results returned with query');
    }

    // If our result doesn't have certain properties, then there's no valid lat/long.
    if (!('geometry' in result) || !('location' in result.geometry)) {
      throw new Error('No latitude/longitude was returned with that query.');
    }

    // If all of the above checks passed, we can return the lat/long to the caller.
    return {
      name: 'formattedAddress' in result ? result.formattedAddress : address,
      lat: result.geometry.location.lat,
      long: result.geometry.location.lng,
    };
  } catch (err) {
    console.warn(err);
    return null;
  }
}

/**
 * Creates search params for any object, to send fetch requests with.
 * @param {any} obj - The generic object to create seachparams for.
 * @return {string} - The searchparams.
 */
function createSearchParamsFromObject(obj) {
  return Object.keys(obj).map((key) => {
    return encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]);
  }).join('&');
}

/**
 * A fake implementation of a GET request for the college of the page we are on.
 * This will be removed once our backend has actual college information.
 * @return {LocationInfo} - The college's location and information.
 */
function fetchFakeCollegeLocation() {
  const newLocation = {
    name: 'Santa Clara University',
    lat: 37.348545,
    long: -121.9386406,
  };
  return newLocation;
}

/**
 * A fake implementation of a GET request that fetches all posts.
 * One post's event hasn't started yet, one has ended, and the
 * other three are in various stages of being completed.
 * This will be removed once our backend has actual posts.
 * @return {array} - The posts.
 */
function fetchFakePosts() {
  const fakePosts = [];
  for (let i = 0; i < 5; i++) {
    const post = {
      id: i*1000 + i*50 + i*2 + i,
      organizationName: `Organization ${i}`,
      postDateTime: new Date(new Date().setHours(new Date().getHours() - i)),
      eventStartTime: new Date(new Date().setMinutes(new Date().getMinutes() + (i-3)*8)),
      eventEndTime: new Date(new Date().setMinutes(new Date().getMinutes() + (i-0.5)*10)),
      location: {
        name: `Office ${i}`,
        lat: 37.348545 + (i + Math.random()*10 - 5) / 5000,
        long: -121.9386406 + (i + Math.random()*10 - 5)/ 5000,
      },
      numOfPeopleFoodWillFeed: (30 - i*5),
      foodType: 'Thai Food',
      description: 'Come join the ACM for free burritos and to learn more ' +
        'about what our club does! All are welcome to join the club happenings, ' +
        'regardless of major or year. ' +
        'We have vegatarian and halal options available.',
    };
    fakePosts.push(post);
  }
  return fakePosts;
}

/**
 * Initializes the embedded Google Maps map.
 */
function initMap() {
  /* eslint-disable no-undef */
  // Get the college of the page and center the map on it.
  map = new google.maps.Map(document.getElementById('map'),
      {
        center: {lat: collegeLocation.lat, lng: collegeLocation.long},
        zoom: 17,
      },
  );

  // Get all posts on the page and show them as markers.
  posts.forEach((post) => {
    const width = getMapMarkerIconSize(post.numOfPeopleFoodWillFeed, 'width');
    const height = getMapMarkerIconSize(post.numOfPeopleFoodWillFeed, 'height');
    const icon = {
      url: getMapMarkerIconUrl(post.eventStartTime),
      scaledSize: new google.maps.Size(width, height),
      origin: new google.maps.Point(0, 0),
    };

    const marker = new google.maps.Marker({
      position: {lat: post.location.lat, lng: post.location.long},
      map: map,
      title: post.organizationName,
      opacity: getMapMarkerOpacity(post.eventStartTime, post.eventEndTime),
      icon: icon,
    });

    marker.addListener('click', function() {
      const postElement = document.getElementById(post.id);
      postElement.scrollIntoView({block: 'center'});
      postElement.style.boxShadow = '0 1px 20px #939393, 0 -1px 20px #939393';

      // JS does not have native sleep(), so we can spoof the behavior with Promises.
      new Promise((r) => setTimeout(r, 1000)).then(() => {
        postElement.style.boxShadow = '0 1px 10px lightgrey, 0 -1px 10px lightgrey';
      });
    });
  });
  /* eslint-enable no-undef */
}

/**
 * Calculates the opacity of a given map marker.
 * @param {Date} eventStartTime - The start time of the event.
 * @param {Date} eventEndTime - The end time of the event.
 * @return {number} - The opacity to show the marker as.
 */
function getMapMarkerOpacity(eventStartTime, eventEndTime) {
  const now = new Date();
  const startTime = eventStartTime;
  const endTime = eventEndTime;

  // If the date is in the future, our marker should have full opacity.
  if (startTime > now) {
    return 1;
  // If the event is over, our marker should not be on the map at all.
  } else if (endTime <= now) {
    return 0;
  }

  // Else, we know that the event is currently happening. In that case, we need to map
  // the range between 1 to 0 to the range of time between eventStartTime and eventEndTime.
  const adjusted = 1 - ((now - startTime) / (endTime - startTime));
  return adjusted;
}

/**
 * Returns the appropriate marker icon for a marker.
 * Grey if the event hasn't started yet, red otherwise.
 * @param {Date} eventStartTime - The start time of the event.
 * @return {string} - The location of the marker icon.
 */
function getMapMarkerIconUrl(eventStartTime) {
  const now = new Date();

  if (eventStartTime > now) {
    return './assets/greymarker.svg';
  }

  return './assets/redmarker.svg';
}

/**
 * Returns the appropriate marker size for a single dimension of a marker, based on the number
 * of people the food will feed. Uses a logistic function so that incredibly large and small
 * numbers still have reasonable icon sizes.
 * @param {number} numOfPeopleFoodWillFeed - The number of people the event's food will feed.
 * @param {string} dimensionType - The dimension type, either 'width' or 'height'.
 * @return {number} - The marker's dimension size.
 */
function getMapMarkerIconSize(numOfPeopleFoodWillFeed, dimensionType) {
  // Sanity check input for invalid values.
  if (dimensionType !== 'width' && dimensionType !== 'height') {
    return null;
  }

  // Apply the logistic function to the input.
  // The number of people is the x-value, and the bounds for the height
  // and width determine the logistic function's horizontal asymptotes.
  if (dimensionType === 'width') {
    return applyLogisticFunction(numOfPeopleFoodWillFeed, MARKER_WIDTH_MINMAX);
  } else {
    return applyLogisticFunction(numOfPeopleFoodWillFeed, MARKER_HEIGHT_MINMAX);
  }
}

/**
 * Applies a logistic function to the input. The function is designed such that x-values
 * over 40 level out to bounds.max, and x-values under 5 level out to bounds.min.
 * @param {number} xValue - The x-value to evaluate the function at.
 * @param {{min: number, max: number}} bounds - The asymptotes of the logistic function.
 * @return {number} - The output of the logistic function (i.e. the y-value).
 */
function applyLogisticFunction(xValue, bounds) {
  return Math.round((bounds.max - bounds.min)/
      (Math.exp(-((xValue - 25)/6)) + 1) + bounds.min);
}

/**
 * Adds posts to the page (uses mock data).
 * @param {array} posts
 */
function addPosts(posts) {
  const allPosts = document.getElementById('all-posts');
  posts.forEach((post) => {
    const titleText = post.organizationName + ' @ ' + post.location.name;
    const subtitleText = post.foodType + ' | ' + post.eventStartTime.toLocaleTimeString('en-US') +
      '-' + post.eventEndTime.toLocaleTimeString('en-US');
    const descriptionText = post.description;

    // Create card.
    const postCard = document.createElement('div');
    postCard.setAttribute('class', 'post-card');
    postCard.setAttribute('id', post.id);

    // Create and add title.
    const title = document.createElement('h2');
    title.setAttribute('class', 'card-title');
    title.innerText = titleText;
    postCard.appendChild(title);

    // Create and add subtitle.
    const subtitle = document.createElement('h3');
    subtitle.setAttribute('class', 'card-subtitle');
    subtitle.innerText = subtitleText;
    postCard.appendChild(subtitle);

    // Create and add description.
    const description = document.createElement('p');
    description.setAttribute('class', 'card-description');
    description.innerText = descriptionText;
    postCard.appendChild(description);

    // Add card to the page.
    allPosts.append(postCard);
  });
}
<|endoftext|>


<commit_before>// Check if a new cache is available on page load.
window.addEventListener('load', function(e) {
    window.applicationCache.addEventListener('updateready', function(e) {
        var doPageReload = setOfflineStatus(false);
        if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
            console.log("Loading new version of page");
            // Browser downloaded a new app cache.
            // Swap it in and reload the page to get the new hotness.
            window.applicationCache.swapCache();
            window.location.reload();
        }
        else if (doPageReload) {
            console.log ("Back online - reload page in case there is new data");
            window.location.reload();
        }
        else {
            console.log ("The manifest did not change!");
        }
    }, false);
    window.applicationCache.addEventListener("error", function(e) {
       setOfflineStatus(true);
    });
    window.applicationCache.addEventListener("noupdate", function(e) {
        setOfflineStatus(false);
    });
}, false);

function setOfflineStatus(offline) {
    var doPageReload = false;
    if (offline) {
        console.log("We are offline");
        dukeconSettings.saveSetting(dukeconSettings.offline, true);
    }
    else {
        console.log("We are online - starting timer to check for updates");
        doPageReload = dukeconSettings.getSetting(dukeconSettings.offline);
        dukeconSettings.saveSetting(dukeconSettings.offline, false);
        dukeconTalkUtils.checkNewDataOnServer();
        setInterval(function() {
            dukeconTalkUtils.checkNewDataOnServer();
        }, 300000);
        dukecloak.init();
    }
    return doPageReload;
}

var dukeconTalkUtils = {
    updateCheck : ko.observable(false),

    getData : function(url, callback) {
        var offline = dukeconSettings.getSetting(dukeconSettings.offline);
        var dataHash = dukeconSettings.getSetting(dukeconSettings.last_updated_hash);
        if (!offline && !dataHash) {
            dukeconTalkUtils.getDataFromServer(url, callback);
        }
        else {
            dukeconDb.get(dukeconDb.talk_store, function(data) {
                if (data) {
                    callback(data);
                }
                else if (!offline) {
                    dukeconTalkUtils.getDataFromServer(url, callback);
                }
            });
        }
    },

    checkNewDataOnServer : function() {
        dukeconTalkUtils.updateCheck(true);
        console.log('Check for new data on server');
        var oldCacheHash = dukeconSettings.getSetting(dukeconSettings.last_updated_hash);
        //var oldCacheHash = Math.random().toString(36).slice(2);

        var successCallback = function(data, status, xhr) {
            if (data) {
                console.log("New Data on server; replacing old data in store");
                dukeconDb.save(dukeconDb.talk_store, data);
                dukeconSettings.saveSetting(dukeconSettings.last_updated_hash, xhr.getResponseHeader("ETag"));
            }
            dukeconTalkUtils.updateCheck(false);
        };
        dukeconTalkUtils.doServerRequest(jsonUrl, successCallback, function(error) {
            console.log('No connection to server');
            dukeconTalkUtils.updateCheck(false);
        }, { "If-None-Match": oldCacheHash });
    },

    getDataFromServer : function(url, callback) {
        console.log("Retrieving data from server");
        var successCallback = function(data, status, xhr) {
            if (data) {
                dukeconDb.save(dukeconDb.talk_store, data);
                dukeconSettings.saveSetting(dukeconSettings.last_updated_hash, xhr.getResponseHeader("ETag"));
                callback(data);
            }
        };
        var errorCallback = function() {
            console.log('No connection to server, retrieving data from local storage');
            dukeconDb.get(dukeconDb.talk_store, function(data) {
                if (data) {
                    callback(data);
                }
                else {
                    console.log('Could not retrieve any data');
                }
            });
        };
        dukeconTalkUtils.doServerRequest(url, successCallback, errorCallback);
    },

    doServerRequest : function(url, successCallback, errorCallback, headers) {
        $.ajax({
            method: 'GET',
            dataType: "json",
            headers: headers,
            url: url,
            success: successCallback,
            error: errorCallback
        });
    }
};

var dukeconSettings = {
    fav_key : "dukeconfavs",
    filter_key_prefix : "dukeconfilters_",
    filter_active_key : "dukeconfilters_active",
    favs_active : "dukeconfavs_active",
    selected_language_key : "dukecon_language",
    day_key : "dukeconday",
    last_updated_hash : "dukecon_last_updated_hash",
    offline : "dukecon_offline",

    context : window.location.pathname,

    getFavourites : function() {
        return dukeconSettings.getSettingOrEmptyArray(dukeconSettings.fav_key);
    },

    getSavedFilters : function(filters) {
        var savedFilters = {};
        _.each(filters, function(filter) {
            savedFilters[filter.filterKey] = dukeconSettings.getSettingOrEmptyArray(dukeconSettings.filter_key_prefix + filter.filterKey);
        });
        return savedFilters;
    },

    getSelectedDay : function() {
        var day = dukeconSettings.getSetting(dukeconSettings.day_key);
        return day ? day : "0";
    },

    getSelectedLanguage : function() {
        var language = dukeconSettings.getSetting(dukeconSettings.selected_language_key);
        return language ? language : "de";
    },

    isFavourite : function(id) {
        return dukeconSettings.getFavourites().indexOf(id) != -1;
    },

    filtersActive : function() {
        var result = dukeconSettings.getSetting(dukeconSettings.filter_active_key);
        return result == null ? true : result;
    },

    favoritesActive : function() {
        var result = dukeconSettings.getSetting(dukeconSettings.favs_active);
        return result == null ? false : result;
    },

    toggleFavourite : function(talkObject) {
        var id = talkObject.talk.id;
        var favourites = dukeconSettings.getFavourites();
        var pos = favourites.indexOf(id);
        if (pos === -1) {
            favourites.push(id);
        }
        else {
            favourites.splice(pos, 1);
        }
        talkObject.talk.toggleFavourite();
        dukeconSettings.saveSetting(dukeconSettings.fav_key, favourites);
        dukeconSynch.push();
    },

    saveSelectedFilters : function(filters) {
        _.each(filters, function(filter) {
            var selected = _.map(
                _.filter(filter.filtervalues(), function(val) { return val.selected(); }),
                function(filterValue) { return filterValue.id; });
            dukeconSettings.saveSetting(dukeconSettings.filter_key_prefix + filter.filterKey, selected);
        });
    },

    saveSelectedDay : function(day_index) {
        dukeconSettings.saveSetting(dukeconSettings.day_key, day_index);
    },

    saveSelectedLanguage : function(language) {
        dukeconSettings.saveSetting(dukeconSettings.selected_language_key, language);
    },

    getSettingOrEmptyArray : function(settingKey) {
        var setting = dukeconSettings.getSetting(settingKey);
        return setting ? setting : [];
    },

    getSetting : function(settingKey) {
        if (localStorage) {
            var setting = localStorage.getItem(dukeconSettings.context + settingKey);
            //console.log("Load: " + settingKey + " -> " + setting);
            return setting ? JSON.parse(setting) : null;
        }
        return null;
    },

    saveSetting : function(settingKey, value) {
        if (localStorage) {
            //console.log("Save: " + settingKey + " -> " + JSON.stringify(value));
            localStorage.setItem(dukeconSettings.context + settingKey, JSON.stringify(value));
        }
    },

    clearSetting : function(settingKey) {
        if (localStorage) {
            //console.log("Clear: " + settingKey));
            localStorage.removeItem(dukeconSettings.context + settingKey);
        }
    }

};

var dukeconSynch = {
    setToken : function(request) {
        request.setRequestHeader("Authorization", 'Bearer ' + dukecloak.keycloakAuth.token);
    },

    push : function() {
        if (!dukecloak.keycloakAuth.authenticated) {
            return;
        }
        var favourites = _.map(dukeconSettings.getFavourites(), function(fav) {
            return {"eventId" : fav, "version" : "1"};
        });
        dukecloak.keycloakAuth.updateToken()
            .success(function() {
                $.ajax({
                    method: 'POST',
                    beforeSend: dukeconSynch.setToken,
                    contentType : "application/json",
                    data : JSON.stringify(favourites),
                    url: "rest/preferences",
                    success: function() {
                        console.log("Pushed favourites to server");
                    },
                    error: function() {
                        console.log("Error pushing favourites to server");
                    }
                });
            })
            .error(function() {
                console.log("Error!");
            });
    },

    pull : function() {
        $.ajax({
            method: 'GET',
            beforeSend: dukeconSynch.setToken,
            dataType: "json",
            url:"rest/preferences",
            success: function(data) {
                var favouritesFromServer = _.map(data, function(fav) {
                    return fav.eventId;
                });
                var localFavourites = dukeconSettings.getFavourites();
                if (_.difference(favouritesFromServer, localFavourites).length > 0) {
                    dukeconSettings.saveSetting(dukeconSettings.fav_key, _.union(favouritesFromServer, localFavourites));
                    dukeconSynch.push();
                    if (dukeconTalklistModel) {
                        dukeconTalklistModel.updateFavourites();
                    }
                }
            },
            error: function() { console.log("Error loading preferences");}
        });
    }
};

var dukeconDb = {
    db_name : 'dukecon',
    talk_store : 'talks',
    indexedDB : window.indexedDB || window.webkitIndexedDB || window.msIndexedDB || window.mozIndexedDB,

    save : function(storeKey, data) {
        dukeconDb.clear(storeKey);
        dukeconDb.add(storeKey, data);
    },

    get : function(storeKey, callback) {
        console.log("Retrieve data from indexeddb");
        dukeconDb.createDatabase(storeKey, function(store) {
            var cursor = store.openCursor();
            cursor.onsuccess = function(event) {
                var cursor = event.target.result;
                callback(cursor ? cursor.value : null);
            };
            cursor.onerror = function(e) {
                console.log("Retrieving data from indexeddb failed");
            };
        });
    },

    clear : function(storeKey) {
        dukeconDb.createDatabase(storeKey, function(store) {
            store.clear().onerror = function(e) {
                console.log("Clearing the indexeddb failed");
            };
        });
    },

    add : function(storeKey, data) {
        dukeconDb.createDatabase(storeKey, function(store) {
            store.add(data).onerror = function(e2) {
                console.log("Saving data to indexeddb failed");
            };
        });
    },

    createDatabase : function(storeKey, callback) {
        //this.indexedDB.deleteDatabase(this.db_name); //please keep this line for debugging
        if (!this.indexedDB) {
            console.log("Cannot store in indexedDB");
            return;
        }
        var request = this.indexedDB.open(this.db_name, 3);
        request.onupgradeneeded = function(e) {
            if (!e || !e.target || !e.target.result) {
                console.log("No db in " + e);
            }
            else {
                dukeconDb.assertObjectStore(e.target.result, storeKey);
            }
        };
        request.onsuccess = function(e) {
            if (!e || !e.target || !e.target.result) {
                console.log("No db in " + e);
            }
            else {
                var store = dukeconDb.openTransaction(storeKey, e.target.result);
                if (store) {
                    callback(store);
                }
            }
        };
        request.onerror = function(e) {
            console.log("Opening database failed: " + e);
        };
    },

    openTransaction : function(storeKey, db) {
        var trans = db.transaction(storeKey, 'readwrite');
        if (!trans) {
            console.log("Could not open transaction");
            return null;
        }
        else {
            return trans.objectStore(storeKey);
        }
    },

    assertObjectStore : function(db, storeKey) {
        if (!db.objectStoreNames.contains(storeKey)){
            db.createObjectStore(storeKey, {
                keyPath: 'key',
                autoIncrement: true
            });
        }
    }
};<commit_msg>initialize dukecloak only on pages where it is available<commit_after>// Check if a new cache is available on page load.
window.addEventListener('load', function(e) {
    window.applicationCache.addEventListener('updateready', function(e) {
        var doPageReload = setOfflineStatus(false);
        if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
            console.log("Loading new version of page");
            // Browser downloaded a new app cache.
            // Swap it in and reload the page to get the new hotness.
            window.applicationCache.swapCache();
            window.location.reload();
        }
        else if (doPageReload) {
            console.log ("Back online - reload page in case there is new data");
            window.location.reload();
        }
        else {
            console.log ("The manifest did not change!");
        }
    }, false);
    window.applicationCache.addEventListener("error", function(e) {
       setOfflineStatus(true);
    });
    window.applicationCache.addEventListener("noupdate", function(e) {
        setOfflineStatus(false);
    });
}, false);

function setOfflineStatus(offline) {
    var doPageReload = false;
    if (offline) {
        console.log("We are offline");
        dukeconSettings.saveSetting(dukeconSettings.offline, true);
    }
    else {
        console.log("We are online - starting timer to check for updates");
        doPageReload = dukeconSettings.getSetting(dukeconSettings.offline);
        dukeconSettings.saveSetting(dukeconSettings.offline, false);
        dukeconTalkUtils.checkNewDataOnServer();
        setInterval(function() {
            dukeconTalkUtils.checkNewDataOnServer();
        }, 300000);
        if(typeof dukecloak !== 'undefined') {
            dukecloak.init();
        }
    }
    return doPageReload;
}

var dukeconTalkUtils = {
    updateCheck : ko.observable(false),

    getData : function(url, callback) {
        var offline = dukeconSettings.getSetting(dukeconSettings.offline);
        var dataHash = dukeconSettings.getSetting(dukeconSettings.last_updated_hash);
        if (!offline && !dataHash) {
            dukeconTalkUtils.getDataFromServer(url, callback);
        }
        else {
            dukeconDb.get(dukeconDb.talk_store, function(data) {
                if (data) {
                    callback(data);
                }
                else if (!offline) {
                    dukeconTalkUtils.getDataFromServer(url, callback);
                }
            });
        }
    },

    checkNewDataOnServer : function() {
        dukeconTalkUtils.updateCheck(true);
        console.log('Check for new data on server');
        var oldCacheHash = dukeconSettings.getSetting(dukeconSettings.last_updated_hash);
        //var oldCacheHash = Math.random().toString(36).slice(2);

        var successCallback = function(data, status, xhr) {
            if (data) {
                console.log("New Data on server; replacing old data in store");
                dukeconDb.save(dukeconDb.talk_store, data);
                dukeconSettings.saveSetting(dukeconSettings.last_updated_hash, xhr.getResponseHeader("ETag"));
            }
            dukeconTalkUtils.updateCheck(false);
        };
        dukeconTalkUtils.doServerRequest(jsonUrl, successCallback, function(error) {
            console.log('No connection to server');
            dukeconTalkUtils.updateCheck(false);
        }, { "If-None-Match": oldCacheHash });
    },

    getDataFromServer : function(url, callback) {
        console.log("Retrieving data from server");
        var successCallback = function(data, status, xhr) {
            if (data) {
                dukeconDb.save(dukeconDb.talk_store, data);
                dukeconSettings.saveSetting(dukeconSettings.last_updated_hash, xhr.getResponseHeader("ETag"));
                callback(data);
            }
        };
        var errorCallback = function() {
            console.log('No connection to server, retrieving data from local storage');
            dukeconDb.get(dukeconDb.talk_store, function(data) {
                if (data) {
                    callback(data);
                }
                else {
                    console.log('Could not retrieve any data');
                }
            });
        };
        dukeconTalkUtils.doServerRequest(url, successCallback, errorCallback);
    },

    doServerRequest : function(url, successCallback, errorCallback, headers) {
        $.ajax({
            method: 'GET',
            dataType: "json",
            headers: headers,
            url: url,
            success: successCallback,
            error: errorCallback
        });
    }
};

var dukeconSettings = {
    fav_key : "dukeconfavs",
    filter_key_prefix : "dukeconfilters_",
    filter_active_key : "dukeconfilters_active",
    favs_active : "dukeconfavs_active",
    selected_language_key : "dukecon_language",
    day_key : "dukeconday",
    last_updated_hash : "dukecon_last_updated_hash",
    offline : "dukecon_offline",

    context : window.location.pathname,

    getFavourites : function() {
        return dukeconSettings.getSettingOrEmptyArray(dukeconSettings.fav_key);
    },

    getSavedFilters : function(filters) {
        var savedFilters = {};
        _.each(filters, function(filter) {
            savedFilters[filter.filterKey] = dukeconSettings.getSettingOrEmptyArray(dukeconSettings.filter_key_prefix + filter.filterKey);
        });
        return savedFilters;
    },

    getSelectedDay : function() {
        var day = dukeconSettings.getSetting(dukeconSettings.day_key);
        return day ? day : "0";
    },

    getSelectedLanguage : function() {
        var language = dukeconSettings.getSetting(dukeconSettings.selected_language_key);
        return language ? language : "de";
    },

    isFavourite : function(id) {
        return dukeconSettings.getFavourites().indexOf(id) != -1;
    },

    filtersActive : function() {
        var result = dukeconSettings.getSetting(dukeconSettings.filter_active_key);
        return result == null ? true : result;
    },

    favoritesActive : function() {
        var result = dukeconSettings.getSetting(dukeconSettings.favs_active);
        return result == null ? false : result;
    },

    toggleFavourite : function(talkObject) {
        var id = talkObject.talk.id;
        var favourites = dukeconSettings.getFavourites();
        var pos = favourites.indexOf(id);
        if (pos === -1) {
            favourites.push(id);
        }
        else {
            favourites.splice(pos, 1);
        }
        talkObject.talk.toggleFavourite();
        dukeconSettings.saveSetting(dukeconSettings.fav_key, favourites);
        dukeconSynch.push();
    },

    saveSelectedFilters : function(filters) {
        _.each(filters, function(filter) {
            var selected = _.map(
                _.filter(filter.filtervalues(), function(val) { return val.selected(); }),
                function(filterValue) { return filterValue.id; });
            dukeconSettings.saveSetting(dukeconSettings.filter_key_prefix + filter.filterKey, selected);
        });
    },

    saveSelectedDay : function(day_index) {
        dukeconSettings.saveSetting(dukeconSettings.day_key, day_index);
    },

    saveSelectedLanguage : function(language) {
        dukeconSettings.saveSetting(dukeconSettings.selected_language_key, language);
    },

    getSettingOrEmptyArray : function(settingKey) {
        var setting = dukeconSettings.getSetting(settingKey);
        return setting ? setting : [];
    },

    getSetting : function(settingKey) {
        if (localStorage) {
            var setting = localStorage.getItem(dukeconSettings.context + settingKey);
            //console.log("Load: " + settingKey + " -> " + setting);
            return setting ? JSON.parse(setting) : null;
        }
        return null;
    },

    saveSetting : function(settingKey, value) {
        if (localStorage) {
            //console.log("Save: " + settingKey + " -> " + JSON.stringify(value));
            localStorage.setItem(dukeconSettings.context + settingKey, JSON.stringify(value));
        }
    },

    clearSetting : function(settingKey) {
        if (localStorage) {
            //console.log("Clear: " + settingKey));
            localStorage.removeItem(dukeconSettings.context + settingKey);
        }
    }

};

var dukeconSynch = {
    setToken : function(request) {
        request.setRequestHeader("Authorization", 'Bearer ' + dukecloak.keycloakAuth.token);
    },

    push : function() {
        if (!dukecloak.keycloakAuth.authenticated) {
            return;
        }
        var favourites = _.map(dukeconSettings.getFavourites(), function(fav) {
            return {"eventId" : fav, "version" : "1"};
        });
        dukecloak.keycloakAuth.updateToken()
            .success(function() {
                $.ajax({
                    method: 'POST',
                    beforeSend: dukeconSynch.setToken,
                    contentType : "application/json",
                    data : JSON.stringify(favourites),
                    url: "rest/preferences",
                    success: function() {
                        console.log("Pushed favourites to server");
                    },
                    error: function() {
                        console.log("Error pushing favourites to server");
                    }
                });
            })
            .error(function() {
                console.log("Error!");
            });
    },

    pull : function() {
        $.ajax({
            method: 'GET',
            beforeSend: dukeconSynch.setToken,
            dataType: "json",
            url:"rest/preferences",
            success: function(data) {
                var favouritesFromServer = _.map(data, function(fav) {
                    return fav.eventId;
                });
                var localFavourites = dukeconSettings.getFavourites();
                if (_.difference(favouritesFromServer, localFavourites).length > 0) {
                    dukeconSettings.saveSetting(dukeconSettings.fav_key, _.union(favouritesFromServer, localFavourites));
                    dukeconSynch.push();
                    if (dukeconTalklistModel) {
                        dukeconTalklistModel.updateFavourites();
                    }
                }
            },
            error: function() { console.log("Error loading preferences");}
        });
    }
};

var dukeconDb = {
    db_name : 'dukecon',
    talk_store : 'talks',
    indexedDB : window.indexedDB || window.webkitIndexedDB || window.msIndexedDB || window.mozIndexedDB,

    save : function(storeKey, data) {
        dukeconDb.clear(storeKey);
        dukeconDb.add(storeKey, data);
    },

    get : function(storeKey, callback) {
        console.log("Retrieve data from indexeddb");
        dukeconDb.createDatabase(storeKey, function(store) {
            var cursor = store.openCursor();
            cursor.onsuccess = function(event) {
                var cursor = event.target.result;
                callback(cursor ? cursor.value : null);
            };
            cursor.onerror = function(e) {
                console.log("Retrieving data from indexeddb failed");
            };
        });
    },

    clear : function(storeKey) {
        dukeconDb.createDatabase(storeKey, function(store) {
            store.clear().onerror = function(e) {
                console.log("Clearing the indexeddb failed");
            };
        });
    },

    add : function(storeKey, data) {
        dukeconDb.createDatabase(storeKey, function(store) {
            store.add(data).onerror = function(e2) {
                console.log("Saving data to indexeddb failed");
            };
        });
    },

    createDatabase : function(storeKey, callback) {
        //this.indexedDB.deleteDatabase(this.db_name); //please keep this line for debugging
        if (!this.indexedDB) {
            console.log("Cannot store in indexedDB");
            return;
        }
        var request = this.indexedDB.open(this.db_name, 3);
        request.onupgradeneeded = function(e) {
            if (!e || !e.target || !e.target.result) {
                console.log("No db in " + e);
            }
            else {
                dukeconDb.assertObjectStore(e.target.result, storeKey);
            }
        };
        request.onsuccess = function(e) {
            if (!e || !e.target || !e.target.result) {
                console.log("No db in " + e);
            }
            else {
                var store = dukeconDb.openTransaction(storeKey, e.target.result);
                if (store) {
                    callback(store);
                }
            }
        };
        request.onerror = function(e) {
            console.log("Opening database failed: " + e);
        };
    },

    openTransaction : function(storeKey, db) {
        var trans = db.transaction(storeKey, 'readwrite');
        if (!trans) {
            console.log("Could not open transaction");
            return null;
        }
        else {
            return trans.objectStore(storeKey);
        }
    },

    assertObjectStore : function(db, storeKey) {
        if (!db.objectStoreNames.contains(storeKey)){
            db.createObjectStore(storeKey, {
                keyPath: 'key',
                autoIncrement: true
            });
        }
    }
};<|endoftext|>


<commit_before>define(function (require) {
  var d3 = require("d3");
  var truncate = require("src/modules/component/axis/truncate");

  return function rotate() {
    // Private variables
    var axisLength = 100;
    var measure = "width";
    var labelPadding = 5;
    var truncateLength = 10;
    var text = {
      transform: "translate(0,0)rotate(-45)",
      x: 0,
      y: 6,
      dx: "",
      dy: ".71em",
      anchor: "end"
    };

    function component(g) {
      g.each(function () {
        var ticks = d3.select(this).selectAll(".tick text");
        var numOfTicks = ticks[0].length;
        var maxTickLabelLength = (axisLength / numOfTicks) - labelPadding;
        var isRotated;

        ticks.each(function () {
          //var labelLength = this.getComputedTextLength();
          var labelLength = this.getBBox()[measure];
          if (labelLength >= maxTickLabelLength) { isRotated = true; }
        });

        // Rotate and truncate
        if (isRotated) {
          ticks
            .attr("transform", text.transform)
            .attr("x", text.x)
            .attr("y", text.y)
            .attr("dx", text.dx)
            .attr("dy", text.dy)
            .style("text-anchor", text.anchor);

          // Truncation logic goes here
          ticks.each(function () {
            d3.select(this).call(truncate().maxCharLength(truncateLength));
          });
        }
      });
    }

    // Public API
    component.axisLength = function (_) {
      if (!arguments.length) { return axisLength; }
      axisLength = typeof _ !== "number" ? axisLength : _;
      return component;
    };

    component.measure = function (_) {
      if (!arguments.length) { return measure; }
      measure = typeof _ !== "string" ? measure : _;
      return component;
    };

    component.labelPadding = function (_) {
      if (!arguments.length) { return labelPadding; }
      labelPadding = typeof _ !== "number" ? labelPadding : _;
      return component;
    };

    component.truncateLength = function (_) {
      if (!arguments.length) { return truncateLength; }
      truncateLength = typeof _ !== "number" ? truncateLength : _;
      return component;
    };

    component.text = function (_) {
      if (!arguments.length) { return text; }
      text.transform = typeof _.transform !== "undefined" ? _.transform : text.transform;
      text.x = typeof _.x !== "undefined" ? _.x : text.x;
      text.y = typeof _.y !== "undefined" ? _.y : text.y;
      text.dx = typeof _.dx !== "undefined" ? _.dx : text.dx;
      text.dy = typeof _.dy !== "undefined" ? _.dy : text.dy;
      text.anchor = typeof _.anchor !== "undefined" ? _.anchor : text.anchor;
      return component;
    };

    return component;
  };
});
<commit_msg>removing unsused code<commit_after>define(function (require) {
  var d3 = require("d3");
  var truncate = require("src/modules/component/axis/truncate");

  return function rotate() {
    // Private variables
    var axisLength = 100;
    var measure = "width";
    var labelPadding = 5;
    var truncateLength = 10;
    var text = {
      transform: "translate(0,0)rotate(-45)",
      x: 0,
      y: 6,
      dx: "",
      dy: ".71em",
      anchor: "end"
    };

    function component(g) {
      g.each(function () {
        var ticks = d3.select(this).selectAll(".tick text");
        var numOfTicks = ticks[0].length;
        var maxTickLabelLength = (axisLength / numOfTicks) - labelPadding;
        var isRotated;

        ticks.each(function () {
          var labelLength = this.getBBox()[measure];
          if (labelLength >= maxTickLabelLength) { isRotated = true; }
        });

        // Rotate and truncate
        if (isRotated) {
          ticks
            .attr("transform", text.transform)
            .attr("x", text.x)
            .attr("y", text.y)
            .attr("dx", text.dx)
            .attr("dy", text.dy)
            .style("text-anchor", text.anchor);

          // Truncation logic goes here
          ticks.each(function () {
            d3.select(this).call(truncate().maxCharLength(truncateLength));
          });
        }
      });
    }

    // Public API
    component.axisLength = function (_) {
      if (!arguments.length) { return axisLength; }
      axisLength = typeof _ !== "number" ? axisLength : _;
      return component;
    };

    component.measure = function (_) {
      if (!arguments.length) { return measure; }
      measure = typeof _ !== "string" ? measure : _;
      return component;
    };

    component.labelPadding = function (_) {
      if (!arguments.length) { return labelPadding; }
      labelPadding = typeof _ !== "number" ? labelPadding : _;
      return component;
    };

    component.truncateLength = function (_) {
      if (!arguments.length) { return truncateLength; }
      truncateLength = typeof _ !== "number" ? truncateLength : _;
      return component;
    };

    component.text = function (_) {
      if (!arguments.length) { return text; }
      text.transform = typeof _.transform !== "undefined" ? _.transform : text.transform;
      text.x = typeof _.x !== "undefined" ? _.x : text.x;
      text.y = typeof _.y !== "undefined" ? _.y : text.y;
      text.dx = typeof _.dx !== "undefined" ? _.dx : text.dx;
      text.dy = typeof _.dy !== "undefined" ? _.dy : text.dy;
      text.anchor = typeof _.anchor !== "undefined" ? _.anchor : text.anchor;
      return component;
    };

    return component;
  };
});
<|endoftext|>


<commit_before>/*    Copyright 2022 Firewalla Inc
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

'use strict';

const log = require('../../../net2/logger.js')(__filename);
const fs = require('fs');
const Promise = require('bluebird');
Promise.promisifyAll(fs);
const exec = require('child-process-promise').exec;
const DockerBaseVPNClient = require('./DockerBaseVPNClient.js');
const YAML = require('../../../vendor_lib/yaml/dist');
const _ = require('lodash');
const f = require('../../../net2/Firewalla.js');

class OCDockerClient extends DockerBaseVPNClient {

  async prepareDockerCompose() {
    log.info("Preparing docker compose file...");
    const src = `${__dirname}/oc/docker-compose.template.yaml`;
    const content = await fs.readFileAsync(src, {encoding: 'utf8'});
    const dst = `${this._getConfigDirectory()}/docker-compose.yaml`;
    log.info("Writing config file", dst);
    await fs.writeFileAsync(dst, content);
  }

  async prepareConfig(config) {
    log.info("Preparing config file...");

    if (!config)
      return;

    const entries = [];
    const ignoredKeys = ["password", "server"];
    for (const key of Object.keys(config)) {
      if (ignoredKeys.includes(key))
        continue;
      if (config[key] !== null) {
        if (_.isArray(config[key])) {
          // parameter will be specified multiple times in config file if it is an array
          for (const value of config[key]) {
            if (value !== null)
              entries.push(`${key}=${value}`);
            else
              entries.push(`${key}`);
          }
        } else
          entries.push(`${key}=${config[key]}`);
      } else {
        entries.push(`${key}`); // a parameter without value
      }
    }
    const dst = `${this._getConfigDirectory()}/oc.conf`;
    await fs.writeFileAsync(dst, entries.join('\n'), {encoding: 'utf8'}) ;
  }

  async preparePasswd(config = {}) {
    log.info("Preparing passwd file...");
    const dst = `${this._getConfigDirectory()}/passwd`;
    await fs.writeFileAsync(dst, config.password, {encoding: 'utf8'});
  }

  async prepareServer(config = {}) {
    log.info("Preparing server file...");
    const dst = `${this._getConfigDirectory()}/server`;
    await fs.writeFileAsync(dst, config.server, {encoding: 'utf8'});
  }

  _getOutputDirectory() {
    return `${f.getHiddenFolder()}/run/docker/${this.profileId}/output`;
  }

  async _getDNSServersFromFile(file) {
    try {
      const str = await fs.readFileAsync(file, {encoding: 'utf8'});
      const ips = str.split(" ");

      if(!_.isEmpty(ips)) {
        return ips;
      }

    } catch(err) {
      log.error("Got error when getting DNS servers from file, err:", err);
    }

    return [];
  }

  async _getDNSServers() {
    const ipv4s = await this._getDNSServersFromFile(`${this._getOutputDirectory()}/nameserver.ipv4`);
    const ipv6s = await this._getDNSServersFromFile(`${this._getOutputDirectory()}/nameserver.ipv6`);

    return [...ipv4s, ...ipv6s]
      .map((x) => x.trim())
      .filter((x) => x !== "");
  }

  async getRoutedSubnets() {
    try {
      const base = await super.getRoutedSubnets();
      const file = `${this._getOutputDirectory()}/routes`;

      const str = await fs.readFileAsync(file, {encoding: 'utf8'});
      const routes = str.split("\n");

      if(!_.isEmpty(routes)) {
        return routes.filter((x) => x !== "");
      }

    } catch(err) {
      log.error("Got error when getting routes from file, err:", err);
    }

    return [];
  }

  async __prepareAssets() {
    const config = await this.loadOriginUserConfig();

    if(_.isEmpty(config)) return;

    await this.prepareDockerCompose(config);
    await this.preparePasswd(config);
    await this.prepareServer(config);
    await this.prepareConfig(config);
  }

  static getProtocol() {
    return "oc";
  }

}

module.exports = OCDockerClient;
<commit_msg>bugfix<commit_after>/*    Copyright 2022 Firewalla Inc
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

'use strict';

const log = require('../../../net2/logger.js')(__filename);
const fs = require('fs');
const Promise = require('bluebird');
Promise.promisifyAll(fs);
const exec = require('child-process-promise').exec;
const DockerBaseVPNClient = require('./DockerBaseVPNClient.js');
const YAML = require('../../../vendor_lib/yaml/dist');
const _ = require('lodash');
const f = require('../../../net2/Firewalla.js');

class OCDockerClient extends DockerBaseVPNClient {

  async prepareDockerCompose() {
    log.info("Preparing docker compose file...");
    const src = `${__dirname}/oc/docker-compose.template.yaml`;
    const content = await fs.readFileAsync(src, {encoding: 'utf8'});
    const dst = `${this._getConfigDirectory()}/docker-compose.yaml`;
    log.info("Writing config file", dst);
    await fs.writeFileAsync(dst, content);
  }

  async prepareConfig(config) {
    log.info("Preparing config file...");

    if (!config)
      return;

    const entries = [];
    const ignoredKeys = ["password", "server"];
    for (const key of Object.keys(config)) {
      if (ignoredKeys.includes(key))
        continue;
      if (config[key] !== null) {
        if (_.isArray(config[key])) {
          // parameter will be specified multiple times in config file if it is an array
          for (const value of config[key]) {
            if (value !== null)
              entries.push(`${key}=${value}`);
            else
              entries.push(`${key}`);
          }
        } else
          entries.push(`${key}=${config[key]}`);
      } else {
        entries.push(`${key}`); // a parameter without value
      }
    }
    const dst = `${this._getConfigDirectory()}/oc.conf`;
    await fs.writeFileAsync(dst, entries.join('\n'), {encoding: 'utf8'}) ;
  }

  async preparePasswd(config = {}) {
    log.info("Preparing passwd file...");
    const dst = `${this._getConfigDirectory()}/passwd`;
    await fs.writeFileAsync(dst, config.password, {encoding: 'utf8'});
  }

  async prepareServer(config = {}) {
    log.info("Preparing server file...");
    const dst = `${this._getConfigDirectory()}/server`;
    await fs.writeFileAsync(dst, config.server, {encoding: 'utf8'});
  }

  _getOutputDirectory() {
    return `${f.getHiddenFolder()}/run/docker/${this.profileId}/output`;
  }

  async _getDNSServersFromFile(file) {
    try {
      const str = await fs.readFileAsync(file, {encoding: 'utf8'});
      const ips = str.split(" ");

      if(!_.isEmpty(ips)) {
        return ips;
      }

    } catch(err) {
      log.error("Got error when getting DNS servers from file, err:", err);
    }

    return [];
  }

  async _getDNSServers() {
    const ipv4s = await this._getDNSServersFromFile(`${this._getOutputDirectory()}/nameserver.ipv4`);
    const ipv6s = await this._getDNSServersFromFile(`${this._getOutputDirectory()}/nameserver.ipv6`);

    return [...ipv4s, ...ipv6s]
      .map((x) => x.trim())
      .filter((x) => x !== "");
  }

  async getRoutedSubnets() {
    try {
      const base = await super.getRoutedSubnets();
      const file = `${this._getOutputDirectory()}/routes`;

      const str = await fs.readFileAsync(file, {encoding: 'utf8'});
      const routes = str.split(",");

      if(!_.isEmpty(routes)) {
        return routes.filter((x) => x !== "");
      }

    } catch(err) {
      log.error("Got error when getting routes from file, err:", err);
    }

    return [];
  }

  async __prepareAssets() {
    const config = await this.loadOriginUserConfig();

    if(_.isEmpty(config)) return;

    await this.prepareDockerCompose(config);
    await this.preparePasswd(config);
    await this.prepareServer(config);
    await this.prepareConfig(config);
  }

  static getProtocol() {
    return "oc";
  }

}

module.exports = OCDockerClient;
<|endoftext|>


<commit_before>/**
 * Copyright 2018 The AMP HTML Authors. All Rights Reserved.
 *
 * 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.
 */

/**
 * A whitelist of djb2a hashes of publisher origins.
 * @const {!Array<string>}
 */
export const ORIGIN_WHITELIST = [
  '3451824873',
  '834917366',
  '4273375831',
  '750731789',
  '3322156041',
  '878041739',
  '2199838184',
  '708478954',
  '142793127',
  '2414533450',
  '212690086',
  '799038630',
  '2128662947',
];
<commit_msg>Whitelist origins (#13567)<commit_after>/**
 * Copyright 2018 The AMP HTML Authors. All Rights Reserved.
 *
 * 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.
 */

/**
 * A whitelist of djb2a hashes of publisher origins.
 * @const {!Array<string>}
 */
export const ORIGIN_WHITELIST = [
  '3451824873',
  '834917366',
  '4273375831',
  '750731789',
  '3322156041',
  '878041739',
  '2199838184',
  '708478954',
  '142793127',
  '2414533450',
  '212690086',
  '799038630',
  '2128662947',
  '3561349461',
  '2930531110',
  '791888271',
  '3743398870',
  '4127351496',
  '3623074634',
  '3935672072',
  '4138748709',
  '2185544172',
  '805461297',
  '3018644774',
  '363894702',
  '4200069764',
  '3390751743',
  '1758506884',
  '1527955987',
  '2616846056',
  '2587850265',
  '4053566461',
  '1597805481',
  '759835262',
  '2231215830',
  '3408249191',
  '2718273594',
  '2479885882',
  '4118683051',
  '781950191',
  '3307671928',
  '4137746800',
  '2114974219',
  '3960376034',
  '2741673809',
  '1960384540',
  '1370863654',
  '2246978037',
  '3424311896',
  '3261687030',
  '3252080663',
  '525657889',
  '1085278050',
  '3309952347',
  '3060985947',
  '1515192136',
  '414191242',
  '4011166599',
  '3381390900',
  '1628432066',
  '2852627326',
  '3584992110',
  '773697188',
  '508331528',
  '3263349802',
  '976603789',
  '1531622827',
  '1040387537',
  '251839354',
  '3341054457',
  '1137284944',
  '2038626037',
  '192946894',
  '2933287666',
  '3132507840',
  '590180088',
  '2529138038',
  '2062991370',
  '2003114268',
  '2076974480',
  '3040972817',
  '270580042',
  '1348111506',
  '3133369938',
  '973612916',
  '3144086645',
  '2861134475',
  '1210508580',
  '2637085339',
  '3353312560',
  '2939338957',
  '1476740172',
  '1729406438',
  '1366298771',
  '475325125',
  '965728075',
  '4140762491',
  '667802223',
  '2845803411',
  '3129729985',
  '1081382723',
  '2529319010',
  '628256541',
  '1462086116',
  '3355368229',
  '1948987049',
  '3902596111',
  '2428857403',
  '4205379393',
  '3172876126',
  '354082441',
  '4050194446',
  '2514009950',
  '1866288580',
  '884942273',
  '552831033',
  '1279420495',
  '3284374556',
  '2375389254',
  '3162265419',
  '3670522374',
  '2812672743',
  '2887960156',
  '1604714840',
  '176099546',
  '722659024',
  '1844097179',
  '2284332078',
  '4083044353',
  '431449824',
  '2138194233',
  '1542890108',
  '2723434276',
  '1193086302',
  '2606715995',
  '2517493756',
  '2299758819',
  '3036809290',
  '3494887100',
  '481608910',
  '468972428',
  '3919621757',
  '1120549175',
  '3572504894',
  '795111685',
  '3535681127',
  '1066656902',
  '826702818',
  '3429649351',
  '990312922',
  '1548043628',
  '2491279653',
  '2761508292',
  '2118239665',
  '2738155690',
  '2930818498',
  '1765761661',
  '2325333010',
  '1926444709',
  '2632194804',
  '3700982698',
  '740460396',
  '959889676',
  '2271213671',
  '3821795909',
  '1048768124',
  '1734343726',
  '2934412348',
  '2797839121',
  '1390195319',
  '2207832807',
  '4177803870',
  '2326328585',
  '4143625838',
  '907695148',
  '4249223952',
  '2917909858',
  '3446215673',
  '3828921363',
  '1960309827',
  '3568064118',
  '802940860',
  '1061774354',
  '2725145135',
  '3022092377',
  '3887937944',
  '2038733725',
  '2795182078',
  '2362191459',
  '2051204756',
  '3866956400',
  '763580606',
  '10089108',
  '1048639639',
  '3181090214',
  '1314122247',
  '853535426',
  '3464002149',
  '1239612924',
  '2600368355',
  '3964022265',
  '179954814',
  '1645367571',
  '1911273138',
  '1958476175',
  '273580543',
  '4206479264',
  '932642421',
  '2872980659',
  '1641755208',
  '3574354948',
  '535848262',
  '2413038656',
  '3402978750',
  '3326846086',
  '1224563123',
  '2718446350',
  '2759976952',
  '2373437534',
  '2497925282',
  '989444878',
  '1172399292',
  '926902921',
  '338844244',
  '353380306',
  '1463873379',
  '2150296095',
  '3741803823',
  '3295776646',
  '399163616',
  '3385846727',
  '884037848',
  '1384885545',
  '690204840',
  '793360597',
  '1323716407',
  '1502183303',
  '1502183300',
  '3768651282',
  '1291285285',
  '3841844405',
  '1587531568',
  '1452555948',
  '4045001232',
  '809525143',
  '436035672',
  '3655120568',
  '3376790055',
  '3226517851',
  '2451485116',
  '4098138342',
  '2252559194',
  '1420964370',
  '4220526267',
  '613252162',
  '2374338088',
  '3711744991',
  '2498415300',
  '32924227',
  '335855777',
  '3551433075',
  '3591311390',
  '785278645',
  '3256754508',
  '4171920376',
  '4015960781',
  '1924394550',
  '1651834381',
  '2052997999',
  '166080441',
  '1657655384',
  '1358275160',
  '283661147',
  '1820719129',
  '1436589466',
  '760645918',
  '302251478',
  '962380673',
  '2236849121',
  '4051110478',
  '1024745460',
  '1762779619',
  '129196458',
  '4166688207',
  '836634675',
  '2972849537',
  '2388222966',
  '4237182114',
  '2486258543',
  '4189681769',
  '673490260',
  '2688673784',
  '3361466716',
  '122776143',
  '1980609434',
  '1275112483',
  '3355865416',
  '2214432888',
  '4102232185',
  '220103233',
  '1897877027',
  '1506356073',
  '1019348494',
  '4195946149',
  '886030867',
  '4136314176',
  '1221946827',
  '188153886',
  '3109674920',
  '4072394641',
  '1416203172',
  '154680092',
  '167065142',
  '675617839',
  '4031427958',
  '3450221390',
  '1574632236',
  '1219663970',
  '2825564215',
  '2866448207',
  '1424038674',
  '4062202631',
  '4205027822',
  '3458310975',
  '605645428',
  '2784479860',
  '2471412274',
  '1212905960',
  '2602595202',
  '684496063',
  '1790421447',
  '2465244303',
  '812914966',
  '2771839729',
  '1337085136',
  '509801075',
  '2927763036',
  '721006546',
  '166550074',
  '1452631355',
  '2247419316',
  '3916735828',
  '1296373605',
  '932940871',
  '2797844367',
  '1267058499',
  '616398681',
  '2311290044',
  '1224575240',
  '3692367215',
  '3931371606',
  '885000263',
  '2732398879',
  '817596813',
  '241505564',
  '1638733129',
  '3942169396',
  '436895478',
  '699835401',
  '3367171293',
  '2835331287',
  '1408213495',
  '2500743291',
  '2134891083',
  '779185847',
  '911963500',
  '4286009170',
  '2798104093',
  '2893590542',
  '455553168',
  '3090399922',
  '3473901382',
  '1182996899',
  '402077837',
  '3695202375',
  '1426205130',
  '1831449632',
  '1203629527',
  '3739222329',
  '1167928101',
  '3835330948',
  '3319768647',
  '3739723588',
  '220632861',
  '2487828098',
  '1873175698',
  '2911378586',
  '3216947982',
  '3785031815',
  '4092369519',
  '1885949806',
  '382915004',
  '2342924379',
  '1796631487',
  '2672599929',
  '1451865411',
  '646925241',
  '2636391541',
  '3416831537',
  '473408210',
  '3808515023',
  '4265092200',
  '101608808',
  '165218748',
  '676286387',
  '3718288426',
  '803626326',
];
<|endoftext|>


<commit_before>Behaviour.register({
	'.TableListField .externallink' : {
		onclick: function(e) {
			window.open(e.target.href);
			Event.stop(e);
			return false;
		}
	}
});

CMSWorkflow = {
	setOption: function(key, value) {
		if (typeof(this.data) == 'undefined') {
			this.data = {};
		}
		this.data[key] = value;
	},
	getOption: function(key) {
		if (typeof(this.data) != 'undefined' && typeof(this.data[key]) != 'undefined') {
			return this.data[key];
		}
		return null;
	},
	/**
	 * Prompt for input from the user and then submit the given form via ajax.
	 */
	submitWithPromptedMessage : function(form, button, msgVar, msgPrompt) {
		var messageEl = CMSWorkflow.createPromptElement(msgVar, msgPrompt);
		if (!messageEl) {
			return;
		}
		form.appendChild(messageEl);

		Ajax.SubmitForm(form, button, {
			onSuccess: Ajax.Evaluator,
			onFailure: ajaxErrorHandler
		});

		// Once Ajax.SubmitForm has been calld, this element is no longer necessary
		form.removeChild(messageEl);
	},

	createPromptElement: function(varName, promptText) {
		var message = prompt(promptText, "");
		if (message === null) {
			// User canceled prompt box
			return null;
		}
		var messageEl = document.createElement("input");
		messageEl.type = "hidden";
		messageEl.name = varName;
		messageEl.value = message;
		return messageEl;
	},

	/**
	 * Simple behaviour for an ajax button
	 */
	WorkflowButton : {
		onclick: function() {
			$('Form_EditForm').changeDetection_fieldsToIgnore['EmbargoExpiryTZConverter_TZ'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['EmbargoExpiryTZConverter_From_Date'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['EmbargoExpiryTZConverter_From_Time'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['EmbargoDate[Date]'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['EmbargoDate[Time]'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['ExpiryDate[Date]'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['ExpiryDate[Time]'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['DeletionScheduling'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['WorkflowComment'] = true;

			if ($('Form_EditForm').isChanged()) {
				if(!confirm('You have unsaved changes. You will lose them if you click OK.')) return false;
			}

			Ajax.SubmitForm($('Form_EditForm'), this.name, {
				onSuccess: Ajax.Evaluator,
				onFailure: ajaxErrorHandler
			});
			return false;
		}
	},

	showHideExpiry: {
		onclick: function() {
			if ($('deleteImmediate').checked) {
				$('expiryField').style.display = 'none';
			} else {
				$('expiryField').style.display = 'block';
			}
		}
	}
};

Behaviour.register({
	'#deleteImmediate' : CMSWorkflow.showHideExpiry,
	'#deleteLater' : CMSWorkflow.showHideExpiry,
	'#Form_EditForm_action_cms_requestedit' : CMSWorkflow.WorkflowButton,
	'#Form_EditForm_action_cms_approve' : CMSWorkflow.WorkflowButton,
	'#Form_EditForm_action_cms_deny' : CMSWorkflow.WorkflowButton,
	'#Form_EditForm_action_cms_comment' : CMSWorkflow.WorkflowButton,
	'#Form_EditForm_action_cms_publish' : CMSWorkflow.WorkflowButton,
	'#WorkflowActions #Form_EditForm_action_cms_requestpublication' : CMSWorkflow.WorkflowButton,
	'#WorkflowActions #Form_EditForm_action_cms_requestdeletefromlive' : CMSWorkflow.WorkflowButton
});

// Create these actions
function action_cms_requestpublication_right(e) {
	if ($('Form_EditForm').isChanged()) {
		if(!confirm('You have unsaved changes. You will lose them if you click to continue requesting publication.'))
			return false;
	}

	return CMSWorkflow.submitWithPromptedMessage(
			$('Form_EditForm'), 'action_cms_requestpublication',
			'WorkflowComment',
			'Please comment on the change you are asking to have published.'
	);
}

function action_cms_requestdeletefromlive_right(e) {
	CMSWorkflow.submitWithPromptedMessage(
			$('Form_EditForm'), 'action_cms_requestdeletefromlive',
			'WorkflowComment',
			'Please comment on why you are asking to have this page deleted.'
	);
}

var EmbargoExpiry = {
	save: function(what, el) {
		EmbargoExpiry.fieldCheck();

		var url = 'admin/cms_setembargoexpiry?wfRequest='+$('WorkflowRequest_ID').value;
		var ids = EmbargoExpiry.ids(what);

		if (what == 'embargo') {
			url += '&EmbargoDate='+escape($(ids.dateField).value)+'&EmbargoTime='+escape($(ids.timeField).value);
		} else if (what == 'expiry') {
			url += '&ExpiryDate='+escape($(ids.dateField).value)+'&ExpiryTime='+escape($(ids.timeField).value);
		}

		if ($(ids.dateField).value == '' || $(ids.timeField).value == '') {
			alert('You must fill out the '+what+' date and time fields');
			return;
		}

		$(el.id).className = 'action loading';
		new Ajax.Request(url, {
			method: 'get',
			onSuccess: function(t) {
				data = eval('('+t.responseText+')');
				if (data.status == 'success') {
					$(ids.wholeMessage).style.display = 'block';
					$(ids.dateTime).innerHTML = eval('data.message.'+what);
				} else { EmbargoExpiry.error(t); }
			},
			onFailure: function(t) { EmbargoExpiry.error(t); },
			onComplete: function(t) { $(el.id).className = 'action'; }
		});
	},
	reset: function(what, el) {
		ids = EmbargoExpiry.ids(what);
		var url = 'admin/cms_setembargoexpiry?wfRequest='+$('WorkflowRequest_ID').value;

		$(ids.dateField).value = '';
		$(ids.timeField).value = '';

		EmbargoExpiry.fieldCheck();

		if (what == 'embargo') {
			url += '&ResetEmbargo';
		} else if (what == 'expiry') {
			url += '&ResetExpiry';
		}

		$(el.id).className = 'action loading';
		new Ajax.Request(url, {
			method: 'get',
			onSuccess: function(t) {
				data = eval('('+t.responseText+')');
				if (data.status == 'success') {
					$(ids.wholeMessage).style.display = 'none';
				} else { EmbargoExpiry.error(t); }
			},
			onFailure: function(t) { EmbargoExpiry.error(t); },
			onComplete: function(t) { $(el.id).className = 'action'; }
		});
	},
	error: function(transport) {
		EmbargoExpiry.fieldCheck();
		alert('There was an error processing that request.');
	},
	ids: function(forWhat) {
		switch(forWhat) {
			case 'expiry':
				return {
					resetButton: 'resetExpiryButton',
					saveButton: 'saveExpiryButton',
					dateField: 'ExpiryDate_Date',
					timeField: 'ExpiryDate_Time',
					wholeMessage: 'embargoExpiry-expiryStatus',
					dateTime: 'expiryDate'
				};
			case 'embargo':
				return {
					resetButton: 'resetEmbargoButton',
					saveButton: 'saveEmbargoButton',
					dateField: 'EmbargoDate_Date',
					timeField: 'EmbargoDate_Time',
					wholeMessage: 'embargoExpiry-embargoStatus',
					dateTime: 'embargoDate'
				};
		}
	},
	eButton: function(id) {
		Element.removeClassName(id, 'disabled');
		$(id).disabled = false;
	},
	dButton: function(id) {
		Element.addClassName(id, 'disabled');
		$(id).disabled = true;
	},
	fieldCheck: function() {
		ids = EmbargoExpiry.ids('embargo');
		// Only call this logic if the date field & save button exist, otherwise it's unnecessary
		if($(ids.dateField) && $(ids.saveButton)) {
			if ($(ids.dateField).value == '' || $(ids.timeField).value == '') {
				EmbargoExpiry.dButton(ids.saveButton);
				EmbargoExpiry.dButton(ids.resetButton);
			} else {
				EmbargoExpiry.eButton(ids.saveButton);
				EmbargoExpiry.eButton(ids.resetButton);
			}
		}

		ids = EmbargoExpiry.ids('expiry');
		// Only call this logic if the date field & save button exist, otherwise it's unnecessary
		if($(ids.dateField) && $(ids.saveButton)) {
			if ($(ids.dateField).value == '' || $(ids.timeField).value == '') {
				EmbargoExpiry.dButton(ids.saveButton);
				EmbargoExpiry.dButton(ids.resetButton);
			} else {
				EmbargoExpiry.eButton(ids.saveButton);
				EmbargoExpiry.eButton(ids.resetButton);
			}
		}
	}
};

Behaviour.register({
	'#EmbargoDate_Date' : {
		onchange: EmbargoExpiry.fieldCheck,
		initialize: EmbargoExpiry.fieldCheck
	},
	'#EmbargoDate_Time' : { onchange: EmbargoExpiry.fieldCheck },
	'#ExpiryDate_Date' : { onchange: EmbargoExpiry.fieldCheck },
	'#ExpiryDate_Time' : { onchange: EmbargoExpiry.fieldCheck }
})

function action_publish_right(e) {
	var messageEl = null;
	if (CMSWorkflow.getOption('noPromptForAdmin')) {
		messageEl = document.createElement("input");
		messageEl.type = "hidden";
		messageEl.name = 'WorkflowComment';
	} else {
		messageEl = CMSWorkflow.createPromptElement('WorkflowComment', 'Please comment on this publication, if applicable.');
	}
	$('Form_EditForm').appendChild(messageEl);
	$('Form_EditForm_action_publish').value = ss.i18n._t('CMSMAIN.PUBLISHING');
	$('Form_EditForm_action_publish').className = 'action loading';
	$('Form_EditForm').save(false, null, 'cms_publishwithcomment', true);
	$('Form_EditForm').removeChild(messageEl);
}
<commit_msg>MINOR: Fix javascript syntax that can confuse IE (from r86393) (from r88431)<commit_after>Behaviour.register({
	'.TableListField .externallink' : {
		onclick: function(e) {
			window.open(e.target.href);
			Event.stop(e);
			return false;
		}
	}
});

CMSWorkflow = {
	setOption: function(key, value) {
		if (typeof(this.data) == 'undefined') {
			this.data = {};
		}
		this.data[key] = value;
	},
	getOption: function(key) {
		if (typeof(this.data) != 'undefined' && typeof(this.data[key]) != 'undefined') {
			return this.data[key];
		}
		return null;
	},
	/**
	 * Prompt for input from the user and then submit the given form via ajax.
	 */
	submitWithPromptedMessage : function(form, button, msgVar, msgPrompt) {
		var messageEl = CMSWorkflow.createPromptElement(msgVar, msgPrompt);
		if (!messageEl) {
			return;
		}
		form.appendChild(messageEl);

		Ajax.SubmitForm(form, button, {
			onSuccess: Ajax.Evaluator,
			onFailure: ajaxErrorHandler
		});

		// Once Ajax.SubmitForm has been calld, this element is no longer necessary
		form.removeChild(messageEl);
	},

	createPromptElement: function(varName, promptText) {
		var message = prompt(promptText, "");
		if (message === null) {
			// User canceled prompt box
			return null;
		}
		var messageEl = document.createElement("input");
		messageEl.type = "hidden";
		messageEl.name = varName;
		messageEl.value = message;
		return messageEl;
	},

	/**
	 * Simple behaviour for an ajax button
	 */
	WorkflowButton : {
		onclick: function() {
			$('Form_EditForm').changeDetection_fieldsToIgnore['EmbargoExpiryTZConverter_TZ'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['EmbargoExpiryTZConverter_From_Date'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['EmbargoExpiryTZConverter_From_Time'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['EmbargoDate[Date]'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['EmbargoDate[Time]'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['ExpiryDate[Date]'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['ExpiryDate[Time]'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['DeletionScheduling'] = true;
			$('Form_EditForm').changeDetection_fieldsToIgnore['WorkflowComment'] = true;

			if ($('Form_EditForm').isChanged()) {
				if(!confirm('You have unsaved changes. You will lose them if you click OK.')) return false;
			}

			Ajax.SubmitForm($('Form_EditForm'), this.name, {
				onSuccess: Ajax.Evaluator,
				onFailure: ajaxErrorHandler
			});
			return false;
		}
	},

	showHideExpiry: {
		onclick: function() {
			if ($('deleteImmediate').checked) {
				$('expiryField').style.display = 'none';
			} else {
				$('expiryField').style.display = 'block';
			}
		}
	}
};

Behaviour.register({
	'#deleteImmediate' : CMSWorkflow.showHideExpiry,
	'#deleteLater' : CMSWorkflow.showHideExpiry,
	'#Form_EditForm_action_cms_requestedit' : CMSWorkflow.WorkflowButton,
	'#Form_EditForm_action_cms_approve' : CMSWorkflow.WorkflowButton,
	'#Form_EditForm_action_cms_deny' : CMSWorkflow.WorkflowButton,
	'#Form_EditForm_action_cms_comment' : CMSWorkflow.WorkflowButton,
	'#Form_EditForm_action_cms_publish' : CMSWorkflow.WorkflowButton,
	'#WorkflowActions #Form_EditForm_action_cms_requestpublication' : CMSWorkflow.WorkflowButton,
	'#WorkflowActions #Form_EditForm_action_cms_requestdeletefromlive' : CMSWorkflow.WorkflowButton
});

// Create these actions
function action_cms_requestpublication_right(e) {
	if ($('Form_EditForm').isChanged()) {
		if(!confirm('You have unsaved changes. You will lose them if you click to continue requesting publication.'))
			return false;
	}

	return CMSWorkflow.submitWithPromptedMessage(
			$('Form_EditForm'), 'action_cms_requestpublication',
			'WorkflowComment',
			'Please comment on the change you are asking to have published.'
	);
}

function action_cms_requestdeletefromlive_right(e) {
	CMSWorkflow.submitWithPromptedMessage(
			$('Form_EditForm'), 'action_cms_requestdeletefromlive',
			'WorkflowComment',
			'Please comment on why you are asking to have this page deleted.'
	);
}

var EmbargoExpiry = {
	save: function(what, el) {
		EmbargoExpiry.fieldCheck();

		var url = 'admin/cms_setembargoexpiry?wfRequest='+$('WorkflowRequest_ID').value;
		var ids = EmbargoExpiry.ids(what);

		if (what == 'embargo') {
			url += '&EmbargoDate='+escape($(ids.dateField).value)+'&EmbargoTime='+escape($(ids.timeField).value);
		} else if (what == 'expiry') {
			url += '&ExpiryDate='+escape($(ids.dateField).value)+'&ExpiryTime='+escape($(ids.timeField).value);
		}

		if ($(ids.dateField).value == '' || $(ids.timeField).value == '') {
			alert('You must fill out the '+what+' date and time fields');
			return;
		}

		$(el.id).className = 'action loading';
		new Ajax.Request(url, {
			method: 'get',
			onSuccess: function(t) {
				data = eval('('+t.responseText+')');
				if (data.status == 'success') {
					$(ids.wholeMessage).style.display = 'block';
					$(ids.dateTime).innerHTML = eval('data.message.'+what);
				} else { EmbargoExpiry.error(t); }
			},
			onFailure: function(t) { EmbargoExpiry.error(t); },
			onComplete: function(t) { $(el.id).className = 'action'; }
		});
	},
	reset: function(what, el) {
		ids = EmbargoExpiry.ids(what);
		var url = 'admin/cms_setembargoexpiry?wfRequest='+$('WorkflowRequest_ID').value;

		$(ids.dateField).value = '';
		$(ids.timeField).value = '';

		EmbargoExpiry.fieldCheck();

		if (what == 'embargo') {
			url += '&ResetEmbargo';
		} else if (what == 'expiry') {
			url += '&ResetExpiry';
		}

		$(el.id).className = 'action loading';
		new Ajax.Request(url, {
			method: 'get',
			onSuccess: function(t) {
				data = eval('('+t.responseText+')');
				if (data.status == 'success') {
					$(ids.wholeMessage).style.display = 'none';
				} else { EmbargoExpiry.error(t); }
			},
			onFailure: function(t) { EmbargoExpiry.error(t); },
			onComplete: function(t) { $(el.id).className = 'action'; }
		});
	},
	error: function(transport) {
		EmbargoExpiry.fieldCheck();
		alert('There was an error processing that request.');
	},
	ids: function(forWhat) {
		switch(forWhat) {
			case 'expiry':
				return {
					resetButton: 'resetExpiryButton',
					saveButton: 'saveExpiryButton',
					dateField: 'ExpiryDate_Date',
					timeField: 'ExpiryDate_Time',
					wholeMessage: 'embargoExpiry-expiryStatus',
					dateTime: 'expiryDate'
				};
			case 'embargo':
				return {
					resetButton: 'resetEmbargoButton',
					saveButton: 'saveEmbargoButton',
					dateField: 'EmbargoDate_Date',
					timeField: 'EmbargoDate_Time',
					wholeMessage: 'embargoExpiry-embargoStatus',
					dateTime: 'embargoDate'
				};
		}
	},
	eButton: function(id) {
		Element.removeClassName(id, 'disabled');
		$(id).disabled = false;
	},
	dButton: function(id) {
		Element.addClassName(id, 'disabled');
		$(id).disabled = true;
	},
	fieldCheck: function() {
		ids = EmbargoExpiry.ids('embargo');
		// Only call this logic if the date field & save button exist, otherwise it's unnecessary
		if($(ids.dateField) && $(ids.saveButton)) {
			if ($(ids.dateField).value == '' || $(ids.timeField).value == '') {
				EmbargoExpiry.dButton(ids.saveButton);
				EmbargoExpiry.dButton(ids.resetButton);
			} else {
				EmbargoExpiry.eButton(ids.saveButton);
				EmbargoExpiry.eButton(ids.resetButton);
			}
		}

		ids = EmbargoExpiry.ids('expiry');
		// Only call this logic if the date field & save button exist, otherwise it's unnecessary
		if($(ids.dateField) && $(ids.saveButton)) {
			if ($(ids.dateField).value == '' || $(ids.timeField).value == '') {
				EmbargoExpiry.dButton(ids.saveButton);
				EmbargoExpiry.dButton(ids.resetButton);
			} else {
				EmbargoExpiry.eButton(ids.saveButton);
				EmbargoExpiry.eButton(ids.resetButton);
			}
		}

		window.setTimeout('EmbargoExpiry.fieldCheck();', 1000);
	}
};

Behaviour.register({
	'#EmbargoDate_Date' : {
		onchange: EmbargoExpiry.fieldCheck,
		initialize: EmbargoExpiry.fieldCheck
	},
	'#EmbargoDate_Time' : { onchange: EmbargoExpiry.fieldCheck },
	'#ExpiryDate_Date' : { onchange: EmbargoExpiry.fieldCheck },
	'#ExpiryDate_Time' : { onchange: EmbargoExpiry.fieldCheck }
})

function action_publish_right(e) {
	var messageEl = null;
	if (CMSWorkflow.getOption('noPromptForAdmin')) {
		messageEl = document.createElement("input");
		messageEl.type = "hidden";
		messageEl.name = 'WorkflowComment';
	} else {
		messageEl = CMSWorkflow.createPromptElement('WorkflowComment', 'Please comment on this publication, if applicable.');
	}
	$('Form_EditForm').appendChild(messageEl);
	$('Form_EditForm_action_publish').value = ss.i18n._t('CMSMAIN.PUBLISHING');
	$('Form_EditForm_action_publish').className = 'action loading';
	$('Form_EditForm').save(false, null, 'cms_publishwithcomment', true);
	$('Form_EditForm').removeChild(messageEl);
}
<|endoftext|>