<commit_before>/*
 * Copyright 2019 EPAM Systems
 *
 * 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.
 */

import { takeEvery, all, put, call, select, take } from 'redux-saga/effects';
import { URLS } from 'common/urls';
import { getStorageItem } from 'common/utils';
import { concatFetchDataAction, createFetchPredicate } from 'controllers/fetch';
import { activeProjectSelector } from 'controllers/user';
import {
  isTestItemsListSelector,
  namespaceSelector,
  fetchTestItemsAction,
  SET_PAGE_LOADING,
  DEFAULT_LAUNCHES_LIMIT,
} from 'controllers/testItem';
import {
  testItemIdsArraySelector,
  filterIdSelector,
  launchIdSelector,
  pagePropertiesSelector,
} from 'controllers/pages';
import { isOldHistorySelector } from 'controllers/appInfo';
import {
  fetchItemsHistoryAction,
  resetHistoryAction,
  setHistoryPageLoadingAction,
  fetchFilterHistoryAction,
} from './actionCreators';
import { historyPaginationSelector, filterForCompareSelector, historySelector } from './selectors';
import {
  HISTORY_ITEMS_TO_LOAD,
  FETCH_ITEMS_HISTORY,
  HISTORY_DEPTH_CONFIG,
  HISTORY_BASE_DEFAULT_VALUE,
  NAMESPACE,
  FILTER_HISTORY_NAMESPACE,
  FETCH_HISTORY_PAGE_INFO,
  REFRESH_HISTORY,
  SET_FILTER_FOR_COMPARE,
  FETCH_FILTER_HISTORY,
  UNIQUE_ID_GROUPING_FIELD,
  TEST_CASE_HASH_GROUPING_FIELD,
} from './constants';

function* getHistoryParams({ loadMore } = {}) {
  const pagination = yield select(historyPaginationSelector);
  const itemIdsArray = yield select(testItemIdsArraySelector);
  const launchId = yield select(launchIdSelector);
  const namespace = yield select(namespaceSelector);
  const query = yield select(pagePropertiesSelector, namespace);
  const isTestItemsList = yield select(isTestItemsListSelector);

  const pageNumber = loadMore ? pagination.number + 1 : pagination.number;
  const parentItemId = itemIdsArray.length > 1 ? itemIdsArray[itemIdsArray.length - 1] : undefined;
  const params = {
    ...query,
    'page.page': pageNumber,
    'page.size': pagination.size,
  };
  if (parentItemId) {
    params['filter.eq.parentId'] = parentItemId;
  } else if (launchId) {
    params['filter.eq.launchId'] = launchId;
  } else if (isTestItemsList) {
    params.filterId = yield select(filterIdSelector);
  }

  return params;
}

function* fetchItemsHistory({ payload = {} }) {
  if (payload.loadMore) {
    yield put(setHistoryPageLoadingAction(true));
  }
  const activeProject = yield select(activeProjectSelector);
  const params = yield call(getHistoryParams, payload);

  const historyDepth =
    payload.historyDepth ||
    getStorageItem(HISTORY_DEPTH_CONFIG.name) ||
    HISTORY_DEPTH_CONFIG.defaultValue;
  const historyBase = payload.historyBase || HISTORY_BASE_DEFAULT_VALUE;

  yield put(
    concatFetchDataAction(NAMESPACE, payload.loadMore)(
      URLS.testItemsHistory(activeProject, historyDepth, historyBase),
      { params },
    ),
  );

  if (payload.loadMore) {
    yield take(createFetchPredicate(NAMESPACE));
    const filterForCompare = yield select(filterForCompareSelector);
    if (filterForCompare) {
      yield put(fetchFilterHistoryAction({ filter: filterForCompare, loadMore: payload.loadMore }));
      yield take(createFetchPredicate(FILTER_HISTORY_NAMESPACE));
    }
    yield put(setHistoryPageLoadingAction(false));
  }
}

function* fetchHistoryPageInfo() {
  yield put(setHistoryPageLoadingAction(true));

  yield put(resetHistoryAction());
  yield put(fetchTestItemsAction());
  yield take((action) => action.type === SET_PAGE_LOADING && action.payload === false);
  yield put(fetchItemsHistoryAction());

  yield take(createFetchPredicate(NAMESPACE));
  yield put(setHistoryPageLoadingAction(false));
}

function* refreshHistory({ payload }) {
  yield put(setHistoryPageLoadingAction(true));
  yield put(resetHistoryAction());
  yield put(fetchItemsHistoryAction(payload || undefined));
  yield take(createFetchPredicate(NAMESPACE));

  const filterForCompare = yield select(filterForCompareSelector);
  if (filterForCompare) {
    yield put(fetchFilterHistoryAction({ filter: filterForCompare }));
    yield take(createFetchPredicate(FILTER_HISTORY_NAMESPACE));
  }
  yield put(setHistoryPageLoadingAction(false));
}

function* fetchFilterHistory({ payload: { filter, loadMore } }) {
  const activeProject = yield select(activeProjectSelector);
  const itemsHistory = yield select(historySelector);
  const isOldHistory = yield select(isOldHistorySelector);
  const historyGroupingFieldKey = isOldHistory
    ? UNIQUE_ID_GROUPING_FIELD
    : TEST_CASE_HASH_GROUPING_FIELD;

  if (!itemsHistory.length) {
    return;
  }

  const historyDepth = 1;
  const params = {
    filterId: filter.id,
    launchesLimit: DEFAULT_LAUNCHES_LIMIT,
    isLatest: true,
  };
  let items = itemsHistory;

  if (loadMore) {
    items = itemsHistory.slice(-HISTORY_ITEMS_TO_LOAD);
  }
  params[`filter.in.${historyGroupingFieldKey}`] = items
    .map((item) => item.groupingField)
    .join(',');

  yield put(
    concatFetchDataAction(FILTER_HISTORY_NAMESPACE, loadMore)(
      URLS.testItemsHistory(activeProject, historyDepth, 'comparing'),
      {
        params,
      },
    ),
  );
}

function* watchFetchHistory() {
  yield takeEvery(FETCH_ITEMS_HISTORY, fetchItemsHistory);
}

function* watchFetchHistoryPageInfo() {
  yield takeEvery(FETCH_HISTORY_PAGE_INFO, fetchHistoryPageInfo);
}

function* watchRefreshHistory() {
  yield takeEvery([REFRESH_HISTORY, SET_FILTER_FOR_COMPARE], refreshHistory);
}

function* watchFetchFilterHistory() {
  yield takeEvery(FETCH_FILTER_HISTORY, fetchFilterHistory);
}

export function* historySagas() {
  yield all([
    watchFetchHistory(),
    watchFetchHistoryPageInfo(),
    watchRefreshHistory(),
    watchFetchFilterHistory(),
  ]);
}
<commit_msg>EPMRPP-45303 History table. Update parameters for suite level history<commit_after>/*
 * Copyright 2019 EPAM Systems
 *
 * 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.
 */

import { takeEvery, all, put, call, select, take } from 'redux-saga/effects';
import { URLS } from 'common/urls';
import { getStorageItem } from 'common/utils';
import { concatFetchDataAction, createFetchPredicate } from 'controllers/fetch';
import { activeProjectSelector } from 'controllers/user';
import { LEVEL_SUITE } from 'common/constants/launchLevels';
import {
  isTestItemsListSelector,
  levelSelector,
  namespaceSelector,
  fetchTestItemsAction,
  SET_PAGE_LOADING,
  DEFAULT_LAUNCHES_LIMIT,
} from 'controllers/testItem';
import {
  testItemIdsArraySelector,
  filterIdSelector,
  launchIdSelector,
  pagePropertiesSelector,
} from 'controllers/pages';
import { isOldHistorySelector } from 'controllers/appInfo';
import {
  fetchItemsHistoryAction,
  resetHistoryAction,
  setHistoryPageLoadingAction,
  fetchFilterHistoryAction,
} from './actionCreators';
import { historyPaginationSelector, filterForCompareSelector, historySelector } from './selectors';
import {
  HISTORY_ITEMS_TO_LOAD,
  FETCH_ITEMS_HISTORY,
  HISTORY_DEPTH_CONFIG,
  HISTORY_BASE_DEFAULT_VALUE,
  NAMESPACE,
  FILTER_HISTORY_NAMESPACE,
  FETCH_HISTORY_PAGE_INFO,
  REFRESH_HISTORY,
  SET_FILTER_FOR_COMPARE,
  FETCH_FILTER_HISTORY,
  UNIQUE_ID_GROUPING_FIELD,
  TEST_CASE_HASH_GROUPING_FIELD,
} from './constants';

function* getHistoryParams({ loadMore } = {}) {
  const pagination = yield select(historyPaginationSelector);
  const itemIdsArray = yield select(testItemIdsArraySelector);
  const launchId = yield select(launchIdSelector);
  const namespace = yield select(namespaceSelector);
  const query = yield select(pagePropertiesSelector, namespace);
  const isTestItemsList = yield select(isTestItemsListSelector);
  const level = yield select(levelSelector);

  const pageNumber = loadMore ? pagination.number + 1 : pagination.number;
  const parentItemId = itemIdsArray.length > 1 ? itemIdsArray[itemIdsArray.length - 1] : undefined;
  const params = {
    ...query,
    'page.page': pageNumber,
    'page.size': pagination.size,
  };
  if (parentItemId) {
    params['filter.eq.parentId'] = parentItemId;
  } else if (launchId) {
    params['filter.eq.launchId'] = launchId;

    if (level === LEVEL_SUITE) {
      params['filter.!ex.parentId'] = true;
    }
  } else if (isTestItemsList) {
    params.filterId = yield select(filterIdSelector);
  }

  return params;
}

function* fetchItemsHistory({ payload = {} }) {
  if (payload.loadMore) {
    yield put(setHistoryPageLoadingAction(true));
  }
  const activeProject = yield select(activeProjectSelector);
  const params = yield call(getHistoryParams, payload);

  const historyDepth =
    payload.historyDepth ||
    getStorageItem(HISTORY_DEPTH_CONFIG.name) ||
    HISTORY_DEPTH_CONFIG.defaultValue;
  const historyBase = payload.historyBase || HISTORY_BASE_DEFAULT_VALUE;

  yield put(
    concatFetchDataAction(NAMESPACE, payload.loadMore)(
      URLS.testItemsHistory(activeProject, historyDepth, historyBase),
      { params },
    ),
  );

  if (payload.loadMore) {
    yield take(createFetchPredicate(NAMESPACE));
    const filterForCompare = yield select(filterForCompareSelector);
    if (filterForCompare) {
      yield put(fetchFilterHistoryAction({ filter: filterForCompare, loadMore: payload.loadMore }));
      yield take(createFetchPredicate(FILTER_HISTORY_NAMESPACE));
    }
    yield put(setHistoryPageLoadingAction(false));
  }
}

function* fetchHistoryPageInfo() {
  yield put(setHistoryPageLoadingAction(true));

  yield put(resetHistoryAction());
  yield put(fetchTestItemsAction());
  yield take((action) => action.type === SET_PAGE_LOADING && action.payload === false);
  yield put(fetchItemsHistoryAction());

  yield take(createFetchPredicate(NAMESPACE));
  yield put(setHistoryPageLoadingAction(false));
}

function* refreshHistory({ payload }) {
  yield put(setHistoryPageLoadingAction(true));
  yield put(resetHistoryAction());
  yield put(fetchItemsHistoryAction(payload || undefined));
  yield take(createFetchPredicate(NAMESPACE));

  const filterForCompare = yield select(filterForCompareSelector);
  if (filterForCompare) {
    yield put(fetchFilterHistoryAction({ filter: filterForCompare }));
    yield take(createFetchPredicate(FILTER_HISTORY_NAMESPACE));
  }
  yield put(setHistoryPageLoadingAction(false));
}

function* fetchFilterHistory({ payload: { filter, loadMore } }) {
  const activeProject = yield select(activeProjectSelector);
  const itemsHistory = yield select(historySelector);
  const isOldHistory = yield select(isOldHistorySelector);
  const historyGroupingFieldKey = isOldHistory
    ? UNIQUE_ID_GROUPING_FIELD
    : TEST_CASE_HASH_GROUPING_FIELD;

  if (!itemsHistory.length) {
    return;
  }

  const historyDepth = 1;
  const params = {
    filterId: filter.id,
    launchesLimit: DEFAULT_LAUNCHES_LIMIT,
    isLatest: true,
  };
  let items = itemsHistory;

  if (loadMore) {
    items = itemsHistory.slice(-HISTORY_ITEMS_TO_LOAD);
  }
  params[`filter.in.${historyGroupingFieldKey}`] = items
    .map((item) => item.groupingField)
    .join(',');

  yield put(
    concatFetchDataAction(FILTER_HISTORY_NAMESPACE, loadMore)(
      URLS.testItemsHistory(activeProject, historyDepth, 'comparing'),
      {
        params,
      },
    ),
  );
}

function* watchFetchHistory() {
  yield takeEvery(FETCH_ITEMS_HISTORY, fetchItemsHistory);
}

function* watchFetchHistoryPageInfo() {
  yield takeEvery(FETCH_HISTORY_PAGE_INFO, fetchHistoryPageInfo);
}

function* watchRefreshHistory() {
  yield takeEvery([REFRESH_HISTORY, SET_FILTER_FOR_COMPARE], refreshHistory);
}

function* watchFetchFilterHistory() {
  yield takeEvery(FETCH_FILTER_HISTORY, fetchFilterHistory);
}

export function* historySagas() {
  yield all([
    watchFetchHistory(),
    watchFetchHistoryPageInfo(),
    watchRefreshHistory(),
    watchFetchFilterHistory(),
  ]);
}
<|endoftext|>


<commit_before>
var sbMultipleService = {

	get FILTER()  { return document.getElementById("sbFilter"); },
	get STATUS()  { return document.getElementById("sbStatus"); },
	get TEXTBOX() { return document.getElementById("ScrapBookTextbox"); },

	vorhLinks : [],
	allURLs : [],
	allTitles : [],
	selURLs : [],
	selTitles : [],
	currentID : null,
	lastID : null,

	init : function()
	{
		document.documentElement.buttons = "accept,cancel,extra2";
		document.documentElement.getButton("accept").label = sbCommonUtils.lang("scrapbook", "CAPTURE_OK_BUTTON");
		document.documentElement.getButton("accept").accesskey = "C";
		this.TEXTBOX.focus();
		sbFolderSelector2.init();
		setTimeout(function(){ sbMultipleService.pasteClipboardURL(); }, 0);
	},

	done : function()
	{
		var allURLs = [];
		var urlList = [];
		var namList = [];
		var urlHash = {};
		var lines = this.TEXTBOX.value.split("\n");
		var charset = document.getElementById("sbCharset").value;
		var timeout = parseInt("0" + document.getElementById("sbTimeout").value, 10);
		for ( var i = 0; i < lines.length; i++ )
		{
			if ( lines[i].length > 5 ) urlHash[lines[i]] = true;
		}
		for ( var url in urlHash ) { allURLs.push(url); }
		if ( allURLs.length < 1 ) return;
		//Verbliebene Links trennen
		for ( var i = 0; i < allURLs.length; i++ )
		{
			lines = allURLs[i].split("|");
			urlList[i] = lines.shift();
			if ( lines.length )
			{
				namList[i] = lines.join("|");
			} else
			{
				namList[i] = "";
			}
		}
		if (document.getElementById("sbLinktitle").value == "ScrapBook")
		{
			window.openDialog(
				"chrome://scrapbook/content/capture.xul", "", "chrome,centerscreen,all,resizable,dialog=no",
				urlList, "", false, sbFolderSelector2.resURI, 0, null, null, null, null, "SB", charset, timeout
				);
		} else
		{
			window.openDialog(
				"chrome://scrapbook/content/capture.xul", "", "chrome,centerscreen,all,resizable,dialog=no",
				urlList, "", false, sbFolderSelector2.resURI, 0, null, null, null, null, "SB", charset, timeout, namList
				);
		}
	},

	addURL : function(auAllHash, auExclude)
	{
		var auAll = "";
		var auSelected = 0;
		var auCount = 0;
		var auFilter = this.FILTER.value;
		if ( auExclude == null )
		{
			auExclude = document.getElementById("sbpExcludeExistingAddresses").checked;
		}
		for ( var auURL in auAllHash )
		{
			auCount++;
			this.allURLs.push(auURL);
			this.allTitles.push(auAllHash[auURL]);
		}
		if ( auCount > 0 )
		{
			//Vergleichen mit Ausschlußliste und Co
			this.currentID = sbFolderSelector2.resURI;
			if ( this.currentID != this.lastID ) this.detectExistingLinks();
			for ( var auI=0; auI<this.allURLs.length; auI++ )
			{
				if ( this.allURLs[auI].match(auFilter) )
				{
					//Abgleich mit Ausschlussliste
					var auDoppelt = 0;
					if ( auExclude )
					{
						for ( var auJ = 0; auJ < this.vorhLinks.length; auJ++)
						{
							if ( this.vorhLinks[auJ] == this.allURLs[auI] )
							{
								auDoppelt = 1;
								auJ = this.vorhLinks.length;
							}
						}
					}
					if ( auDoppelt == 0 )
					{
						auSelected++;
						this.selURLs.push(this.allURLs[auI]);
						this.selTitles.push(this.allTitles[auI]);
						auAll += this.allURLs[auI]+"|"+this.allTitles[auI]+"\n";
					}
				}
			}
			document.getElementById("sbpCounter").setAttribute("value", auSelected+" \/ "+auCount);
		} else
		{
			document.getElementById("sbpCounter").setAttribute("value", "");
		}
		this.TEXTBOX.value = auAll;
	},

	clear : function()
	{
		this.TEXTBOX.value = "";
	},

	pasteClipboardURL : function()
	{
		var pcuAllHash = {};
		var pcuLines = [];
		this.allURLs = [];
		this.allTitles = [];
		try
		{
			var clip  = Components.classes['@mozilla.org/widget/clipboard;1'].createInstance(Components.interfaces.nsIClipboard);
			if ( !clip ) return false;
			var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
			if ( !trans ) return false;
			if ( 'init' in trans ) trans.init(null);
			trans.addDataFlavor("text/unicode");
			clip.getData(trans, clip.kGlobalClipboard);
			var str = new Object();
			var len = new Object();
			trans.getTransferData("text/unicode", str, len);
			if ( str )
			{
				str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
				pcuLines = str.toString().split("\n");
				for ( var i = 0; i < pcuLines.length; i++ )
				{
					if ( pcuLines[i].match(/^(http|https|ftp|file):\/\//) )
					{
						var pcuGetrennt = pcuLines[i].split("\;");
						if ( pcuGetrennt.length > 1 )
						{
							pcuAllHash[pcuGetrennt[0]] = pcuGetrennt[1];
						} else
						{
							pcuAllHash[pcuGetrennt[0]] = "";
						}
					}
				}
				this.addURL(pcuAllHash);
			}
		} catch(ex) {
		}
	},

	detectURLsOfTabs : function()
	{
		this.clear();
		var duotURL = "";
		var duotAllHash = {};
		var nodes = window.opener.gBrowser.mTabContainer.childNodes;
		this.allURLs = [];
		this.allTitles = [];
		for ( var i = 0; i < nodes.length; i++ )
		{
			duotURL = window.opener.gBrowser.getBrowserForTab(nodes[i]).contentDocument.location.href;
			if ( duotURL.match(/^(http|https|ftp|file):\/\//) )
			{
				duotAllHash[duotURL] = "";
			}
		}
		this.addURL(duotAllHash);
	},

	detectURLsInPage : function()
	{
		this.clear();
		var duipURL = "";
		var duipAllHash = {};
		var node = window.opener.top.content.document.body;
		this.allURLs = [];
		this.allTitles = [];
		traceTree : while ( true )
		{
			if ( node instanceof HTMLAnchorElement || node instanceof HTMLAreaElement )
			{
				duipURL = node.href;
				if ( duipURL.match(/^(http|https|ftp|file):\/\//) )
				{
					duipAllHash[duipURL] = node.text.replace(/^\s+/, '').replace(/\s+$/, '').replace(/\n/, ' ');
				}
			}
			if ( node.hasChildNodes() ) node = node.firstChild;
			else
			{
				while ( !node.nextSibling ) { node = node.parentNode; if ( !node ) break traceTree; }
				node = node.nextSibling;
			}
		}
		this.addURL(duipAllHash);
	},

	detectURLsInSelection : function()
	{
		this.clear();
		var duisURL = "";
		var duisAllHash = {};
		var sel = window.opener.top.sbPageEditor.getSelection();
		if ( !sel )
		{
			document.getElementById("sbpCounter").setAttribute("value", "");
			return;
		}
		var selRange  = sel.getRangeAt(0);
		var node = selRange.startContainer;
		if ( node.nodeName == "#text" ) node = node.parentNode;
		var nodeRange = window.opener.top.content.document.createRange();
		this.allURLs = [];
		traceTree : while ( true )
		{
			nodeRange.selectNode(node);
			if ( nodeRange.compareBoundaryPoints(Range.START_TO_END, selRange) > -1 )
			{
				if ( nodeRange.compareBoundaryPoints(Range.END_TO_START, selRange) > 0 ) break;
				else if ( node instanceof HTMLAnchorElement || node instanceof HTMLAreaElement )
				{
					duisURL = node.href;
					if ( duisURL.match(/^(http|https|ftp|file):\/\//) )
					{
						duisAllHash[duisURL] = node.text.replace(/^\s+/, '').replace(/\s+$/, '').replace(/\n/, ' ');
					}
				}
			}
			if ( node.hasChildNodes() ) node = node.firstChild;
			else
			{
				while ( !node.nextSibling ) { node = node.parentNode; if ( !node ) break traceTree; }
				node = node.nextSibling;
			}
		}
		this.addURL(duisAllHash);
	},

	detectExistingLinks : function()
	{
		//Funktion ermittelt die Links der vorhandenen Einträge im aktuell gewählten Zielverzeichnis
		var delResource = null;
		var delRDFCont = null;
		var delResEnum = [];
		this.vorhLinks = [];
		this.lastID = this.currentID;
		delResource = sbCommonUtils.RDF.GetResource(this.currentID);
		delRDFCont = Components.classes['@mozilla.org/rdf/container;1'].createInstance(Components.interfaces.nsIRDFContainer);
		delRDFCont.Init(sbDataSource.data, delResource);
		delResEnum = delRDFCont.GetElements();
		while ( delResEnum.hasMoreElements() )
		{
			var delRes = delResEnum.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
			if ( !sbDataSource.isContainer(delRes) )
			{
				this.vorhLinks.push(sbDataSource.getProperty(delRes, "source"));
			}
		}
	},

	updateSelection : function(usEvent)
	{
		//Funktion aktualisiert den Inhalt der aktuellen Auswahl
		var usCount = this.allURLs.length;
		var usAllHash = {};
		var usExclude = true;
		usExclude = document.getElementById("sbpExcludeExistingAddresses").checked;
		if ( usEvent )
		{
			if ( usEvent.button == 0 )
			{
				if ( usExclude )
				{
					usExclude = false;
				} else
				{
					usExclude = true;
				}
			}
		}
		for ( var i=0; i<usCount; i++ )
		{
			usAllHash[this.allURLs[i]] = this.allTitles[i];
		}
		this.allURLs = [];
		this.allTitles = [];
		this.addURL(usAllHash, usExclude);
	},

	toggleMethod : function()
	{
		//Funktion aktiviert bzw. deaktiviert die Zeichensatzauswahl
		var tmMethod = document.getElementById("sbMethod").value;
		if ( tmMethod == "SB" )
		{
			document.getElementById("sbCharset").disabled = false;
		} else
		{
			document.getElementById("sbCharset").disabled = true;
		}
	},

};

var sbURLDetector1 = {

	index : 0,

	run : function()
	{
		this.index = 0;
		var FP = Components.classes['@mozilla.org/filepicker;1'].createInstance(Components.interfaces.nsIFilePicker);
		FP.init(window, "", FP.modeGetFolder);
		var answer = FP.show();
		if ( answer == FP.returnOK )
		{
			sbMultipleService.clear();
			this.inspectDirectory(FP.file, 0);
		}
	},

	inspectDirectory : function(aDir, curIdx)
	{
		sbMultipleService.STATUS.value = sbCommonUtils.lang("scrapbook", "SCANNING_DIR", [curIdx, this.index, aDir.path]);
		var entries = aDir.directoryEntries;
		while ( entries.hasMoreElements() )
		{
			var entry = entries.getNext().QueryInterface(Components.interfaces.nsILocalFile);
			if ( entry.isDirectory() ) {
				this.inspectDirectoryWithDelay(entry, ++this.index);
			} else {
				if ( entry.leafName.match(/\.(html|htm)$/i) ) {
					var hash = {};
					hash[sbCommonUtils.convertFilePathToURL(entry.path)] = "";
					sbMultipleService.addURL(hash);
				}
			}
		}
		if ( curIdx == this.index ) sbMultipleService.STATUS.value = "";
	},

	inspectDirectoryWithDelay : function(aDir, aIndex)
	{
		setTimeout(function(){ sbURLDetector1.inspectDirectory(aDir, aIndex); }, 200 * aIndex);
	},

};

var sbURLDetector2 = {

	type   : "",
	index  : 0,
	lines  : [],
	result : "",
	weboxBaseURL : "",

	run : function(aType)
	{
		this.type = aType;
		this.index = 0;
		this.lines = [];
		this.result = "";
		this.weboxBaseURL = "";
		var theFile ;
		if ( this.type == "W" ) {
			var FP = Components.classes['@mozilla.org/filepicker;1'].createInstance(Components.interfaces.nsIFilePicker);
			FP.init(window, "Select default.html of WeBoX.", FP.modeOpen);
			FP.appendFilters(FP.filterHTML);
			var answer = FP.show();
			if ( answer == FP.returnOK ) theFile = FP.file;
			else return;
			this.weboxBaseURL = theFile.parent.path + '\\Data\\';
		} else {
			theFile = sbCommonUtils.DIR.get("ProfD", Components.interfaces.nsIFile);
			theFile.append("bookmarks.html");
			if ( !theFile.exists() ) return;
		}
		sbMultipleService.clear();
		this.lines = sbCommonUtils.readFile(theFile).split("\n");
		this.inspect();
	},

	inspect : function()
	{
		sbMultipleService.STATUS.value = sbCommonUtils.lang("scrapbook", "SCANNING", [this.index, (this.lines.length-1)]);
		this.result += "\n";
		if ( this.type == "W" ) {
			if ( this.lines[this.index].match(/ LOCALFILE\=\"([^\"]+)\" /) )
				this.result += sbCommonUtils.convertFilePathToURL(this.weboxBaseURL + RegExp.$1);
		} else {
			if ( this.lines[this.index].match(/ HREF\=\"([^\"]+)\" /) )
				this.result += RegExp.$1;
		}
		if ( ++this.index < this.lines.length ) {
			setTimeout(function(){ sbURLDetector2.inspect(); }, 0);
		} else {
			this.result = this.result.replace(/\n\n+/g, "\n\n");
			this.result = this.result.replace(/^\n+/, "");
			sbMultipleService.TEXTBOX.value = this.result;
			sbMultipleService.STATUS.value = "";
		}
	},

};<commit_msg>Replace "|" with " " (2 spaces), which is always URI encoded when we read an href, is rare seen (even arbitrary input), and is visual friendly.<commit_after>
var sbMultipleService = {

	get FILTER()  { return document.getElementById("sbFilter"); },
	get STATUS()  { return document.getElementById("sbStatus"); },
	get TEXTBOX() { return document.getElementById("ScrapBookTextbox"); },

	vorhLinks : [],
	allURLs : [],
	allTitles : [],
	selURLs : [],
	selTitles : [],
	currentID : null,
	lastID : null,

	init : function()
	{
		document.documentElement.buttons = "accept,cancel,extra2";
		document.documentElement.getButton("accept").label = sbCommonUtils.lang("scrapbook", "CAPTURE_OK_BUTTON");
		document.documentElement.getButton("accept").accesskey = "C";
		this.TEXTBOX.focus();
		sbFolderSelector2.init();
		setTimeout(function(){ sbMultipleService.pasteClipboardURL(); }, 0);
	},

	done : function()
	{
		var allURLs = [];
		var urlList = [];
		var namList = [];
		var urlHash = {};
		var lines = this.TEXTBOX.value.split("\n");
		var charset = document.getElementById("sbCharset").value;
		var timeout = parseInt("0" + document.getElementById("sbTimeout").value, 10);
		for ( var i = 0; i < lines.length; i++ )
		{
			if ( lines[i].length > 5 ) urlHash[lines[i]] = true;
		}
		for ( var url in urlHash ) { allURLs.push(url); }
		if ( allURLs.length < 1 ) return;
		//Verbliebene Links trennen
		for ( var i = 0; i < allURLs.length; i++ )
		{
			lines = allURLs[i].split("  ");
			urlList[i] = lines.shift();
			if ( lines.length )
			{
				namList[i] = lines.join("  ");
			} else
			{
				namList[i] = "";
			}
		}
		if (document.getElementById("sbLinktitle").value == "ScrapBook")
		{
			window.openDialog(
				"chrome://scrapbook/content/capture.xul", "", "chrome,centerscreen,all,resizable,dialog=no",
				urlList, "", false, sbFolderSelector2.resURI, 0, null, null, null, null, "SB", charset, timeout
				);
		} else
		{
			window.openDialog(
				"chrome://scrapbook/content/capture.xul", "", "chrome,centerscreen,all,resizable,dialog=no",
				urlList, "", false, sbFolderSelector2.resURI, 0, null, null, null, null, "SB", charset, timeout, namList
				);
		}
	},

	addURL : function(auAllHash, auExclude)
	{
		var auAll = "";
		var auSelected = 0;
		var auCount = 0;
		var auFilter = this.FILTER.value;
		if ( auExclude == null )
		{
			auExclude = document.getElementById("sbpExcludeExistingAddresses").checked;
		}
		for ( var auURL in auAllHash )
		{
			auCount++;
			this.allURLs.push(auURL);
			this.allTitles.push(auAllHash[auURL]);
		}
		if ( auCount > 0 )
		{
			//Vergleichen mit Ausschlußliste und Co
			this.currentID = sbFolderSelector2.resURI;
			if ( this.currentID != this.lastID ) this.detectExistingLinks();
			for ( var auI=0; auI<this.allURLs.length; auI++ )
			{
				if ( this.allURLs[auI].match(auFilter) )
				{
					//Abgleich mit Ausschlussliste
					var auDoppelt = 0;
					if ( auExclude )
					{
						for ( var auJ = 0; auJ < this.vorhLinks.length; auJ++)
						{
							if ( this.vorhLinks[auJ] == this.allURLs[auI] )
							{
								auDoppelt = 1;
								auJ = this.vorhLinks.length;
							}
						}
					}
					if ( auDoppelt == 0 )
					{
						auSelected++;
						this.selURLs.push(this.allURLs[auI]);
						this.selTitles.push(this.allTitles[auI]);
						auAll += this.allURLs[auI]+"  "+this.allTitles[auI]+"\n";
					}
				}
			}
			document.getElementById("sbpCounter").setAttribute("value", auSelected+" \/ "+auCount);
		} else
		{
			document.getElementById("sbpCounter").setAttribute("value", "");
		}
		this.TEXTBOX.value = auAll;
	},

	clear : function()
	{
		this.TEXTBOX.value = "";
	},

	pasteClipboardURL : function()
	{
		var pcuAllHash = {};
		var pcuLines = [];
		this.allURLs = [];
		this.allTitles = [];
		try
		{
			var clip  = Components.classes['@mozilla.org/widget/clipboard;1'].createInstance(Components.interfaces.nsIClipboard);
			if ( !clip ) return false;
			var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
			if ( !trans ) return false;
			if ( 'init' in trans ) trans.init(null);
			trans.addDataFlavor("text/unicode");
			clip.getData(trans, clip.kGlobalClipboard);
			var str = new Object();
			var len = new Object();
			trans.getTransferData("text/unicode", str, len);
			if ( str )
			{
				str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
				pcuLines = str.toString().split("\n");
				for ( var i = 0; i < pcuLines.length; i++ )
				{
					if ( pcuLines[i].match(/^(http|https|ftp|file):\/\//) )
					{
						var pcuGetrennt = pcuLines[i].split("\;");
						if ( pcuGetrennt.length > 1 )
						{
							pcuAllHash[pcuGetrennt[0]] = pcuGetrennt[1];
						} else
						{
							pcuAllHash[pcuGetrennt[0]] = "";
						}
					}
				}
				this.addURL(pcuAllHash);
			}
		} catch(ex) {
		}
	},

	detectURLsOfTabs : function()
	{
		this.clear();
		var duotURL = "";
		var duotAllHash = {};
		var nodes = window.opener.gBrowser.mTabContainer.childNodes;
		this.allURLs = [];
		this.allTitles = [];
		for ( var i = 0; i < nodes.length; i++ )
		{
			duotURL = window.opener.gBrowser.getBrowserForTab(nodes[i]).contentDocument.location.href;
			if ( duotURL.match(/^(http|https|ftp|file):\/\//) )
			{
				duotAllHash[duotURL] = "";
			}
		}
		this.addURL(duotAllHash);
	},

	detectURLsInPage : function()
	{
		this.clear();
		var duipURL = "";
		var duipAllHash = {};
		var node = window.opener.top.content.document.body;
		this.allURLs = [];
		this.allTitles = [];
		traceTree : while ( true )
		{
			if ( node instanceof HTMLAnchorElement || node instanceof HTMLAreaElement )
			{
				duipURL = node.href;
				if ( duipURL.match(/^(http|https|ftp|file):\/\//) )
				{
					duipAllHash[duipURL] = node.text.replace(/^\s+/, '').replace(/\s+$/, '').replace(/\n/, ' ');
				}
			}
			if ( node.hasChildNodes() ) node = node.firstChild;
			else
			{
				while ( !node.nextSibling ) { node = node.parentNode; if ( !node ) break traceTree; }
				node = node.nextSibling;
			}
		}
		this.addURL(duipAllHash);
	},

	detectURLsInSelection : function()
	{
		this.clear();
		var duisURL = "";
		var duisAllHash = {};
		var sel = window.opener.top.sbPageEditor.getSelection();
		if ( !sel )
		{
			document.getElementById("sbpCounter").setAttribute("value", "");
			return;
		}
		var selRange  = sel.getRangeAt(0);
		var node = selRange.startContainer;
		if ( node.nodeName == "#text" ) node = node.parentNode;
		var nodeRange = window.opener.top.content.document.createRange();
		this.allURLs = [];
		traceTree : while ( true )
		{
			nodeRange.selectNode(node);
			if ( nodeRange.compareBoundaryPoints(Range.START_TO_END, selRange) > -1 )
			{
				if ( nodeRange.compareBoundaryPoints(Range.END_TO_START, selRange) > 0 ) break;
				else if ( node instanceof HTMLAnchorElement || node instanceof HTMLAreaElement )
				{
					duisURL = node.href;
					if ( duisURL.match(/^(http|https|ftp|file):\/\//) )
					{
						duisAllHash[duisURL] = node.text.replace(/^\s+/, '').replace(/\s+$/, '').replace(/\n/, ' ');
					}
				}
			}
			if ( node.hasChildNodes() ) node = node.firstChild;
			else
			{
				while ( !node.nextSibling ) { node = node.parentNode; if ( !node ) break traceTree; }
				node = node.nextSibling;
			}
		}
		this.addURL(duisAllHash);
	},

	detectExistingLinks : function()
	{
		//Funktion ermittelt die Links der vorhandenen Einträge im aktuell gewählten Zielverzeichnis
		var delResource = null;
		var delRDFCont = null;
		var delResEnum = [];
		this.vorhLinks = [];
		this.lastID = this.currentID;
		delResource = sbCommonUtils.RDF.GetResource(this.currentID);
		delRDFCont = Components.classes['@mozilla.org/rdf/container;1'].createInstance(Components.interfaces.nsIRDFContainer);
		delRDFCont.Init(sbDataSource.data, delResource);
		delResEnum = delRDFCont.GetElements();
		while ( delResEnum.hasMoreElements() )
		{
			var delRes = delResEnum.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
			if ( !sbDataSource.isContainer(delRes) )
			{
				this.vorhLinks.push(sbDataSource.getProperty(delRes, "source"));
			}
		}
	},

	updateSelection : function(usEvent)
	{
		//Funktion aktualisiert den Inhalt der aktuellen Auswahl
		var usCount = this.allURLs.length;
		var usAllHash = {};
		var usExclude = true;
		usExclude = document.getElementById("sbpExcludeExistingAddresses").checked;
		if ( usEvent )
		{
			if ( usEvent.button == 0 )
			{
				if ( usExclude )
				{
					usExclude = false;
				} else
				{
					usExclude = true;
				}
			}
		}
		for ( var i=0; i<usCount; i++ )
		{
			usAllHash[this.allURLs[i]] = this.allTitles[i];
		}
		this.allURLs = [];
		this.allTitles = [];
		this.addURL(usAllHash, usExclude);
	},

	toggleMethod : function()
	{
		//Funktion aktiviert bzw. deaktiviert die Zeichensatzauswahl
		var tmMethod = document.getElementById("sbMethod").value;
		if ( tmMethod == "SB" )
		{
			document.getElementById("sbCharset").disabled = false;
		} else
		{
			document.getElementById("sbCharset").disabled = true;
		}
	},

};

var sbURLDetector1 = {

	index : 0,

	run : function()
	{
		this.index = 0;
		var FP = Components.classes['@mozilla.org/filepicker;1'].createInstance(Components.interfaces.nsIFilePicker);
		FP.init(window, "", FP.modeGetFolder);
		var answer = FP.show();
		if ( answer == FP.returnOK )
		{
			sbMultipleService.clear();
			this.inspectDirectory(FP.file, 0);
		}
	},

	inspectDirectory : function(aDir, curIdx)
	{
		sbMultipleService.STATUS.value = sbCommonUtils.lang("scrapbook", "SCANNING_DIR", [curIdx, this.index, aDir.path]);
		var entries = aDir.directoryEntries;
		while ( entries.hasMoreElements() )
		{
			var entry = entries.getNext().QueryInterface(Components.interfaces.nsILocalFile);
			if ( entry.isDirectory() ) {
				this.inspectDirectoryWithDelay(entry, ++this.index);
			} else {
				if ( entry.leafName.match(/\.(html|htm)$/i) ) {
					var hash = {};
					hash[sbCommonUtils.convertFilePathToURL(entry.path)] = "";
					sbMultipleService.addURL(hash);
				}
			}
		}
		if ( curIdx == this.index ) sbMultipleService.STATUS.value = "";
	},

	inspectDirectoryWithDelay : function(aDir, aIndex)
	{
		setTimeout(function(){ sbURLDetector1.inspectDirectory(aDir, aIndex); }, 200 * aIndex);
	},

};

var sbURLDetector2 = {

	type   : "",
	index  : 0,
	lines  : [],
	result : "",
	weboxBaseURL : "",

	run : function(aType)
	{
		this.type = aType;
		this.index = 0;
		this.lines = [];
		this.result = "";
		this.weboxBaseURL = "";
		var theFile ;
		if ( this.type == "W" ) {
			var FP = Components.classes['@mozilla.org/filepicker;1'].createInstance(Components.interfaces.nsIFilePicker);
			FP.init(window, "Select default.html of WeBoX.", FP.modeOpen);
			FP.appendFilters(FP.filterHTML);
			var answer = FP.show();
			if ( answer == FP.returnOK ) theFile = FP.file;
			else return;
			this.weboxBaseURL = theFile.parent.path + '\\Data\\';
		} else {
			theFile = sbCommonUtils.DIR.get("ProfD", Components.interfaces.nsIFile);
			theFile.append("bookmarks.html");
			if ( !theFile.exists() ) return;
		}
		sbMultipleService.clear();
		this.lines = sbCommonUtils.readFile(theFile).split("\n");
		this.inspect();
	},

	inspect : function()
	{
		sbMultipleService.STATUS.value = sbCommonUtils.lang("scrapbook", "SCANNING", [this.index, (this.lines.length-1)]);
		this.result += "\n";
		if ( this.type == "W" ) {
			if ( this.lines[this.index].match(/ LOCALFILE\=\"([^\"]+)\" /) )
				this.result += sbCommonUtils.convertFilePathToURL(this.weboxBaseURL + RegExp.$1);
		} else {
			if ( this.lines[this.index].match(/ HREF\=\"([^\"]+)\" /) )
				this.result += RegExp.$1;
		}
		if ( ++this.index < this.lines.length ) {
			setTimeout(function(){ sbURLDetector2.inspect(); }, 0);
		} else {
			this.result = this.result.replace(/\n\n+/g, "\n\n");
			this.result = this.result.replace(/^\n+/, "");
			sbMultipleService.TEXTBOX.value = this.result;
			sbMultipleService.STATUS.value = "";
		}
	},

};<|endoftext|>


<commit_before>/**
 * Created by atan on 9/14/16.
 */

/* globals characterCounter: true */
/* globals getRequestAgencyInstructions: true */
/* globals getCustomRequestForms: true */
/* globals renderCustomRequestForm: true */
/* globals processCustomRequestForms: true */

"use strict";

$(document).ready(function () {
    $(window).load(function () {
        // Determine if the agencyRequestInstructions need to be shown on page load.
        getRequestAgencyInstructions();
        // Check for custom request forms on page load (browser back button behavior).
        getCustomRequestForms($("#request-agency").val());
    });

    $("input[name='tz-name']").val(jstz.determine().name());

    $("#request-category").change(function () {
        $.ajax({
            url: "/request/agencies",
            type: "GET",
            data: {
                category: $("#request-category").val()
            },
            success: function (data) {
                var sel = $("#request-agency");
                sel.empty();
                for (var i = 0; i < data.length; i++) {
                    var opt = document.createElement("option");
                    opt.innerHTML = data[i][1];
                    opt.value = data[i][0];
                    sel.append(opt);
                }
                // Determine if the agencyRequestInstructions need to be shown on page load.
                getRequestAgencyInstructions();
                $(".appended-div").remove(); // remove the appended divs from previous agency
                previousValues = [];
                currentValues = [];
                getCustomRequestForms(sel.find("option:first-child").val());
            }
        });
    });

    $("#request-agency").change(function () {
        getRequestAgencyInstructions();
        $(".appended-div").remove(); // remove the appended divs from previous agency
        previousValues = [];
        currentValues = [];
        getCustomRequestForms($("#request-agency").val());
    });

    $(document).on("focus", ".request-type", function () {
        var target = document.activeElement.id;
        target = target.replace("request-type-", "");
        var targetId = "#" + document.activeElement.id;
        $(targetId).off().change(function () {
            if (categorized && categorySelected) {
                var formId = $(targetId).val();
                var selectedCategory = formCategories[formId];
                // if the selected category is not the same as the current category warn the user with a modal
                if (selectedCategory !== currentCategory && formId !== "") {
                    // show the modal
                    $("#category-warning-modal").modal({
                        backdrop: "static",
                        keyboard: false
                    });
                    // handle modal button actions
                    $("#change-category-button").off().click(function () {
                        handleCategorySwitch(formId);
                    });
                    $("#cancel-change-category-button").off().click(function () {
                        $(targetId).val(previousValues[target - 1]);
                    });
                } else { // otherwise render the form normally
                    renderCustomRequestForm(target);
                }
            } else {
                renderCustomRequestForm(target);
                categorySelected = true;
            }
        });
    });

    // determine which dismiss button is being clicked
    $(document).on("click", ".panel-dismiss", function () {
        dismissTarget = "#" + document.activeElement.id;
    });

    // append a new dropdown and content div every time the additional content button is clicked
    $("#custom-request-form-additional-content").click(function () {
        customRequestFormCounter = customRequestFormCounter + 1;
        var dropdownTemplate = "

Request Type (optional)<button type='button' class='close panel-dismiss' id='panel-dismiss-button-" + customRequestFormCounter + "' data-target='#panel-dismiss-modal' data-toggle='modal'>×Close</button><select class='input-block-level request-type' id='request-type-" + customRequestFormCounter + "' name='request_type' aria-label='Request type, required, please select the option that best describes the records you are requesting'></select>

";
        var contentTemplate = "

";
        $(dropdownTemplate + contentTemplate).insertBefore("#custom-request-form-additional-content");
        $("#custom-request-form-additional-content").hide();

        // populate any empty dropdowns with that agency's form options
        populateDropdown($("#request-agency").val());

        previousValues[customRequestFormCounter - 1] = "";
        currentValues[customRequestFormCounter - 1] = "";

        $("#request-type-" + customRequestFormCounter).focus();
    });

    // Apply parsley validation styles to the input forms for a new request.
    $("#request-title").attr("data-parsley-required", "");
    $("#request-title").attr("data-parsley-maxlength", 90);
    $("#request-agency").attr("data-parsley-required", "");
    $("#request-description").attr("data-parsley-required", "");
    $("#request-description").attr("data-parsley-maxlength", 5000);

    // Custom Validation Messages
    $("#request-agency").attr("data-parsley-required-message",
        " " +
        "Error, an agency is required. Please select an agency from the drop-down menu.");
    $("#request-title").attr("data-parsley-required-message",
        " " +
        "Error, a title is required. Please type in a short title for your request.");
    $("#request-description").attr("data-parsley-required-message",
        " " +
        "Error, a description is required. Please type in a detailed description of your request.");

    // Limit the size of the file upload to 20 Mb. Second parameter is number of Mb's.
    $("#request-file").attr("data-parsley-max-file-size", "20");

    // Specify container for file input parsley error message
    $("#request-file").attr("data-parsley-errors-container", ".file-error");

    // Set name of the file to the text of filename div if file exists
    $("#request-file").change(function () {
        var file = this.files[0];

        if (file) {
            // return chosen filename to additional input
            var filename = this.files[0].name;
            $("#filename").val(filename);
            $("#filename").removeAttr("placeholder");
            $("#filename").focus();
            $("#choose-file").hide();
            $("#clear-file").show();
        }
    });

    // Clear the file from input and the name from filename div
    $("#clear-file").click(function () {
        if ($(".file-error").is(":visible")) {
            $(".file-error").hide();
        }
        $("#request-file").val("");
        $("#filename").val("");
        $("#filename").attr("placeholder", "No file uploaded");
        $("#clear-file").hide();
        $("#choose-file").show();
        $("#choose-file-button").focus();
    });

    // trigger file explorer on space and enter
    $("#choose-file span[role=button]").bind("keypress keyup", function (e) {
        if (e.which === 32 || e.which === 13) {
            e.preventDefault();
            $("#request-file").click();
        }
    });

    $("#request-form").parsley().on("form:validate", function () {
        // Do stuff when parsley validates
        // TODO: this or combine (see the other new-request-* js files)
        if ($("#request-file").parsley().isValid() === false) {
            $(".file-error").show();
        } else {
            $(".file-error").hide();
        }

        // Add tab index to any error messages
        $(".parsley-required").each(function () {
            // only add tab index to sections where error messages are currently visible
            if ($(this).text() !== "") {
                $(this).attr("tabindex", 0);
            }
        });
    });

    // Add tab index to any error messages after validation has failed
    $("#request-form").parsley().on("form:error", function () {
        $(".parsley-required").each(function () {
            // Only add tab index to sections where error messages are currently visible
            if ($(this).text() !== "") {
                $(this).attr("tabindex", 0);
            }
        });
    });

    // Clear error messages for form.request_file on submit .
    $("#submit").click(function () {
        $(".upload-error").remove();
    });
    // . or on input change for request_file
    $("#request-file").change(function () {
        $(".upload-error").remove();
    });

    var showSsnWarning = true; // variable to determine is the SSN warning modal should be shown
    // Disable submit button on form submission
    $("#request-form").submit(function (e) {
        $(".remove-on-resubmit").remove();
        if ($("#request-form").parsley().isValid()) {
            // section to check if at least one request type has been selected
            var emptyRequestDropdown = checkRequestTypeDropdowns();
            if (customRequestFormsEnabled === true && emptyRequestDropdown === true) {
                e.preventDefault();
                $("#processing-submission").hide();
                $("#submit").show();
                $(window).scrollTop($("#custom-request-forms-warning").offset().top - 50);
                return;
            }
            // section to check if all custom forms pass the minimum required validator
            var invalidForms = processCustomRequestFormData();
            if (invalidForms.length > 0) {
                e.preventDefault();
                $("#processing-submission").hide();
                $("#submit").show();
                $(window).scrollTop($(invalidForms[0]).offset().top);
                return;
            }

            e.preventDefault();
            $('#submit').show();
            $('#pii-warning-modal').modal('show');
            return;

            if (showPIIWarning) {
                e.preventDefault();
                $('#submit').show();
                $('#pii-warning-modal').modal('show');
                showPIIWarning = false;
                return;
            }
        }

        // Prevent multiple submissions
        $(this).submit(function () {
            return false;
        });
        $("#submit").hide();
        $("#processing-submission").show();
    });

    // Character count for creating a new request
    $("#request-title").keyup(function () {
        characterCounter("#title-character-count", 90, $(this).val().length);
    });

    $("#request-description").keyup(function () {
        characterCounter("#description-character-count", 5000, $(this).val().length);
    });

});
<commit_msg>Rename variable showSSNWarning<commit_after>/**
 * Created by atan on 9/14/16.
 */

/* globals characterCounter: true */
/* globals getRequestAgencyInstructions: true */
/* globals getCustomRequestForms: true */
/* globals renderCustomRequestForm: true */
/* globals processCustomRequestForms: true */

"use strict";

$(document).ready(function () {
    $(window).load(function () {
        // Determine if the agencyRequestInstructions need to be shown on page load.
        getRequestAgencyInstructions();
        // Check for custom request forms on page load (browser back button behavior).
        getCustomRequestForms($("#request-agency").val());
    });

    $("input[name='tz-name']").val(jstz.determine().name());

    $("#request-category").change(function () {
        $.ajax({
            url: "/request/agencies",
            type: "GET",
            data: {
                category: $("#request-category").val()
            },
            success: function (data) {
                var sel = $("#request-agency");
                sel.empty();
                for (var i = 0; i < data.length; i++) {
                    var opt = document.createElement("option");
                    opt.innerHTML = data[i][1];
                    opt.value = data[i][0];
                    sel.append(opt);
                }
                // Determine if the agencyRequestInstructions need to be shown on page load.
                getRequestAgencyInstructions();
                $(".appended-div").remove(); // remove the appended divs from previous agency
                previousValues = [];
                currentValues = [];
                getCustomRequestForms(sel.find("option:first-child").val());
            }
        });
    });

    $("#request-agency").change(function () {
        getRequestAgencyInstructions();
        $(".appended-div").remove(); // remove the appended divs from previous agency
        previousValues = [];
        currentValues = [];
        getCustomRequestForms($("#request-agency").val());
    });

    $(document).on("focus", ".request-type", function () {
        var target = document.activeElement.id;
        target = target.replace("request-type-", "");
        var targetId = "#" + document.activeElement.id;
        $(targetId).off().change(function () {
            if (categorized && categorySelected) {
                var formId = $(targetId).val();
                var selectedCategory = formCategories[formId];
                // if the selected category is not the same as the current category warn the user with a modal
                if (selectedCategory !== currentCategory && formId !== "") {
                    // show the modal
                    $("#category-warning-modal").modal({
                        backdrop: "static",
                        keyboard: false
                    });
                    // handle modal button actions
                    $("#change-category-button").off().click(function () {
                        handleCategorySwitch(formId);
                    });
                    $("#cancel-change-category-button").off().click(function () {
                        $(targetId).val(previousValues[target - 1]);
                    });
                } else { // otherwise render the form normally
                    renderCustomRequestForm(target);
                }
            } else {
                renderCustomRequestForm(target);
                categorySelected = true;
            }
        });
    });

    // determine which dismiss button is being clicked
    $(document).on("click", ".panel-dismiss", function () {
        dismissTarget = "#" + document.activeElement.id;
    });

    // append a new dropdown and content div every time the additional content button is clicked
    $("#custom-request-form-additional-content").click(function () {
        customRequestFormCounter = customRequestFormCounter + 1;
        var dropdownTemplate = "

Request Type (optional)<button type='button' class='close panel-dismiss' id='panel-dismiss-button-" + customRequestFormCounter + "' data-target='#panel-dismiss-modal' data-toggle='modal'>×Close</button><select class='input-block-level request-type' id='request-type-" + customRequestFormCounter + "' name='request_type' aria-label='Request type, required, please select the option that best describes the records you are requesting'></select>

";
        var contentTemplate = "

";
        $(dropdownTemplate + contentTemplate).insertBefore("#custom-request-form-additional-content");
        $("#custom-request-form-additional-content").hide();

        // populate any empty dropdowns with that agency's form options
        populateDropdown($("#request-agency").val());

        previousValues[customRequestFormCounter - 1] = "";
        currentValues[customRequestFormCounter - 1] = "";

        $("#request-type-" + customRequestFormCounter).focus();
    });

    // Apply parsley validation styles to the input forms for a new request.
    $("#request-title").attr("data-parsley-required", "");
    $("#request-title").attr("data-parsley-maxlength", 90);
    $("#request-agency").attr("data-parsley-required", "");
    $("#request-description").attr("data-parsley-required", "");
    $("#request-description").attr("data-parsley-maxlength", 5000);

    // Custom Validation Messages
    $("#request-agency").attr("data-parsley-required-message",
        " " +
        "Error, an agency is required. Please select an agency from the drop-down menu.");
    $("#request-title").attr("data-parsley-required-message",
        " " +
        "Error, a title is required. Please type in a short title for your request.");
    $("#request-description").attr("data-parsley-required-message",
        " " +
        "Error, a description is required. Please type in a detailed description of your request.");

    // Limit the size of the file upload to 20 Mb. Second parameter is number of Mb's.
    $("#request-file").attr("data-parsley-max-file-size", "20");

    // Specify container for file input parsley error message
    $("#request-file").attr("data-parsley-errors-container", ".file-error");

    // Set name of the file to the text of filename div if file exists
    $("#request-file").change(function () {
        var file = this.files[0];

        if (file) {
            // return chosen filename to additional input
            var filename = this.files[0].name;
            $("#filename").val(filename);
            $("#filename").removeAttr("placeholder");
            $("#filename").focus();
            $("#choose-file").hide();
            $("#clear-file").show();
        }
    });

    // Clear the file from input and the name from filename div
    $("#clear-file").click(function () {
        if ($(".file-error").is(":visible")) {
            $(".file-error").hide();
        }
        $("#request-file").val("");
        $("#filename").val("");
        $("#filename").attr("placeholder", "No file uploaded");
        $("#clear-file").hide();
        $("#choose-file").show();
        $("#choose-file-button").focus();
    });

    // trigger file explorer on space and enter
    $("#choose-file span[role=button]").bind("keypress keyup", function (e) {
        if (e.which === 32 || e.which === 13) {
            e.preventDefault();
            $("#request-file").click();
        }
    });

    $("#request-form").parsley().on("form:validate", function () {
        // Do stuff when parsley validates
        // TODO: this or combine (see the other new-request-* js files)
        if ($("#request-file").parsley().isValid() === false) {
            $(".file-error").show();
        } else {
            $(".file-error").hide();
        }

        // Add tab index to any error messages
        $(".parsley-required").each(function () {
            // only add tab index to sections where error messages are currently visible
            if ($(this).text() !== "") {
                $(this).attr("tabindex", 0);
            }
        });
    });

    // Add tab index to any error messages after validation has failed
    $("#request-form").parsley().on("form:error", function () {
        $(".parsley-required").each(function () {
            // Only add tab index to sections where error messages are currently visible
            if ($(this).text() !== "") {
                $(this).attr("tabindex", 0);
            }
        });
    });

    // Clear error messages for form.request_file on submit .
    $("#submit").click(function () {
        $(".upload-error").remove();
    });
    // . or on input change for request_file
    $("#request-file").change(function () {
        $(".upload-error").remove();
    });

    var showPIIWarning = true; // variable to determine is the SSN warning modal should be shown
    // Disable submit button on form submission
    $("#request-form").submit(function (e) {
        $(".remove-on-resubmit").remove();
        if ($("#request-form").parsley().isValid()) {
            // section to check if at least one request type has been selected
            var emptyRequestDropdown = checkRequestTypeDropdowns();
            if (customRequestFormsEnabled === true && emptyRequestDropdown === true) {
                e.preventDefault();
                $("#processing-submission").hide();
                $("#submit").show();
                $(window).scrollTop($("#custom-request-forms-warning").offset().top - 50);
                return;
            }
            // section to check if all custom forms pass the minimum required validator
            var invalidForms = processCustomRequestFormData();
            if (invalidForms.length > 0) {
                e.preventDefault();
                $("#processing-submission").hide();
                $("#submit").show();
                $(window).scrollTop($(invalidForms[0]).offset().top);
                return;
            }

            e.preventDefault();
            $('#submit').show();
            $('#pii-warning-modal').modal('show');
            return;

            if (showPIIWarning) {
                e.preventDefault();
                $('#submit').show();
                $('#pii-warning-modal').modal('show');
                showPIIWarning = false;
                return;
            }
        }

        // Prevent multiple submissions
        $(this).submit(function () {
            return false;
        });
        $("#submit").hide();
        $("#processing-submission").show();
    });

    // Character count for creating a new request
    $("#request-title").keyup(function () {
        characterCounter("#title-character-count", 90, $(this).val().length);
    });

    $("#request-description").keyup(function () {
        characterCounter("#description-character-count", 5000, $(this).val().length);
    });

});
<|endoftext|>


<commit_before>/***** BEGIN LICENSE BLOCK ***** {{{
Version: MPL 1.1/GPL 2.0/LGPL 2.1

The contents of this file are subject to the Mozilla Public License Version
1.1 (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.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.

(c) 2006-2007: Martin Stubenschrott <stubenschrott@gmx.net>

Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/

function Buffer() //{{{
{
    ////////////////////////////////////////////////////////////////////////////////
    ////////////////////// PRIVATE SECTION /////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    var zoom_manager = ZoomManager.prototype.getInstance();
    const ZOOM_INTERVAL = 25;

    // initialise the zoom levels
    zoom_manager.zoomFactors = [zoom_manager.MIN];
    for (var i = ZOOM_INTERVAL; i <= zoom_manager.MAX; i += ZOOM_INTERVAL)
        zoom_manager.zoomFactors.push(i);

    // NOTE: this is only needed as there's currently no way to specify a
    // {count} when calling ZM.reduce()/ZM.enlarge().  TODO: see if we can get
    // this added to ZoomManager.
    function bumpZoomLevel(steps)
    {
        var adjusted_zoom = zoom_manager.snap(zoom_manager.textZoom);
        var current = zoom_manager.indexOf(adjusted_zoom);
        var next = current + steps;

        var start = 0, end = zoom_manager.zoomFactors.length - 1;

        if ((current == start && steps < 0) || (current == end && steps > 0))
        {
            vimperator.beep();
            return;
        }

        if (next < start)
            next = start;
        else if (next > end)
            next = end;

        zoom_manager.textZoom = zoom_manager.zoomFactors[next];
        vimperator.hints.reshowHints();
    }

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// PUBLIC SECTION //////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    this.__defineGetter__("location", function()
    {
        return window.content.document.location.href;
    });

    this.__defineGetter__("pageHeight", function()
    {
        return getBrowser().mPanelContainer.boxObject.height; // FIXME: best way to do this?
    });

    this.__defineGetter__("textZoom", function()
    {
        return zoom_manager.textZoom;
    });

    this.__defineSetter__("textZoom", function(value)
    {
        try
        {
            zoom_manager.textZoom = value;
        }
        catch (e) // Components.results.NS_ERROR_INVALID_ARG
        {
            vimperator.echoerr("Zoom value out of range (" + zoom_manager.MIN + "-" + zoom_manager.MAX + ")");
        }

        // TODO: shouldn't this just recalculate hint coords, rather than
        // unsuccessfully attempt to reshow hints?  i.e. isn't it just relying
        // on the recalculation side effect? -- djk
        // NOTE: we could really do with a zoom event...
        vimperator.hints.reshowHints();
    });

    this.__defineGetter__("title", function()
    {
        return window.content.document.title;
    });

    // both values are given in percent, -1 means no change
    this.scrollAbsolute = function(horizontal, vertical)
    {
        var win = document.commandDispatcher.focusedWindow;
        //var win = window.content;
        var horiz, vert;

        if (horizontal < 0)
            horiz = win.scrollX;
        else
            horiz = win.scrollMaxX / 100 * horizontal;

        if (vertical < 0)
            vert = win.scrollY;
        else
            vert = win.scrollMaxY / 100 * vertical;

        win.scrollTo(horiz, vert);
    }

    this.scrollRelative = function(right, down)
    {
        var win = window.document.commandDispatcher.focusedWindow;
        //var win = window.content; // XXX: This would fix scrolling when the tab has focus, but breaks when it has frames --MST

        // beep if we can't go there
        if (down > 0)
        {
            if (win.scrollY == win.scrollMaxY)
                vimperator.beep();
        }
        else if (down < 0)
        {
            if (win.scrollY == 0)
                vimperator.beep();
        }

        if (right > 0)
        {
            if (win.scrollX == win.scrollMaxX)
                vimperator.beep();
        }
        else if (right < 0)
        {
            if (win.scrollX == 0)
                vimperator.beep();
        }

        win.scrollBy(right * 20, down * 20);
    }

    // TODO: allow callback for filtering out unwanted frames? User defined?
    this.shiftFrameFocus = function(count, forward)
    {
        try
        {
            var frames = [];

            // find all frames - depth-first search
            (function(frame)
            {
                if (frame.document.body.localName.toLowerCase() == "body")
                    frames.push(frame);
                for (var i = 0; i < frame.frames.length; i++)
                    arguments.callee(frame.frames[i])
            })(window.content);

            if (frames.length == 0) // currently top is always included
                return;

            // remove all unfocusable frames
            // TODO: find a better way to do this
            var start = document.commandDispatcher.focusedWindow;
            frames = frames.filter(function(frame) {
                    frame.focus();
                    if (document.commandDispatcher.focusedWindow == frame)
                        return frame;
            });
            start.focus();

            // find the currently focused frame index
            // TODO: If the window is a frameset then the first _frame_ should be
            //       focused.  Since this is not the current FF behaviour,
            //       we initalise current to -1 so the first call takes us to the
            //       first frame.
            var current = -1;
            for (var i = 0; i < frames.length; i++)
            {
                if (frames[i] == document.commandDispatcher.focusedWindow)
                {
                    var current = i;
                    break;
                }
            }

            // calculate the next frame to focus
            var next = current;
            if (forward)
            {
                if (count > 1)
                    next = current + count;
                else
                    next++;

                if (next > frames.length - 1)
                    next = frames.length - 1;
            }
            else
            {
                if (count > 1)
                    next = current - count;
                else
                    next--;

                if (next < 0)
                    next = 0;
            }

            // focus next frame and scroll into view
            frames[next].focus();
            if (frames[next] != window.content)
                frames[next].frameElement.scrollIntoView(false);

            // add the frame indicator
            var doc = frames[next].document;
            var indicator = doc.createElement("div");
            indicator.id = "vimperator-frame-indicator";
            // NOTE: need to set a high z-index - it's a crapshoot!
            var style = "background-color: red; opacity: 0.5; z-index: 999;" +
                        "position: fixed; top: 0; bottom: 0; left: 0; right: 0;";
            indicator.setAttribute("style", style);
            doc.body.appendChild(indicator);

            // remove the frame indicator
            setTimeout(function() { doc.body.removeChild(indicator); }, 500);
        }
        catch (e)
        {
            //vimperator.echoerr(e);
            // FIXME: fail silently here for now
        }
    }

    // updates the buffer preview in place only if list is visible
    this.updateBufferList = function()
    {
        if (!vimperator.bufferwindow.visible())
            return false;

        var items = vimperator.completion.get_buffer_completions("");
        vimperator.bufferwindow.show(items);
        vimperator.bufferwindow.selectItem(getBrowser().mTabContainer.selectedIndex);
    }

    this.zoomIn = function(steps)
    {
        bumpZoomLevel(steps);
    }

    this.zoomOut = function(steps)
    {
        bumpZoomLevel(-steps);
    }
    //}}}
} //}}}

// vim: set fdm=marker sw=4 ts=4 et:
<commit_msg>beep when frame motion bounds are exceeded<commit_after>/***** BEGIN LICENSE BLOCK ***** {{{
Version: MPL 1.1/GPL 2.0/LGPL 2.1

The contents of this file are subject to the Mozilla Public License Version
1.1 (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.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.

(c) 2006-2007: Martin Stubenschrott <stubenschrott@gmx.net>

Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/

function Buffer() //{{{
{
    ////////////////////////////////////////////////////////////////////////////////
    ////////////////////// PRIVATE SECTION /////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    var zoom_manager = ZoomManager.prototype.getInstance();
    const ZOOM_INTERVAL = 25;

    // initialise the zoom levels
    zoom_manager.zoomFactors = [zoom_manager.MIN];
    for (var i = ZOOM_INTERVAL; i <= zoom_manager.MAX; i += ZOOM_INTERVAL)
        zoom_manager.zoomFactors.push(i);

    // NOTE: this is only needed as there's currently no way to specify a
    // {count} when calling ZM.reduce()/ZM.enlarge().  TODO: see if we can get
    // this added to ZoomManager.
    function bumpZoomLevel(steps)
    {
        var adjusted_zoom = zoom_manager.snap(zoom_manager.textZoom);
        var current = zoom_manager.indexOf(adjusted_zoom);
        var next = current + steps;

        var start = 0, end = zoom_manager.zoomFactors.length - 1;

        if ((current == start && steps < 0) || (current == end && steps > 0))
        {
            vimperator.beep();
            return;
        }

        if (next < start)
            next = start;
        else if (next > end)
            next = end;

        zoom_manager.textZoom = zoom_manager.zoomFactors[next];
        vimperator.hints.reshowHints();
    }

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// PUBLIC SECTION //////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    this.__defineGetter__("location", function()
    {
        return window.content.document.location.href;
    });

    this.__defineGetter__("pageHeight", function()
    {
        return getBrowser().mPanelContainer.boxObject.height; // FIXME: best way to do this?
    });

    this.__defineGetter__("textZoom", function()
    {
        return zoom_manager.textZoom;
    });

    this.__defineSetter__("textZoom", function(value)
    {
        try
        {
            zoom_manager.textZoom = value;
        }
        catch (e) // Components.results.NS_ERROR_INVALID_ARG
        {
            vimperator.echoerr("Zoom value out of range (" + zoom_manager.MIN + "-" + zoom_manager.MAX + ")");
        }

        // TODO: shouldn't this just recalculate hint coords, rather than
        // unsuccessfully attempt to reshow hints?  i.e. isn't it just relying
        // on the recalculation side effect? -- djk
        // NOTE: we could really do with a zoom event...
        vimperator.hints.reshowHints();
    });

    this.__defineGetter__("title", function()
    {
        return window.content.document.title;
    });

    // both values are given in percent, -1 means no change
    this.scrollAbsolute = function(horizontal, vertical)
    {
        var win = document.commandDispatcher.focusedWindow;
        //var win = window.content;
        var horiz, vert;

        if (horizontal < 0)
            horiz = win.scrollX;
        else
            horiz = win.scrollMaxX / 100 * horizontal;

        if (vertical < 0)
            vert = win.scrollY;
        else
            vert = win.scrollMaxY / 100 * vertical;

        win.scrollTo(horiz, vert);
    }

    this.scrollRelative = function(right, down)
    {
        var win = window.document.commandDispatcher.focusedWindow;
        //var win = window.content; // XXX: This would fix scrolling when the tab has focus, but breaks when it has frames --MST

        // beep if we can't go there
        if (down > 0)
        {
            if (win.scrollY == win.scrollMaxY)
                vimperator.beep();
        }
        else if (down < 0)
        {
            if (win.scrollY == 0)
                vimperator.beep();
        }

        if (right > 0)
        {
            if (win.scrollX == win.scrollMaxX)
                vimperator.beep();
        }
        else if (right < 0)
        {
            if (win.scrollX == 0)
                vimperator.beep();
        }

        win.scrollBy(right * 20, down * 20);
    }

    // TODO: allow callback for filtering out unwanted frames? User defined?
    this.shiftFrameFocus = function(count, forward)
    {
        try
        {
            var frames = [];

            // find all frames - depth-first search
            (function(frame)
            {
                if (frame.document.body.localName.toLowerCase() == "body")
                    frames.push(frame);
                for (var i = 0; i < frame.frames.length; i++)
                    arguments.callee(frame.frames[i])
            })(window.content);

            if (frames.length == 0) // currently top is always included
                return;

            // remove all unfocusable frames
            // TODO: find a better way to do this
            var start = document.commandDispatcher.focusedWindow;
            frames = frames.filter(function(frame) {
                    frame.focus();
                    if (document.commandDispatcher.focusedWindow == frame)
                        return frame;
            });
            start.focus();

            // find the currently focused frame index
            // TODO: If the window is a frameset then the first _frame_ should be
            //       focused.  Since this is not the current FF behaviour,
            //       we initalise current to -1 so the first call takes us to the
            //       first frame.
            var current = -1;
            for (var i = 0; i < frames.length; i++)
            {
                if (frames[i] == document.commandDispatcher.focusedWindow)
                {
                    var current = i;
                    break;
                }
            }

            // calculate the next frame to focus
            var next = current;
            if (forward)
            {
                if (count > 1)
                    next = current + count;
                else
                    next++;

                if (next > frames.length - 1)
                {
                    if (current == frames.length - 1)
                        vimperator.beep(); // still allow the frame indicator to be activated

                    next = frames.length - 1;
                }
            }
            else
            {
                if (count > 1)
                    next = current - count;
                else
                    next--;

                if (next < 0)
                {
                    if (current == 0)
                        vimperator.beep(); // still allow the frame indicator to be activated

                    next = 0;
                }
            }

            // focus next frame and scroll into view
            frames[next].focus();
            if (frames[next] != window.content)
                frames[next].frameElement.scrollIntoView(false);

            // add the frame indicator
            var doc = frames[next].document;
            var indicator = doc.createElement("div");
            indicator.id = "vimperator-frame-indicator";
            // NOTE: need to set a high z-index - it's a crapshoot!
            var style = "background-color: red; opacity: 0.5; z-index: 999;" +
                        "position: fixed; top: 0; bottom: 0; left: 0; right: 0;";
            indicator.setAttribute("style", style);
            doc.body.appendChild(indicator);

            // remove the frame indicator
            setTimeout(function() { doc.body.removeChild(indicator); }, 500);
        }
        catch (e)
        {
            //vimperator.echoerr(e);
            // FIXME: fail silently here for now
        }
    }

    // updates the buffer preview in place only if list is visible
    this.updateBufferList = function()
    {
        if (!vimperator.bufferwindow.visible())
            return false;

        var items = vimperator.completion.get_buffer_completions("");
        vimperator.bufferwindow.show(items);
        vimperator.bufferwindow.selectItem(getBrowser().mTabContainer.selectedIndex);
    }

    this.zoomIn = function(steps)
    {
        bumpZoomLevel(steps);
    }

    this.zoomOut = function(steps)
    {
        bumpZoomLevel(-steps);
    }
    //}}}
} //}}}

// vim: set fdm=marker sw=4 ts=4 et:
<|endoftext|>


<commit_before>import _ from 'lodash';
import React from 'react/addons';
import moment from 'moment';
import ItemCard from '../item-card';
import Sprint from './sprint';
import ColumnSummary from './summary';
import ColumnHeader from './header';
import Loading from 'react-loading';
import ProductStore from '../../../stores/product-store';
import ProductActions from '../../../actions/product-actions';
import FilterActions from '../../../actions/filter-actions';
import ScoreMap from '../../../lib/score-map';

const EMPTY_CHUNK = {
  points: 0,
  items: []
};

function getColumnState(items=[]) {
  return {
    items,
    isLoading: false,
    hideLoadMore: false,
    sortField: 'last_modified',
    sortDirection: 'desc',
    offset: 0,
    limit: 0
  }
}

var ItemColumn = React.createClass({

  propTypes: {
    status: React.PropTypes.string.isRequired,
    product: React.PropTypes.object.isRequired
  },

  getInitialState() {
    return getColumnState();
  },

  _onChange() {
    let state = ProductStore.getItems(this.props.product.id, this.props.status);
    if (!state) {
      return;
    }

    state.isLoading = false;
    this.setState(state);
  },

  setSortCriteria(field=this.state.sortField, direction=this.state.sortDirection) {
    let items = ProductStore.getItemsCollection(this.props.product.id, this.props.status);
    if (!items) {
      return;
    }

    this.setState({ isLoading: true });
    ProductActions.changeSortCriteria(items, field, direction);
  },

  getItems(product, options={ hideLoader: false }) {
    this.setState({ isLoading: !options.hideLoader });
    ProductActions.getItemsForProduct(product, {
      filters: options.filters || this.props.filters,
      status: this.props.status,
      sortField: this.state.sortField,
      sortDirection: this.state.sortDirection
    })
  },

  loadMoreItems() {
    let items = ProductStore.getItemsCollection(this.props.product.id, this.props.status);
    if (!items) {
      return;
    }
    ProductActions.loadMoreItems(items);
  },

  componentDidMount() {
    ProductStore.addChangeListener(this._onChange);
    this.getItems(this.props.product);
  },

  componentWillUnmount() {
    ProductStore.removeChangeListener(this._onChange);
  },

  componentWillReceiveProps(nextProps) {
    var reload = false;

    if (nextProps.product.id !== this.props.product.id) {
      reload = true;
    }

    if (_.isEqual(nextProps.filters, this.props.filters) === false) {
      reload = true;
    }

    if (reload) {
      this.setState({ isLoading: true });
      this.getItems(nextProps.product, {
        filters: nextProps.filters
      });
    }
  },

  /**
   * Chunks the items passed into the props into sprints based on the current predicted velocity.
   * Each sprint chunk is an object with the following structure:
   * {
   *   points: {Number}, // the number of points in the sprint.
   *   items: {Array} // an array of objects containing item data
   * }
   *
   * @returns {Array} // an array of raw sprint chunks
   */
  chunkItems() {
    let chunks = [];
    let currentChunk = _.cloneDeep(EMPTY_CHUNK);
    _.each(this.state.items, (item, i) => {
      let itemScore = ScoreMap[item.score];
      currentChunk.points += itemScore;
      currentChunk.items.push(item);

      // Check whether adding the next item's score will push the current sprint chunks's point
      // count above the predicted velocity. If so, add it to the chunks collection and start a
      // new sprint chunk. In the case that the current sprint chunk is under the predicted velocity
      // by *more* than adding the next item would cause it to go over, allow the sprint's total to
      // go over the predicted velocity instead. This prevents things like a 3 or 5 point sprint
      // when followed by an 8 point sprint.
      let nextItem = this.state.items[i + 1] || {score: '~'};
      let nextItemScore = ScoreMap[nextItem.score];
      let scoreWithNext = currentChunk.points + nextItemScore;
      let nextScoreIsOverAverage = currentChunk.points + nextItemScore >= this.props.velocity.average;
      let underageIsGreaterThanOverage = this.props.velocity.average - currentChunk.points >
      scoreWithNext - this.props.velocity.average;

      let isLastItem = this.state.items.length === i + 1;

      if ((nextScoreIsOverAverage && !underageIsGreaterThanOverage) || isLastItem) {
        // Add the current chunk to the collection and start a new one
        chunks.push(currentChunk);
        currentChunk = _.cloneDeep(EMPTY_CHUNK);
      }
    });
    return chunks;
  },

  calculateSummary() {
    let points = _.reduce(this.state.items, function(total, item) {
      total += ScoreMap[item.score];
      return total;
    }, 0);
    return {
      points,
      startDate: moment().startOf('isoweek').format('D MMM')
    }
  },

  renderLoadMore() {
    var loadMore = <button className="load-more" onClick={this.loadMoreItems}>Load More</button>;

    if (this.state.isLoading || this.state.hideLoadMore || this.state.items.length < this.state.limit) {
      return '';
    }

    return loadMore;
  },

  renderItemCard(item, index) {
    let card = (
      <ItemCard
        item={item}
        members={this.props.members}
        sortField={this.state.sortField}
        productId={this.props.product.id}
        key={`item-${this.props.product.id}${item.number}`}
      />
    );
    return card;
  },

  renderItemCards() {
    let showSummary = this.props.status === 'in-progress' && this.state.sortField === 'priority';
    let itemCards = _.map(this.state.items, this.renderItemCard);
    if (showSummary) {
      let props = this.calculateSummary();
      return (

          <ColumnSummary {...props} />
          {itemCards}

      );
    } else {
      return (
{itemCards}
);
    }
  },

  renderSprints() {
    let rawSprints = this.chunkItems();
    return _.map(rawSprints, (sprint, i) => {
      // Start the groups in the backlog with the next week
      let startDate = moment().startOf('isoweek').add(7 * (i + 1), 'days').format('D MMM');
      return (
        <Sprint
          key={`item-group-${i}`}
          items={sprint.items}
          sortField={this.state.sortField}
          productId={this.props.product.id}
          startDate={startDate}
          startOpen={i === 0}
          points={sprint.points}
        />
      );
    });
  },

  render() {

    let classes = {
      column: true,
      [this.props.status]: true
    };

    let reverseSort = (ev) => {
      let direction = this.state.sortDirection === 'desc' ? 'asc' : 'desc';
      this.setSortCriteria(this.state.sortField, direction);
    };

    let showSprints = this.props.status === 'backlog' && this.state.sortField === 'priority';
    let renderCardsOrSprints = showSprints ? this.renderSprints : this.renderItemCards;

    return (

        <ColumnHeader {...this.props}
          reverse={reverseSort}
          setSortCriteria={this.setSortCriteria}
          sortDirection={this.state.sortDirection}
          sortField={this.state.sortField}
        />
        {this.state.isLoading ?

<Loading type="bubbles" color="#ccc"/>
 :
          renderCardsOrSprints()
        }
        {this.renderLoadMore()}

    );
  }
});

module.exports = ItemColumn

<commit_msg>WIP persist column sort order<commit_after>import _ from 'lodash';
import React from 'react/addons';
import moment from 'moment';
import ItemCard from '../item-card';
import Sprint from './sprint';
import ColumnSummary from './summary';
import ColumnHeader from './header';
import Loading from 'react-loading';
import ProductStore from '../../../stores/product-store';
import ProductActions from '../../../actions/product-actions';
import FilterActions from '../../../actions/filter-actions';
import ScoreMap from '../../../lib/score-map';
import LocalStorageMixin from 'react-localstorage';

const EMPTY_CHUNK = {
  points: 0,
  items: []
};

function getColumnState(items=[]) {
  return {
    items,
    isLoading: false,
    hideLoadMore: false,
    sortField: 'last_modified',
    sortDirection: 'desc',
    offset: 0,
    limit: 0
  }
}

var ItemColumn = React.createClass({
  mixins: [
    LocalStorageMixin
  ],

  localStorageKey: 'itemColumn',

  propTypes: {
    status: React.PropTypes.string.isRequired,
    product: React.PropTypes.object.isRequired
  },

  getInitialState() {
    return getColumnState();
  },

  _onChange() {
    let state = ProductStore.getItems(this.props.product.id, this.props.status);
    if (!state) {
      return;
    }

    state.isLoading = false;
    this.setState(state);
  },

  setSortCriteria(field=this.state.sortField, direction=this.state.sortDirection) {
    let items = ProductStore.getItemsCollection(this.props.product.id, this.props.status);
    if (!items) {
      return;
    }

    this.setState({ isLoading: true });
    ProductActions.changeSortCriteria(items, field, direction);
  },

  getItems(product, options={ hideLoader: false }) {
    this.setState({ isLoading: !options.hideLoader });
    ProductActions.getItemsForProduct(product, {
      filters: options.filters || this.props.filters,
      status: this.props.status,
      sortField: this.state.sortField,
      sortDirection: this.state.sortDirection
    })
  },

  loadMoreItems() {
    let items = ProductStore.getItemsCollection(this.props.product.id, this.props.status);
    if (!items) {
      return;
    }
    ProductActions.loadMoreItems(items);
  },

  componentDidMount() {
    ProductStore.addChangeListener(this._onChange);
    this.getItems(this.props.product);
  },

  componentWillUnmount() {
    ProductStore.removeChangeListener(this._onChange);
  },

  componentWillReceiveProps(nextProps) {
    var reload = false;

    if (nextProps.product.id !== this.props.product.id) {
      reload = true;
    }

    if (_.isEqual(nextProps.filters, this.props.filters) === false) {
      reload = true;
    }

    if (reload) {
      this.setState({ isLoading: true });
      this.getItems(nextProps.product, {
        filters: nextProps.filters
      });
    }
  },

  /**
   * Chunks the items passed into the props into sprints based on the current predicted velocity.
   * Each sprint chunk is an object with the following structure:
   * {
   *   points: {Number}, // the number of points in the sprint.
   *   items: {Array} // an array of objects containing item data
   * }
   *
   * @returns {Array} // an array of raw sprint chunks
   */
  chunkItems() {
    let chunks = [];
    let currentChunk = _.cloneDeep(EMPTY_CHUNK);
    _.each(this.state.items, (item, i) => {
      let itemScore = ScoreMap[item.score];
      currentChunk.points += itemScore;
      currentChunk.items.push(item);

      // Check whether adding the next item's score will push the current sprint chunks's point
      // count above the predicted velocity. If so, add it to the chunks collection and start a
      // new sprint chunk. In the case that the current sprint chunk is under the predicted velocity
      // by *more* than adding the next item would cause it to go over, allow the sprint's total to
      // go over the predicted velocity instead. This prevents things like a 3 or 5 point sprint
      // when followed by an 8 point sprint.
      let nextItem = this.state.items[i + 1] || {score: '~'};
      let nextItemScore = ScoreMap[nextItem.score];
      let scoreWithNext = currentChunk.points + nextItemScore;
      let nextScoreIsOverAverage = currentChunk.points + nextItemScore >= this.props.velocity.average;
      let underageIsGreaterThanOverage = this.props.velocity.average - currentChunk.points >
      scoreWithNext - this.props.velocity.average;

      let isLastItem = this.state.items.length === i + 1;

      if ((nextScoreIsOverAverage && !underageIsGreaterThanOverage) || isLastItem) {
        // Add the current chunk to the collection and start a new one
        chunks.push(currentChunk);
        currentChunk = _.cloneDeep(EMPTY_CHUNK);
      }
    });
    return chunks;
  },

  calculateSummary() {
    let points = _.reduce(this.state.items, function(total, item) {
      total += ScoreMap[item.score];
      return total;
    }, 0);
    return {
      points,
      startDate: moment().startOf('isoweek').format('D MMM')
    }
  },

  renderLoadMore() {
    var loadMore = <button className="load-more" onClick={this.loadMoreItems}>Load More</button>;

    if (this.state.isLoading || this.state.hideLoadMore || this.state.items.length < this.state.limit) {
      return '';
    }

    return loadMore;
  },

  renderItemCard(item, index) {
    let card = (
      <ItemCard
        item={item}
        members={this.props.members}
        sortField={this.state.sortField}
        productId={this.props.product.id}
        key={`item-${this.props.product.id}${item.number}`}
      />
    );
    return card;
  },

  renderItemCards() {
    let showSummary = this.props.status === 'in-progress' && this.state.sortField === 'priority';
    let itemCards = _.map(this.state.items, this.renderItemCard);
    if (showSummary) {
      let props = this.calculateSummary();
      return (

          <ColumnSummary {...props} />
          {itemCards}

      );
    } else {
      return (
{itemCards}
);
    }
  },

  renderSprints() {
    let rawSprints = this.chunkItems();
    return _.map(rawSprints, (sprint, i) => {
      // Start the groups in the backlog with the next week
      let startDate = moment().startOf('isoweek').add(7 * (i + 1), 'days').format('D MMM');
      return (
        <Sprint
          key={`item-group-${i}`}
          items={sprint.items}
          sortField={this.state.sortField}
          productId={this.props.product.id}
          startDate={startDate}
          startOpen={i === 0}
          points={sprint.points}
        />
      );
    });
  },

  render() {

    let classes = {
      column: true,
      [this.props.status]: true
    };

    let reverseSort = (ev) => {
      let direction = this.state.sortDirection === 'desc' ? 'asc' : 'desc';
      this.setSortCriteria(this.state.sortField, direction);
    };

    let showSprints = this.props.status === 'backlog' && this.state.sortField === 'priority';
    let renderCardsOrSprints = showSprints ? this.renderSprints : this.renderItemCards;

    return (

        <ColumnHeader {...this.props}
          reverse={reverseSort}
          setSortCriteria={this.setSortCriteria}
          sortDirection={this.state.sortDirection}
          sortField={this.state.sortField}
        />
        {this.state.isLoading ?

<Loading type="bubbles" color="#ccc"/>
 :
          renderCardsOrSprints()
        }
        {this.renderLoadMore()}

    );
  }
});

module.exports = ItemColumn

<|endoftext|>


<commit_before>/*
-----------------------------------------------------------------------------
Copyright (c) 2014-2015 Seth Anderson

This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the
use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be appreciated
but is not required.

2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.

3. This notice may not be removed or altered from any source distribution.
-----------------------------------------------------------------------------
*/

require("../common/userservice.js");
require("../common/arrayentrydirectives.js");
require("../common/enumselectdirective.js");
require("../common/validationdirectives.js");

(function(){
	var UserDetailController = function($scope, $state, ngToast, UserService){
		var user = this;
		user.busy = false;
		user.current = {
			lanInviteDesired: true,
			blacklisted: false,
			vip: false,
			tertiaryHandles: [],
			roles: []
		};
		user.boolPossibles = [
			{label: "Yes", value: true},
			{label: "No", value: false}
		];
		user.help = {
			lanInviteDesired: "Determines whether the user will receive the LAN invitation. This value can be modified by the user.",
			blacklisted: "Determines whether the user will receive the LAN invitation. This value cannot be seen or modified by the user.",
			vip: "Determines whether the user is considered a LAN VIP (and is therefore privy to VIP date consultation)."
		};

		if($state.params.userId && $state.params.userId !== "new"){
			UserService.retrieve($state.params.userId)
			.then(function(data){
				user.current = data;
			}).catch(function(){
				$state.go("^");
				ngToast.danger("Failed to retrieve user.");
			});
		}

		user.save = function(){
			user.busy = true;
			if($state.params.userId === "new"){
				UserService.create(user.current)
				.then(function(data){
					$scope.reloadList();
					$state.go(".", {userId: data._id});
					ngToast.create("User created.");
					user.busy = false;
				}).catch(function(){
					ngToast.danger("Failed to create user.");
					user.busy = false;
				});
			}else{
				UserService.edit($state.params.userId, user.current)
				.then(function(){
					$scope.reloadList();
					ngToast.create("User updated.");
					user.busy = false;
				}).catch(function(){
					ngToast.danger("Failed to update user.");
					user.busy = false;
				});
			}
		};

		user.resendVerificationEmail = function(){
			user.busy = true;
			UserService.resendVerificationEmail()
			.then(function(){
				ngToast.create("Verification email sent.");
				user.busy = false;
			}).catch(function(){
				ngToast.danger("Failed to send verification email.");
				user.busy = false;
			});
		};
	};

	angular
		.module("3akm.admin.userDetail",
			[
				"3akm.user",
				"3akm.common.validation",
				"3akm.common.arrayentry",
				"3akm.common.enumselect"
			])
		.controller("UserDetailController", UserDetailController);

	UserDetailController.$inject = ["$scope", "$state", "ngToast", "UserService"];
})();
<commit_msg>Fixed resend button in admin portal user detail not submitting properly<commit_after>/*
-----------------------------------------------------------------------------
Copyright (c) 2014-2015 Seth Anderson

This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the
use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be appreciated
but is not required.

2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.

3. This notice may not be removed or altered from any source distribution.
-----------------------------------------------------------------------------
*/

require("../common/userservice.js");
require("../common/arrayentrydirectives.js");
require("../common/enumselectdirective.js");
require("../common/validationdirectives.js");

(function(){
	var UserDetailController = function($scope, $state, ngToast, UserService){
		var user = this;
		user.busy = false;
		user.current = {
			lanInviteDesired: true,
			blacklisted: false,
			vip: false,
			tertiaryHandles: [],
			roles: []
		};
		user.boolPossibles = [
			{label: "Yes", value: true},
			{label: "No", value: false}
		];
		user.help = {
			lanInviteDesired: "Determines whether the user will receive the LAN invitation. This value can be modified by the user.",
			blacklisted: "Determines whether the user will receive the LAN invitation. This value cannot be seen or modified by the user.",
			vip: "Determines whether the user is considered a LAN VIP (and is therefore privy to VIP date consultation)."
		};

		if($state.params.userId && $state.params.userId !== "new"){
			UserService.retrieve($state.params.userId)
			.then(function(data){
				user.current = data;
			}).catch(function(){
				$state.go("^");
				ngToast.danger("Failed to retrieve user.");
			});
		}

		user.save = function(){
			user.busy = true;
			if($state.params.userId === "new"){
				UserService.create(user.current)
				.then(function(data){
					$scope.reloadList();
					$state.go(".", {userId: data._id});
					ngToast.create("User created.");
					user.busy = false;
				}).catch(function(){
					ngToast.danger("Failed to create user.");
					user.busy = false;
				});
			}else{
				UserService.edit($state.params.userId, user.current)
				.then(function(){
					$scope.reloadList();
					ngToast.create("User updated.");
					user.busy = false;
				}).catch(function(){
					ngToast.danger("Failed to update user.");
					user.busy = false;
				});
			}
		};

		user.resendVerificationEmail = function(){
			user.busy = true;
			UserService.resendVerificationEmail($state.params.userId)
			.then(function(){
				ngToast.create("Verification email sent.");
				user.busy = false;
			}).catch(function(){
				ngToast.danger("Failed to send verification email.");
				user.busy = false;
			});
		};
	};

	angular
		.module("3akm.admin.userDetail",
			[
				"3akm.user",
				"3akm.common.validation",
				"3akm.common.arrayentry",
				"3akm.common.enumselect"
			])
		.controller("UserDetailController", UserDetailController);

	UserDetailController.$inject = ["$scope", "$state", "ngToast", "UserService"];
})();
<|endoftext|>