<commit_before>import { is } from 'redux-saga/utils'
import getEffectName from './get-effect-name'
import getEffectDescription from './get-effect-description'
import {
  ITERATOR,
  CALL,
  PUT,
  FORK,
  RACE,
  PENDING,
  RESOLVED,
  REJECTED,
  CANCELLED
} from './saga-constants'
import {
  reject,
  values,
  pluck,
  isNil,
  split,
  pathOr,
  last,
  forEach,
  propEq,
  filter,
  __
} from 'ramda'
// import { reject, values, pluck, isNil, split, pathOr, last, forEach, propEq, filter, __, map, omit } from 'ramda'

// creates a saga monitor
export default (reactotron, options) => {
  // a lookup table of effects - keys are numbers, values are objects
  let effects = {}

  // filtering that effect table
  const byParentId = propEq('parentEffectId', __)
  const byLabel = propEq('label', __)
  const getChildEffectInfos = parentEffectId => filter(byParentId(parentEffectId), values(effects))
  const getChildEffectIds = effectId => pluck('effectId', getChildEffectInfos(effectId))

  // start a relative timer
  const timer = reactotron.startTimer()

  // ---------------- Sending Effect Updates ----------------
  // const sendReactotronEffectTree = () => reactotron.send('saga.effect.update', effects)

  // ---------------- Starting -----------------------------

  // redux-saga calls this when an effect is triggered (started)
  const effectTriggered = description => {
    const { effect, effectId, parentEffectId, label } = description

    // create an EffectInfo to hold the details
    const effectInfo = {
      effectId,
      parentEffectId,
      effect,
      label,
      status: PENDING,
      name: getEffectName(effect),
      description: getEffectDescription(effect),
      result: null,
      startedAt: timer()
    }

    // store it
    effects[effectId] = effectInfo

    // send it
    // sendReactotronEffectTree()
  }

  // ---------------- Finishing ----------------------------

  // update the duration of the effect
  const updateDuration = effectInfo => {
    effectInfo.duration = timer() - effectInfo.startedAt
  }

  // fires when a task has been resolved
  const taskResolved = (effectId, taskResult) => {
    // lookup this effect info
    const effectInfo = effects[effectId]
    updateDuration(effectInfo)
    const { duration } = effectInfo

    // grab the parent too
    const { parentEffectId } = effectInfo
    const parentEffectInfo = effects[parentEffectId]
    const children = []

    // a human friendly name of the saga task
    let sagaDescription
    // what caused the trigger
    let triggerType

    // for FORK tasks, we have a bunch on things to pass along
    if (effectInfo.name === FORK) {
      const args = pathOr([], split('.', 'effect.FORK.args'), effectInfo)
      const lastArg = last(args)
      triggerType = lastArg && lastArg.type
      if (parentEffectInfo) {
        if (parentEffectInfo.name === ITERATOR) {
          sagaDescription = parentEffectInfo.description
        }
      } else {
        sagaDescription = '(root)'
        triggerType = `${effectInfo.description}()`
      }

      // flatten out the nested effects
      const buildChild = (depth, effectId) => {
        const sourceEffectInfo = effects[effectId]
        if (isNil(sourceEffectInfo)) return

        let extra = null
        switch (sourceEffectInfo.name) {
          case CALL:
            extra = sourceEffectInfo.effect[sourceEffectInfo.name].args
            break

          case PUT:
            extra = sourceEffectInfo.effect[sourceEffectInfo.name].action
            break

          // children handle this
          case RACE:
            break

          // TODO: More of customizations needed here

          default:
            extra = sourceEffectInfo.effect[sourceEffectInfo.name]
            break
        }
        // assemble the structure
        children.push({
          depth,
          effectId: sourceEffectInfo.effectId,
          parentEffectId: sourceEffectInfo.parentEffectId || null,
          name: sourceEffectInfo.name || null,
          description: sourceEffectInfo.description || null,
          duration: Math.round(sourceEffectInfo.duration),
          status: sourceEffectInfo.status || null,
          winner: sourceEffectInfo.winner || null,
          loser: sourceEffectInfo.loser || null,
          result: sourceEffectInfo.result || null,
          extra: extra || null
        })

        // rerun this function for our children
        forEach(x => buildChild(depth + 1, x), getChildEffectIds(effectId))
      }
      const xs = getChildEffectIds(effectId)
      forEach(effectId => buildChild(0, effectId), xs)
    }

    reactotron.send('saga.task.complete', {
      triggerType: triggerType || effectInfo.description,
      description: sagaDescription,
      duration: Math.round(duration),
      children
    })

    // effects = omit(map(String, pluck('effectId', children)), effects)
  }

  // redux-saga calls this when an effect is resolved (successfully or not)
  const effectResolved = (effectId, result) => {
    // lookup this effect info and set the rsult
    const effectInfo = effects[effectId]
    updateDuration(effectInfo)
    effectInfo.result = result

    // this is a task
    if (is.task(result)) {
      // when the task promise resolves,
      const onTaskResult = taskResult => {
        if (result.isCancelled()) {
          effectCancelled(effectId)
        } else {
          effectResolved(effectId, taskResult)
          taskResolved(effectId, taskResult)
        }
      }

      // hook the promise to capture the resolve or reject
      result.done.then(onTaskResult, error => {
        effectRejected(effectId, error)
        if (!error.reactotronWasHere) {
          reactotron.reportError(error)
        }
        error.reactotronWasHere = true
      })
    } else {
      // this is an effect and we are complete
      effectInfo.status = RESOLVED
      effectInfo.result = result
      if (effectInfo.name === RACE) {
        setRaceWinner(effectId, result)
      }
    }

    // send it
    // sendReactotronEffectTree()
  }

  // flags on of the children as the winner
  const setRaceWinner = (effectId, resultOrError) => {
    const winnerLabel = Object.keys(resultOrError)[0]
    const children = getChildEffectInfos(effectId)
    const winningChildren = filter(byLabel(winnerLabel), children)
    const losingChildren = reject(byLabel(winnerLabel), children)
    const setWinner = effectInfo => {
      effectInfo.winner = true
    }
    const setLoser = effectInfo => {
      effectInfo.loser = true
    }

    // set the 1 (hopefully 1) winner -- but i'm not sure
    forEach(setWinner, winningChildren)
    forEach(setLoser, losingChildren)
  }

  // ---------------- Failing ------------------------------

  // redux-saga calls this when an effect is rejected (an error has happened)
  const effectRejected = (effectId, error) => {
    const effectInfo = effects[effectId]
    updateDuration(effectInfo)
    effectInfo.status = REJECTED
    effectInfo.error = error
    if (effectInfo.name === RACE) {
      setRaceWinner(effectId, error)
    }

    // send it
    // sendReactotronEffectTree()
  }

  // ---------------- Cancelling ---------------------------

  // redux-saga calls this when an effect is cancelled
  const effectCancelled = effectId => {
    const effectInfo = effects[effectId]
    updateDuration(effectInfo)
    effectInfo.status = CANCELLED

    // send it
    // sendReactotronEffectTree()
  }

  // the interface for becoming a redux-saga monitor
  return {
    effectTriggered,
    effectResolved,
    effectRejected,
    effectCancelled,
    actionDispatched: () => {}
  }
}
<commit_msg>Interface of Task object has changed<commit_after>import { is } from 'redux-saga/utils'
import getEffectName from './get-effect-name'
import getEffectDescription from './get-effect-description'
import {
  ITERATOR,
  CALL,
  PUT,
  FORK,
  RACE,
  PENDING,
  RESOLVED,
  REJECTED,
  CANCELLED
} from './saga-constants'
import {
  reject,
  values,
  pluck,
  isNil,
  split,
  pathOr,
  last,
  forEach,
  propEq,
  filter,
  __
} from 'ramda'
// import { reject, values, pluck, isNil, split, pathOr, last, forEach, propEq, filter, __, map, omit } from 'ramda'

// creates a saga monitor
export default (reactotron, options) => {
  // a lookup table of effects - keys are numbers, values are objects
  let effects = {}

  // filtering that effect table
  const byParentId = propEq('parentEffectId', __)
  const byLabel = propEq('label', __)
  const getChildEffectInfos = parentEffectId => filter(byParentId(parentEffectId), values(effects))
  const getChildEffectIds = effectId => pluck('effectId', getChildEffectInfos(effectId))

  // start a relative timer
  const timer = reactotron.startTimer()

  // ---------------- Sending Effect Updates ----------------
  // const sendReactotronEffectTree = () => reactotron.send('saga.effect.update', effects)

  // ---------------- Starting -----------------------------

  // redux-saga calls this when an effect is triggered (started)
  const effectTriggered = description => {
    const { effect, effectId, parentEffectId, label } = description

    // create an EffectInfo to hold the details
    const effectInfo = {
      effectId,
      parentEffectId,
      effect,
      label,
      status: PENDING,
      name: getEffectName(effect),
      description: getEffectDescription(effect),
      result: null,
      startedAt: timer()
    }

    // store it
    effects[effectId] = effectInfo

    // send it
    // sendReactotronEffectTree()
  }

  // ---------------- Finishing ----------------------------

  // update the duration of the effect
  const updateDuration = effectInfo => {
    effectInfo.duration = timer() - effectInfo.startedAt
  }

  // fires when a task has been resolved
  const taskResolved = (effectId, taskResult) => {
    // lookup this effect info
    const effectInfo = effects[effectId]
    updateDuration(effectInfo)
    const { duration } = effectInfo

    // grab the parent too
    const { parentEffectId } = effectInfo
    const parentEffectInfo = effects[parentEffectId]
    const children = []

    // a human friendly name of the saga task
    let sagaDescription
    // what caused the trigger
    let triggerType

    // for FORK tasks, we have a bunch on things to pass along
    if (effectInfo.name === FORK) {
      const args = pathOr([], split('.', 'effect.FORK.args'), effectInfo)
      const lastArg = last(args)
      triggerType = lastArg && lastArg.type
      if (parentEffectInfo) {
        if (parentEffectInfo.name === ITERATOR) {
          sagaDescription = parentEffectInfo.description
        }
      } else {
        sagaDescription = '(root)'
        triggerType = `${effectInfo.description}()`
      }

      // flatten out the nested effects
      const buildChild = (depth, effectId) => {
        const sourceEffectInfo = effects[effectId]
        if (isNil(sourceEffectInfo)) return

        let extra = null
        switch (sourceEffectInfo.name) {
          case CALL:
            extra = sourceEffectInfo.effect[sourceEffectInfo.name].args
            break

          case PUT:
            extra = sourceEffectInfo.effect[sourceEffectInfo.name].action
            break

          // children handle this
          case RACE:
            break

          // TODO: More of customizations needed here

          default:
            extra = sourceEffectInfo.effect[sourceEffectInfo.name]
            break
        }
        // assemble the structure
        children.push({
          depth,
          effectId: sourceEffectInfo.effectId,
          parentEffectId: sourceEffectInfo.parentEffectId || null,
          name: sourceEffectInfo.name || null,
          description: sourceEffectInfo.description || null,
          duration: Math.round(sourceEffectInfo.duration),
          status: sourceEffectInfo.status || null,
          winner: sourceEffectInfo.winner || null,
          loser: sourceEffectInfo.loser || null,
          result: sourceEffectInfo.result || null,
          extra: extra || null
        })

        // rerun this function for our children
        forEach(x => buildChild(depth + 1, x), getChildEffectIds(effectId))
      }
      const xs = getChildEffectIds(effectId)
      forEach(effectId => buildChild(0, effectId), xs)
    }

    reactotron.send('saga.task.complete', {
      triggerType: triggerType || effectInfo.description,
      description: sagaDescription,
      duration: Math.round(duration),
      children
    })

    // effects = omit(map(String, pluck('effectId', children)), effects)
  }

  // redux-saga calls this when an effect is resolved (successfully or not)
  const effectResolved = (effectId, result) => {
    // lookup this effect info and set the rsult
    const effectInfo = effects[effectId]
    updateDuration(effectInfo)
    effectInfo.result = result

    // this is a task
    if (is.task(result)) {
      // when the task promise resolves,
      const onTaskResult = taskResult => {
        if (result.isCancelled()) {
          effectCancelled(effectId)
        } else {
          effectResolved(effectId, taskResult)
          taskResolved(effectId, taskResult)
        }
      }

      // hook the promise to capture the resolve or reject
      var promise = result.done ? result.done : result.toPromise()
      promise.then(onTaskResult, error => {
        effectRejected(effectId, error)
        if (!error.reactotronWasHere) {
          reactotron.reportError(error)
        }
        error.reactotronWasHere = true
      })
    } else {
      // this is an effect and we are complete
      effectInfo.status = RESOLVED
      effectInfo.result = result
      if (effectInfo.name === RACE) {
        setRaceWinner(effectId, result)
      }
    }

    // send it
    // sendReactotronEffectTree()
  }

  // flags on of the children as the winner
  const setRaceWinner = (effectId, resultOrError) => {
    const winnerLabel = Object.keys(resultOrError)[0]
    const children = getChildEffectInfos(effectId)
    const winningChildren = filter(byLabel(winnerLabel), children)
    const losingChildren = reject(byLabel(winnerLabel), children)
    const setWinner = effectInfo => {
      effectInfo.winner = true
    }
    const setLoser = effectInfo => {
      effectInfo.loser = true
    }

    // set the 1 (hopefully 1) winner -- but i'm not sure
    forEach(setWinner, winningChildren)
    forEach(setLoser, losingChildren)
  }

  // ---------------- Failing ------------------------------

  // redux-saga calls this when an effect is rejected (an error has happened)
  const effectRejected = (effectId, error) => {
    const effectInfo = effects[effectId]
    updateDuration(effectInfo)
    effectInfo.status = REJECTED
    effectInfo.error = error
    if (effectInfo.name === RACE) {
      setRaceWinner(effectId, error)
    }

    // send it
    // sendReactotronEffectTree()
  }

  // ---------------- Cancelling ---------------------------

  // redux-saga calls this when an effect is cancelled
  const effectCancelled = effectId => {
    const effectInfo = effects[effectId]
    updateDuration(effectInfo)
    effectInfo.status = CANCELLED

    // send it
    // sendReactotronEffectTree()
  }

  // the interface for becoming a redux-saga monitor
  return {
    effectTriggered,
    effectResolved,
    effectRejected,
    effectCancelled,
    actionDispatched: () => {}
  }
}
<|endoftext|>


<commit_before>//v1.0 as Starting Point
//v2.0 Cleaning up code. Setting up Menu Structure

//Declare and Initialize Variables
var audio_Bell = new Audio("http://xenko.comxa.com/Ship_Bell.mp3");
	audio_Bell.volume=1;
var audio_Chime = new Audio("http://xenko.comxa.com/Chime.mp3");
	audio_Chime.volume=1;
var desc = "Error";
var i = 0;
var IdleStatus = 0;
var incoming_ONG = 0;
var KeepBlue = 0;
var NinjaAutoClickStatus = 0;
var RKAutoClickStatus = 0;
var RKAlertFrequency = 8;
var Time_to_ONG = 1800000;

//Beach Ball Startup
	//Set Settings
	IdleStatus = prompt("Set to idle? (1 = Yes, 0 = No)");
	RKAutoClickStatus = IdleStatus;
	NinjaAutoClickStatus = IdleStatus;
	if (Molpy.Got('Kitnip') == 1){RKAlertFrequency = 10;}

	//Create Menu
	$('#optionsItems').append('

RK Auto Click

');

//Main Loop
setInterval(function() {
    RedundaKitty();
    Ninja();
}, 1800);

function Ninja() {
	//Ninja Warnings and Incoming ONG Warning
    Time_to_ONG = (Molpy.NPlength * 1000) - Molpy.ONGelapsed;
	if (Molpy.ninjad == 0) {
        if (Molpy.npbONG == 0) {
			$("#beach").css("border","4px solid red");
        }
        else {
            incoming_ONG = 0;
            if (NinjaAutoClickStatus == 1) {
                Molpy.ClickBeach();
                $("#beach").css("border","4px solid blue");
                KeepBlue = 1;
            }
            else {
            $("#beach").css("border","4px solid green");
            }

        }
	}
    else if (Time_to_ONG <= 15000) {
        $("#beach").css("border","4px solid yellow");
            if (incoming_ONG == 0) {
                audio_Chime.play();
                incoming_ONG = 1;
            }
    }
    else if (KeepBlue == 0) {
        $("#beach").css("border","1px solid white");
	}
}

function WriteOptionDescription(option, description) {

}

function RedundaKitty() {
	i = Molpy.redactedToggle - Molpy.redactedCountup;

	if (Molpy.redactedVisible > 0) {
		//Clicks if AutoClicker Enabled
		if (RKAutoClickStatus == 1) {
			Molpy.ClickRedacted();
		}

		else {
			//Redundakitty Notifications (Title Bar and Audio)
			document.title = "! kitten !";
			if (Math.floor(i % RKAlertFrequency) == 0) {
				audio_Bell.play();
			}
		}
	}
	else {
		document.title = Molpy.redactedToggle-Molpy.redactedCountup;
		if (i != 0) { i = 0; }
	}
}

function SwitchOption(option) {
	if (option == 'RKAutoClick') {
		RKAutoClickStatus++;
		/*if (RKAutoClickStatus > 1) {
			RKAutoClickStatus = 0;
		}*/
		switch (RKAutoClickStatus) {
			case 2:
				RKAutoClickStatus = 0;
				desc = "Off";
				break;
			case 1:
				desc = "On";
				break;
			case 0:
				desc = "Off";
				break;
		}

	}
	g(option + 'Desc').innerHTML = '
' + desc;
}<commit_msg>Testing Option Description<commit_after>//v1.0 as Starting Point
//v2.0 Cleaning up code. Setting up Menu Structure

//Declare and Initialize Variables
var audio_Bell = new Audio("http://xenko.comxa.com/Ship_Bell.mp3");
	audio_Bell.volume=1;
var audio_Chime = new Audio("http://xenko.comxa.com/Chime.mp3");
	audio_Chime.volume=1;
var desc = "Error";
var i = 0;
var IdleStatus = 0;
var incoming_ONG = 0;
var KeepBlue = 0;
var NinjaAutoClickStatus = 0;
var RKAutoClickStatus = 0;
var RKAlertFrequency = 8;
var Time_to_ONG = 1800000;

//Beach Ball Startup
	//Set Settings
	IdleStatus = prompt("Set to idle? (1 = Yes, 0 = No)");
	RKAutoClickStatus = IdleStatus;
	NinjaAutoClickStatus = IdleStatus;
	if (Molpy.Got('Kitnip') == 1){RKAlertFrequency = 10;}

	//Create Menu
	$('#optionsItems').append('

RK Auto Click

');
	WriteOptionDescription('RKAutoClick',RKAutoClickStatus);

//Main Loop
setInterval(function() {
    RedundaKitty();
    Ninja();
}, 1800);

function Ninja() {
	//Ninja Warnings and Incoming ONG Warning
    Time_to_ONG = (Molpy.NPlength * 1000) - Molpy.ONGelapsed;
	if (Molpy.ninjad == 0) {
        if (Molpy.npbONG == 0) {
			$("#beach").css("border","4px solid red");
        }
        else {
            incoming_ONG = 0;
            if (NinjaAutoClickStatus == 1) {
                Molpy.ClickBeach();
                $("#beach").css("border","4px solid blue");
                KeepBlue = 1;
            }
            else {
            $("#beach").css("border","4px solid green");
            }

        }
	}
    else if (Time_to_ONG <= 15000) {
        $("#beach").css("border","4px solid yellow");
            if (incoming_ONG == 0) {
                audio_Chime.play();
                incoming_ONG = 1;
            }
    }
    else if (KeepBlue == 0) {
        $("#beach").css("border","1px solid white");
	}
}

function RedundaKitty() {
	i = Molpy.redactedToggle - Molpy.redactedCountup;

	if (Molpy.redactedVisible > 0) {
		//Clicks if AutoClicker Enabled
		if (RKAutoClickStatus == 1) {
			Molpy.ClickRedacted();
		}

		else {
			//Redundakitty Notifications (Title Bar and Audio)
			document.title = "! kitten !";
			if (Math.floor(i % RKAlertFrequency) == 0) {
				audio_Bell.play();
			}
		}
	}
	else {
		document.title = Molpy.redactedToggle-Molpy.redactedCountup;
		if (i != 0) { i = 0; }
	}
}

function SwitchOption(option) {
	if (option == 'RKAutoClick') {
		RKAutoClickStatus++;
		/*if (RKAutoClickStatus > 1) {
			RKAutoClickStatus = 0;
		}*/
		switch (RKAutoClickStatus) {
			case 2:
				RKAutoClickStatus = 0;
				desc = "Off";
				break;
			case 1:
				desc = "On";
				break;
			case 0:
				desc = "Off";
				break;
		}

	}
	WriteOptionDescription(option, desc);
}

function WriteOptionDescription(option, description) {
	g(option + 'Desc').innerHTML = '
' + description;
}<|endoftext|>


<commit_before>import { h, Component } 				from 'preact/preact';

import NavSpinner						from 'com/nav-spinner/spinner';

import DropdownCommon					from 'com/dropdown-common/common';

import $Notification					from '../../shrub/js/notification/notification';
import $Node							from '../../shrub/js/node/node';
import $Note							from '../../shrub/js/note/note';

export default class DropdownNotification extends Component {

	constructor( props ) {
		super(props);

		this.state = {
			notifications: [],
			notificationsTotal: -1,
		};
	}

	componentDidMount() {
		if (this.props.getNew) {
			$Notification.GetFeedUnread(0, 8).then((r) => {
				const caller_id = r.caller_id;
				this.setState({
					status: r.status,
					notificationsTotal: r.feed.length
				});
				r.feed.forEach((notification) => {
					this.queryInfoForNotification(caller_id, notification);
				});

			}).catch((e)=> console.log('[Notification error]', e));
		} else {
			$Notification.GetFeedAll(-8, 8).then((r) => {
				const caller_id = r.caller_id;
				r.feed.forEach((notification) => {
					this.queryInfoForNotification(caller_id, notification);
				});
			});
		}
	}

	queryInfoForNotification(caller_id, notification) {

		let results = {
			caller_id: caller_id,
			users: new Map([[caller_id, null]]),
			usersCompleted: 'no',
			people: {},
			node: {},
			note: {},
			notification: notification,
		};

		$Node.GetMy().then((response) => {
			results.people = {
				following: response.star ? response.star : [],
				followers: response.refs.star ? response.refs.star : [],
				friends: Array.isArray(response.refs.star) ? new Set([...response.star].filter((i) => response.refs.star.indexOf(i) > -1)) : [],
			};
			results.people.following.forEach((user) => {
				results.users.set(user, null);
			});
			results.people.followers.forEach((user) => {
				results.users.set(user, null);
			});
			results.people.completed = true;
			this.processNotificationStepTwo(results);
		});

		$Node.Get(notification.node).then((response) => {
			const nodes = response.node;
			if (nodes && nodes.length == 1) {
				const node = nodes[0];
				results.node = node;
				results.node.selfauthored = false;
				if (node.author == caller_id) {
					results.node.selfauthored = true;
				} else if (node.link && node.link.author) {
					node.link.author.forEach((author) => {
						if (author == caller_id) {
							results.node.selfauthored = true;
						}
					});
				}

				results.node.completed = true;

				if (!results.node.selfauthored) {
					results.users.set(node.author, null);
					if (node.link.author) {
						node.link.author.forEach((user_id) => {
							results.users.set(user_id, null);
						});
					}
				}
				this.processNotificationStepTwo(results);
			} else {
				results.node = {corrupted: true, completed: true};
			}
		});

		if (notification.note) {
			$Note.Get(notification.node).then((response) => {
				if (response.note && response.note.length > 0) {
					response.note.forEach((note) => {
						if (note.id == notification.note) {
							results.note = note;
							results.note.selfauthored = note.author == caller_id;
							results.note.completed = true;
							if (!results.note.selfauthored) {
								results.users.set(note.author, null);
							}
						}

					});
				}
				this.processNotificationStepTwo(results);
			});
		} else {
			results.note.completed = true;
			this.processNotificationStepTwo(results);
		}

	}

	processNotificationStepTwo(notification) {
		if (!(notification.people.completed && notification.note.completed && notification.node.completed)) {
			return;
		}

		if (notification.usersCompleted == 'no') {
			notification.usersCompleted = 'started';
			let users = [];
			notification.users.forEach((value, key) => users.push(key));
			$Node.Get(users).then((response) => {
				response.node.forEach((node) => {
					if (node.type == 'user') {
						notification.users.set(node.id, node);
					}
				});
				notification.usersCompleted = 'yes';
				this.processNotificationStepTwo(notification);
			});
			return;
		} else if (notification.usersCompleted == 'started') {
			return;
		}

		let notifications = [...this.state.notifications];
		notifications.push(notification);
		this.setState({notifications: notifications});
	}

	getSocialStringList(notification, relation) {
		const social = notification.people[relation];
		let isFriend = social.has(notification.node.author);
		let myFriends = isFriend ? new Set(notification.users.get(notification.node.author)) : new Set();

		if (notification.node.link) {
			notification.node.link.forEach((author) => {
				if (social.has(author)) {
					isFriend = true;
					myFriends.add(notification.users.get(author));
				}
			});
		}

		let User = null;
		const friendArray = [...myFriends];
		let names = null;
		if (friendArray.length > 1) {
			names = friendArray.slice(0, friendArray.length - 1).join(', ') + ' & ' + friendArray[friendArray.length - 1];
		} else {
			names = friendArray[0];
		}
		return {count: myFriends.size, string: names, list: myFriends};
	}

	getNotifications() {
		let Notifications = [];
		const feed = this.state.notifications;

		if (feed && feed.length > 0) {

			feed.forEach((notification) => {
				if (notification) {
					console.log('feed:notification', notification);
					//TODO: Make real notifications based on the notification-data
					let nodeType = notification.node.type;
					if (notification.node.subtype) {
						nodeType = notification.node.subtype;
					}

					let nodeAuthor = null;
					if (notification.node) {
						nodeAuthor = notification.users.get(notification.node.author);
					}
					let noteAuthor = null;
					if (notification.note) {
						noteAuthor = notification.users.get(notification.note.author);
					}

					if (notification.notification.note) {

						if (!notification.node.selfauthored && !notification.note.selfauthored) {

							const myAtName = "@" + notification.users.get(notification.caller_id).name;
							const firstAt = notification.note.body.indexOf(myAtName);
							if (firstAt > -1) {
								Notifications.push((

 window.location = notification.node.path}>
									{noteAuthor.name} mentioned you in a commented on {nodeAuthor.name}'s {nodeType}
									 "{notification.node.name}"

								));
							} else {
								Notifications.push((

 window.location = notification.node.path}>
									{noteAuthor.name} commented on {nodeAuthor.name}'s {nodeType}
									 "{notification.node.name}"

								));
							}
						} else if (notification.node.selfauthored && !notification.note.selfauthored) {
							Notifications.push((

 window.location = notification.node.path}>
								{noteAuthor.name} commented on your {nodeType}
								 "{notification.node.name}"

							));
						} else {
							//Notification about weird stuff
							Notifications.push((

 window.location = notification.node.path}>
								You recieved notification that you posted a comment on {nodeAuthor.name}'s {nodeType}
								 "{notification.node.name}"
								please report to dev-team that you already knew this.

							));
						}
					} else {
						if (notification.node.selfauthored) {

							Notifications.push((

 window.location = notification.node.path}>
								Your {nodeType}
								 "{notification.node.name}"
								was either created or updated.

							));
						} else {

							const friends = this.getSocialStringList(notification, 'friends');

							if (friends.count > 0) {
								User = (Your friend{friends.count > 1 ? 's' : ''} {friends.string});
							} else {
								const following = this.getSocialStringList(notification, 'following');
								if (following.count > 0) {
									User = ({following.string});
								} else {
									User = ({nodeAuthor.name});
								}
							}

							if (notification.node.type == 'post') {
								Notifications.push((

 window.location = notification.node.path}>
									{User} posted
									 "{notification.node.name}"

								));
							} else {
								Notifications.push((

 window.location = notification.node.path}>
									{User} posted a {nodeType}
									 "{notification.node.name}"

								));
							}
						}

					}
				} else {
					console.log('[rejectedNotification]', notification);
				}
			});

		}
		console.log(Notifications.length, feed.length);
		return Notifications;
	}

	render( props ) {
		const state = this.state;

		let ShowSpinner = null;
		let Notifications = null;

		if (state.status === undefined) {
			ShowSpinner = (<NavSpinner />);
		} else if (state.status != 200) {
			Notifications = (
An error occurred retrieving the notifications...
);
		} else {
			if (state.notificationsTotal > state.notifications.length) {
				ShowSpinner = (<NavSpinner />);
			}
			Notifications = this.getNotifications();
		}

		return (
			<DropdownCommon>
			{Notifications}
			{ShowSpinner}
			</DropdownCommon>
		);
	}

}<commit_msg>If notification cant be understood decrease total count<commit_after>import { h, Component } 				from 'preact/preact';

import NavSpinner						from 'com/nav-spinner/spinner';

import DropdownCommon					from 'com/dropdown-common/common';

import $Notification					from '../../shrub/js/notification/notification';
import $Node							from '../../shrub/js/node/node';
import $Note							from '../../shrub/js/note/note';

export default class DropdownNotification extends Component {

	constructor( props ) {
		super(props);

		this.state = {
			notifications: [],
			notificationsTotal: -1,
		};
	}

	componentDidMount() {
		if (this.props.getNew) {
			$Notification.GetFeedUnread(0, 8).then((r) => {
				const caller_id = r.caller_id;
				this.setState({
					status: r.status,
					notificationsTotal: r.feed.length
				});
				r.feed.forEach((notification) => {
					this.queryInfoForNotification(caller_id, notification);
				});

			}).catch((e)=> console.log('[Notification error]', e));
		} else {
			$Notification.GetFeedAll(-8, 8).then((r) => {
				const caller_id = r.caller_id;
				r.feed.forEach((notification) => {
					this.queryInfoForNotification(caller_id, notification);
				});
			});
		}
	}

	queryInfoForNotification(caller_id, notification) {

		let results = {
			caller_id: caller_id,
			users: new Map([[caller_id, null]]),
			usersCompleted: 'no',
			people: {},
			node: {},
			note: {},
			notification: notification,
		};

		$Node.GetMy().then((response) => {
			results.people = {
				following: response.star ? response.star : [],
				followers: response.refs.star ? response.refs.star : [],
				friends: Array.isArray(response.refs.star) ? new Set([...response.star].filter((i) => response.refs.star.indexOf(i) > -1)) : [],
			};
			results.people.following.forEach((user) => {
				results.users.set(user, null);
			});
			results.people.followers.forEach((user) => {
				results.users.set(user, null);
			});
			results.people.completed = true;
			this.processNotificationStepTwo(results);
		});

		$Node.Get(notification.node).then((response) => {
			const nodes = response.node;
			if (nodes && nodes.length == 1) {
				const node = nodes[0];
				results.node = node;
				results.node.selfauthored = false;
				if (node.author == caller_id) {
					results.node.selfauthored = true;
				} else if (node.link && node.link.author) {
					node.link.author.forEach((author) => {
						if (author == caller_id) {
							results.node.selfauthored = true;
						}
					});
				}

				results.node.completed = true;

				if (!results.node.selfauthored) {
					results.users.set(node.author, null);
					if (node.link.author) {
						node.link.author.forEach((user_id) => {
							results.users.set(user_id, null);
						});
					}
				}
				this.processNotificationStepTwo(results);
			} else {
				results.node = {corrupted: true, completed: true};
				this.setState({notificationsTotal: this.state.notificationsTotal - 1});
			}
		});

		if (notification.note) {
			$Note.Get(notification.node).then((response) => {
				if (response.note && response.note.length > 0) {
					response.note.forEach((note) => {
						if (note.id == notification.note) {
							results.note = note;
							results.note.selfauthored = note.author == caller_id;
							results.note.completed = true;
							if (!results.note.selfauthored) {
								results.users.set(note.author, null);
							}
						}

					});
				}
				this.processNotificationStepTwo(results);
			});
		} else {
			results.note.completed = true;
			this.processNotificationStepTwo(results);
		}

	}

	processNotificationStepTwo(notification) {
		if (!(notification.people.completed && notification.note.completed && notification.node.completed)) {
			return;
		}

		if (notification.usersCompleted == 'no') {
			notification.usersCompleted = 'started';
			let users = [];
			notification.users.forEach((value, key) => users.push(key));
			$Node.Get(users).then((response) => {
				response.node.forEach((node) => {
					if (node.type == 'user') {
						notification.users.set(node.id, node);
					}
				});
				notification.usersCompleted = 'yes';
				this.processNotificationStepTwo(notification);
			});
			return;
		} else if (notification.usersCompleted == 'started') {
			return;
		}

		let notifications = [...this.state.notifications];
		notifications.push(notification);
		this.setState({notifications: notifications});
	}

	getSocialStringList(notification, relation) {
		const social = notification.people[relation];
		let isFriend = social.has(notification.node.author);
		let myFriends = isFriend ? new Set(notification.users.get(notification.node.author)) : new Set();

		if (notification.node.link) {
			notification.node.link.forEach((author) => {
				if (social.has(author)) {
					isFriend = true;
					myFriends.add(notification.users.get(author));
				}
			});
		}

		let User = null;
		const friendArray = [...myFriends];
		let names = null;
		if (friendArray.length > 1) {
			names = friendArray.slice(0, friendArray.length - 1).join(', ') + ' & ' + friendArray[friendArray.length - 1];
		} else {
			names = friendArray[0];
		}
		return {count: myFriends.size, string: names, list: myFriends};
	}

	getNotifications() {
		let Notifications = [];
		const feed = this.state.notifications;

		if (feed && feed.length > 0) {

			feed.forEach((notification) => {
				if (notification) {
					console.log('feed:notification', notification);
					//TODO: Make real notifications based on the notification-data
					let nodeType = notification.node.type;
					if (notification.node.subtype) {
						nodeType = notification.node.subtype;
					}

					let nodeAuthor = null;
					if (notification.node) {
						nodeAuthor = notification.users.get(notification.node.author);
					}
					let noteAuthor = null;
					if (notification.note) {
						noteAuthor = notification.users.get(notification.note.author);
					}

					if (notification.notification.note) {

						if (!notification.node.selfauthored && !notification.note.selfauthored) {

							const myAtName = "@" + notification.users.get(notification.caller_id).name;
							const firstAt = notification.note.body.indexOf(myAtName);
							if (firstAt > -1) {
								Notifications.push((

 window.location = notification.node.path}>
									{noteAuthor.name} mentioned you in a commented on {nodeAuthor.name}'s {nodeType}
									 "{notification.node.name}"

								));
							} else {
								Notifications.push((

 window.location = notification.node.path}>
									{noteAuthor.name} commented on {nodeAuthor.name}'s {nodeType}
									 "{notification.node.name}"

								));
							}
						} else if (notification.node.selfauthored && !notification.note.selfauthored) {
							Notifications.push((

 window.location = notification.node.path}>
								{noteAuthor.name} commented on your {nodeType}
								 "{notification.node.name}"

							));
						} else {
							//Notification about weird stuff
							Notifications.push((

 window.location = notification.node.path}>
								You recieved notification that you posted a comment on {nodeAuthor.name}'s {nodeType}
								 "{notification.node.name}"
								please report to dev-team that you already knew this.

							));
						}
					} else {
						if (notification.node.selfauthored) {

							Notifications.push((

 window.location = notification.node.path}>
								Your {nodeType}
								 "{notification.node.name}"
								was either created or updated.

							));
						} else {

							const friends = this.getSocialStringList(notification, 'friends');

							if (friends.count > 0) {
								User = (Your friend{friends.count > 1 ? 's' : ''} {friends.string});
							} else {
								const following = this.getSocialStringList(notification, 'following');
								if (following.count > 0) {
									User = ({following.string});
								} else {
									User = ({nodeAuthor.name});
								}
							}

							if (notification.node.type == 'post') {
								Notifications.push((

 window.location = notification.node.path}>
									{User} posted
									 "{notification.node.name}"

								));
							} else {
								Notifications.push((

 window.location = notification.node.path}>
									{User} posted a {nodeType}
									 "{notification.node.name}"

								));
							}
						}

					}
				} else {
					console.log('[rejectedNotification]', notification);
				}
			});

		}
		console.log(Notifications.length, feed.length);
		return Notifications;
	}

	render( props ) {
		const state = this.state;

		let ShowSpinner = null;
		let Notifications = null;

		if (state.status === undefined) {
			ShowSpinner = (<NavSpinner />);
		} else if (state.status != 200) {
			Notifications = (
An error occurred retrieving the notifications...
);
		} else {
			if (state.notificationsTotal > state.notifications.length) {
				ShowSpinner = (<NavSpinner />);
			}
			Notifications = this.getNotifications();
		}

		return (
			<DropdownCommon>
			{Notifications}
			{ShowSpinner}
			</DropdownCommon>
		);
	}

}<|endoftext|>


<commit_before>import React, { Component } from 'react';
import _ from 'lodash';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { Dropdown, Icon, Menu, Modal } from 'antd';
import classnames from 'classnames';
import scrollIntoView from 'dom-scroll-into-view';
import history from '../../common/history';
import { closeTab } from './redux/actions';
import editorStateMap from './editorStateMap';
import modelManager from '../common/monaco/modelManager';
import { getElementData, getElementFiles } from './helpers';

export class TabsBar extends Component {
  static propTypes = {
    home: PropTypes.object.isRequired,
    router: PropTypes.object.isRequired,
    actions: PropTypes.object.isRequired,
  };

  componentDidUpdate(prevProps) {
    if (this.delayScroll) clearTimeout(this.delayScroll);
    this.delayScroll = setTimeout(this.scrollActiveTabIntoView, 100);
  }

  getCurrentFile() {
    // only for element page
    const { pathname } = this.props.router.location;
    const arr = _.compact(pathname.split('/')).map(decodeURIComponent);
    if (arr[0] === 'element') return arr[1];
    return null;
  }

  getElementData(file) {
    const { elementById, projectRoot } = this.props.home;
    if (!file) return null;
    file = decodeURIComponent(file);
    const fullPath = projectRoot + file;
    const arr = fullPath.split('.');
    const ext = arr.length > 1 ? arr.pop() : null;
    const ele = elementById[file];

    if (!ele) return null;

    return {
      ...ele,
      hasDiagram: /^(js|jsx)$/.test(ext),
      hasTest: ele.feature && /^(js|jsx)$/.test(ext),
      hasCode: /^(js|jsx|html|css|less|scss|txt|json|sass|md|log|pl|py|sh|cmd)$/.test(ext),
      isPic: /^(jpe?g|png|gif|bmp)$/.test(ext),
    };
  }

  getTabTooltip(tab) {
    if (!_.startsWith(tab.key, '#')) return tab.key;
    return tab.name;
  }

  isChanged(tab) {
    const files = getElementFiles(this.props.home, tab.key);
    if (!files) return false;
    return (
      modelManager.isChanged(files.code) || modelManager.isChanged(files.test) || modelManager.isChanged(files.style)
    );
  }

  isCurrentTab(tab) {
    const { pathname } = document.location;
    switch (tab.type) {
      case 'home':
        return pathname === '/';
      case 'element':
        return tab.key === this.getCurrentFile();
      case 'routes':
        return pathname.indexOf(`/${tab.key}`) === 0;
      case 'tests':
        return _.startsWith(pathname, '/tools/tests');
      case 'coverage':
        return _.startsWith(pathname, '/tools/coverage');
      case 'build':
        return _.startsWith(pathname, '/tools/build');
      default:
        return false;
    }
  }

  openTab = key => {
    const tab = _.find(this.props.home.openTabs, { key });
    let path;
    switch (tab.type) {
      case 'home':
        path = '/';
        break;
      case 'element':
        path = `/element/${encodeURIComponent(key)}/${tab.subTab}`;
        break;
      case 'routes':
        path = `/${tab.key}/${tab.subTab || ''}`;
        break;
      case 'tests':
      case 'coverage':
      case 'build':
        path = tab.pathname;
        break;
      default:
        console.error('unknown tab type: ', tab);
        break;
    }
    if (document.location.pathname !== path) {
      history.push(path);
    }
  };

  scrollActiveTabIntoView = () => {
    delete this.delayScroll;
    if (!this.rootNode) return;
    const node = this.rootNode.querySelector('.tab-active');
    if (node) {
      scrollIntoView(node, this.rootNode, {
        allowHorizontalScroll: true,
        onlyScrollIfNeeded: true,
        offsetRight: 100,
        offsetLeft: 100,
      });
    }
    this.rootNode.scrollTop = 0; // Prevent vertical offset when switching tabs.
  };

  handleClose = (evt, tab) => {
    if (evt && evt.stopPropagation) evt.stopPropagation();
    const files = getElementFiles(this.props.home, tab.key);
    const data = getElementData(this.props.home, tab.key);

    const doClose = () => {
      if (files) {
        delete editorStateMap[files.code];
        delete editorStateMap[files.style];
        delete editorStateMap[files.test];
        modelManager.reset(files.code);
        modelManager.reset(files.style);
        modelManager.reset(files.test);
      }

      this.props.actions.closeTab(tab.key);
      const { historyTabs } = this.props.home;
      if (historyTabs.length === 1) {
        history.push('/welcome');
      } else if (this.isCurrentTab(tab)) {
        // Close the current one
        const nextKey = historyTabs[1]; // at this point the props has not been updated.
        this.openTab(nextKey);
      }
    };

    if (files && [files.code, files.test, files.style].some(f => modelManager.isChanged(f))) {
      Modal.confirm({
        title: 'Discard changes?',
        content: `Do you want to discard changes you made to ${data.name}?`,
        okText: 'Discard',
        onOk: () => {
          doClose();
        },
        onCancel: () => {},
      });
    } else {
      doClose();
    }
  };

  handleMenuClick = (tab, menuKey) => {
    const { openTabs } = this.props.home;
    switch (menuKey) {
      case 'close-others':
        openTabs.filter(t => t.key !== tab.key).forEach(t => this.handleClose({}, t));
        break;
      case 'close-right':
        openTabs.slice(_.findIndex(openTabs, { key: tab.key }) + 1).forEach(t => this.handleClose({}, t));
        break;
      case 'close-self':
        this.handleClose({}, tab);
        break;
      default:
        break;
    }
  };

  assignRef = node => {
    this.rootNode = node;
  };

  render() {
    const { openTabs, sidePanelWidth } = this.props.home;
    const getMenu = tab => (
      <Menu onClick={args => this.handleMenuClick(tab, args.key)}>
        <Menu.Item key="close-others">Close others</Menu.Item>
        <Menu.Item key="close-right">Close to the right</Menu.Item>
        <Menu.Item key="close-self">Close</Menu.Item>
      </Menu>
    );
    return (
      <div className="home-tabs-bar" ref={this.assignRef} style={{ marginLeft: `${sidePanelWidth}px` }}>
        {openTabs.map(tab => (
          <Dropdown overlay={getMenu(tab)} trigger={['contextMenu']} key={tab.key}>
             this.openTab(tab.key)}
              className={classnames('tab', {
                'tab-active': this.isCurrentTab(tab),
                'tab-has-change': this.isChanged(tab),
              })}
            >
              <Icon type={tab.icon || 'file'} />
              {tab.name}
              <Icon type="close" onClick={evt => this.handleClose(evt, tab)} />

          </Dropdown>
        ))}

    );
  }
}

/* istanbul ignore next */
function mapStateToProps(state) {
  return {
    home: state.home,
    router: state.router,
  };
}

/* istanbul ignore next */
function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators({ closeTab }, dispatch),
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(TabsBar);
<commit_msg>Close the tab when its element is deleted.<commit_after>import React, { Component } from 'react';
import _ from 'lodash';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { Dropdown, Icon, Menu, Modal } from 'antd';
import classnames from 'classnames';
import scrollIntoView from 'dom-scroll-into-view';
import history from '../../common/history';
import { closeTab } from './redux/actions';
import editorStateMap from './editorStateMap';
import modelManager from '../common/monaco/modelManager';
import { getElementData, getElementFiles } from './helpers';

export class TabsBar extends Component {
  static propTypes = {
    home: PropTypes.object.isRequired,
    rekitCmds: PropTypes.object.isRequired,
    router: PropTypes.object.isRequired,
    actions: PropTypes.object.isRequired,
  };

  componentWillReceiveProps(nextProps) {
    if (
      this.props.rekitCmds.execCmdPending &&
      !nextProps.rekitCmds.execCmdPending &&
      nextProps.rekitCmds.execCmdResult
    ) {
      // If current tab is renamed/moved/removed
      const cmdRes = nextProps.rekitCmds.execCmdResult;
      console.log('cmd exec result: ', cmdRes);
      if (/^(rename|move|remove)$/.test(cmdRes.args.commandName)) {
        const file = cmdRes.args.path;
        const tab = _.find(nextProps.home.openTabs, { key: file });
        if (tab) {
          this.handleClose({}, tab);
        }
      }
    }
  }

  componentDidUpdate() {
    if (this.delayScroll) clearTimeout(this.delayScroll);
    this.delayScroll = setTimeout(this.scrollActiveTabIntoView, 100);
  }

  getCurrentFile() {
    // only for element page
    const { pathname } = this.props.router.location;
    const arr = _.compact(pathname.split('/')).map(decodeURIComponent);
    if (arr[0] === 'element') return arr[1];
    return null;
  }

  getElementData(file) {
    const { elementById, projectRoot } = this.props.home;
    if (!file) return null;
    file = decodeURIComponent(file);
    const fullPath = projectRoot + file;
    const arr = fullPath.split('.');
    const ext = arr.length > 1 ? arr.pop() : null;
    const ele = elementById[file];

    if (!ele) return null;

    return {
      ...ele,
      hasDiagram: /^(js|jsx)$/.test(ext),
      hasTest: ele.feature && /^(js|jsx)$/.test(ext),
      hasCode: /^(js|jsx|html|css|less|scss|txt|json|sass|md|log|pl|py|sh|cmd)$/.test(ext),
      isPic: /^(jpe?g|png|gif|bmp)$/.test(ext),
    };
  }

  getTabTooltip(tab) {
    if (!_.startsWith(tab.key, '#')) return tab.key;
    return tab.name;
  }

  isChanged(tab) {
    const files = getElementFiles(this.props.home, tab.key);
    if (!files) return false;
    return (
      modelManager.isChanged(files.code) || modelManager.isChanged(files.test) || modelManager.isChanged(files.style)
    );
  }

  isCurrentTab(tab) {
    const { pathname } = document.location;
    switch (tab.type) {
      case 'home':
        return pathname === '/';
      case 'element':
        return tab.key === this.getCurrentFile();
      case 'routes':
        return pathname.indexOf(`/${tab.key}`) === 0;
      case 'tests':
        return _.startsWith(pathname, '/tools/tests');
      case 'coverage':
        return _.startsWith(pathname, '/tools/coverage');
      case 'build':
        return _.startsWith(pathname, '/tools/build');
      default:
        return false;
    }
  }

  openTab = key => {
    const tab = _.find(this.props.home.openTabs, { key });
    let path;
    switch (tab.type) {
      case 'home':
        path = '/';
        break;
      case 'element':
        path = `/element/${encodeURIComponent(key)}/${tab.subTab}`;
        break;
      case 'routes':
        path = `/${tab.key}/${tab.subTab || ''}`;
        break;
      case 'tests':
      case 'coverage':
      case 'build':
        path = tab.pathname;
        break;
      default:
        console.error('unknown tab type: ', tab);
        break;
    }
    if (document.location.pathname !== path) {
      history.push(path);
    }
  };

  scrollActiveTabIntoView = () => {
    delete this.delayScroll;
    if (!this.rootNode) return;
    const node = this.rootNode.querySelector('.tab-active');
    if (node) {
      scrollIntoView(node, this.rootNode, {
        allowHorizontalScroll: true,
        onlyScrollIfNeeded: true,
        offsetRight: 100,
        offsetLeft: 100,
      });
    }
    this.rootNode.scrollTop = 0; // Prevent vertical offset when switching tabs.
  };

  handleClose = (evt, tab, force) => {
    if (evt && evt.stopPropagation) evt.stopPropagation();
    const files = getElementFiles(this.props.home, tab.key);
    const data = getElementData(this.props.home, tab.key);

    const doClose = () => {
      if (files) {
        delete editorStateMap[files.code];
        delete editorStateMap[files.style];
        delete editorStateMap[files.test];
        modelManager.reset(files.code);
        modelManager.reset(files.style);
        modelManager.reset(files.test);
      }

      this.props.actions.closeTab(tab.key);
      const { historyTabs } = this.props.home;
      if (historyTabs.length === 1) {
        history.push('/welcome');
      } else if (this.isCurrentTab(tab)) {
        // Close the current one
        const nextKey = historyTabs[1]; // at this point the props has not been updated.
        this.openTab(nextKey);
      }
    };

    if (!force && files && [files.code, files.test, files.style].some(f => modelManager.isChanged(f))) {
      Modal.confirm({
        title: 'Discard changes?',
        content: `Do you want to discard changes you made to ${data.name}?`,
        okText: 'Discard',
        onOk: () => {
          doClose();
        },
        onCancel: () => {},
      });
    } else {
      doClose();
    }
  };

  handleMenuClick = (tab, menuKey) => {
    const { openTabs } = this.props.home;
    switch (menuKey) {
      case 'close-others':
        openTabs.filter(t => t.key !== tab.key).forEach(t => this.handleClose({}, t));
        break;
      case 'close-right':
        openTabs.slice(_.findIndex(openTabs, { key: tab.key }) + 1).forEach(t => this.handleClose({}, t));
        break;
      case 'close-self':
        this.handleClose({}, tab);
        break;
      default:
        break;
    }
  };

  assignRef = node => {
    this.rootNode = node;
  };

  render() {
    const { openTabs, sidePanelWidth } = this.props.home;
    const getMenu = tab => (
      <Menu onClick={args => this.handleMenuClick(tab, args.key)}>
        <Menu.Item key="close-others">Close others</Menu.Item>
        <Menu.Item key="close-right">Close to the right</Menu.Item>
        <Menu.Item key="close-self">Close</Menu.Item>
      </Menu>
    );
    return (
      <div className="home-tabs-bar" ref={this.assignRef} style={{ marginLeft: `${sidePanelWidth}px` }}>
        {openTabs.map(tab => (
          <Dropdown overlay={getMenu(tab)} trigger={['contextMenu']} key={tab.key}>
             this.openTab(tab.key)}
              className={classnames('tab', {
                'tab-active': this.isCurrentTab(tab),
                'tab-has-change': this.isChanged(tab),
              })}
            >
              <Icon type={tab.icon || 'file'} />
              {tab.name}
              <Icon type="close" onClick={evt => this.handleClose(evt, tab)} />

          </Dropdown>
        ))}

    );
  }
}

/* istanbul ignore next */
function mapStateToProps(state) {
  return {
    home: state.home,
    rekitCmds: state.rekitCmds,
    router: state.router,
  };
}

/* istanbul ignore next */
function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators({ closeTab }, dispatch),
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(TabsBar);
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies
 */

// Public node modules.
const fs = require('fs');
const path = require('path');
/* eslint-disable no-unused-vars */
module.exports = {
  provider: 'local',
  name: 'Local server',
  init: (config) => {
    return {
      upload: (file) => {
        return new Promise((resolve, reject) => {
          // write file in public/assets folder
          fs.writeFile(path.join(strapi.config.appPath, 'public', `uploads/${file.hash}${file.ext}`), file.buffer, (err) => {
            if (err) {
              return reject(err);
            }

            file.url = `/uploads/${file.hash}${file.ext}`;

            resolve();
          });
        });
      },
      delete: (file) => {
        return new Promise((resolve, reject) => {
          const filePath = path.join(strapi.config.appPath, 'public', `uploads/${file.hash}${file.ext}`);

          if (!fs.existsSync(filePath)) {
            return resolve('File doesn\'t exist');
          }

          // remove file from public/assets folder
          fs.unlink(filePath, (err) => {
            if (err) {
              return reject(err);
            }

            resolve();
          });
        });
      }
    };
  }
};
<commit_msg>Update index.js<commit_after>'use strict';

/**
 * Module dependencies
 */

// Public node modules.
const fs = require('fs');
const path = require('path');
/* eslint-disable no-unused-vars */
module.exports = {
  provider: 'local',
  name: 'Local server',
  init: (config) => {
    return {
      upload: (file) => {
        return new Promise((resolve, reject) => {
          // write file in public/assets folder
          fs.writeFile(path.join(strapi.config.public.path, `/uploads/${file.hash}${file.ext}`), file.buffer, (err) => {
            if (err) {
              return reject(err);
            }

            file.url = `/uploads/${file.hash}${file.ext}`;

            resolve();
          });
        });
      },
      delete: (file) => {
        return new Promise((resolve, reject) => {
          const filePath = path.join(strapi.config.public.path, `/uploads/${file.hash}${file.ext}`);

          if (!fs.existsSync(filePath)) {
            return resolve('File doesn\'t exist');
          }

          // remove file from public/assets folder
          fs.unlink(filePath, (err) => {
            if (err) {
              return reject(err);
            }

            resolve();
          });
        });
      }
    };
  }
};
<|endoftext|>


<commit_before>import { BigNumber } from "bignumber.js";

export const WORD_SIZE = 0x20;
export const MAX_WORD = new BigNumber(2).pow(256).minus(1);

export function typeIdentifier(definition) {
  return definition.typeDescriptions.typeIdentifier;
}

/**
 * returns basic type class for a variable definition node
 * e.g.:
 *  `t_uint256` becomes `uint`
 *  `t_struct$_Thing_$20_memory_ptr` becomes `struct`
 */
export function typeClass(definition) {
  return typeIdentifier(definition).match(/t_([^$_0-9]+)/)[1];
}

export function isReference(definition) {
  return typeIdentifier(definition).match(/_ptr$/) != null;
}

export function referenceType(definition) {
  return typeIdentifier(definition).match(/_([^_]+)_ptr$/)[1];
}

export function toBigNumber(bytes) {
  if (typeof bytes == "string") {
    return new BigNumber(bytes, 16);
  } else if (typeof bytes == "number") {
    return new BigNumber(bytes);
  } else if (bytes.reduce) {
    return bytes.reduce(
      (num, byte) => num.times(0x100).plus(byte),
      new BigNumber(0)
    );
  }
}

export function toSignedBigNumber(bytes) {
  if (bytes[0] < 0b10000000) {  // first bit is 0
    return toBigNumber(bytes);
  } else {
    return toBigNumber(bytes.map( (b) => 0xff - b )).plus(1).negated();
  }
}

export function toHexString(bytes, omitZeroes) {
  const pad = (s) => `${"00".slice(0, 2 - s.length)}${s}`

  let string = bytes.reduce(
    (str, byte) => `${str}${pad(byte.toString(16))}`, ""
  );

  if (omitZeroes) {
    string = string.replace(/^(00)+/, "");
  }

  if (string.length == 0) {
    string = "00";
  }

  return `0x${string}`;
}
<commit_msg>Add decode.utils.toBytes, toHexString length param<commit_after>import debugModule from "debug";
const debug = debugModule("debugger:data:decode:utils");

import { BigNumber } from "bignumber.js";

export const WORD_SIZE = 0x20;
export const MAX_WORD = new BigNumber(2).pow(256).minus(1);

export function typeIdentifier(definition) {
  return definition.typeDescriptions.typeIdentifier;
}

/**
 * returns basic type class for a variable definition node
 * e.g.:
 *  `t_uint256` becomes `uint`
 *  `t_struct$_Thing_$20_memory_ptr` becomes `struct`
 */
export function typeClass(definition) {
  return typeIdentifier(definition).match(/t_([^$_0-9]+)/)[1];
}

export function isReference(definition) {
  return typeIdentifier(definition).match(/_ptr$/) != null;
}

export function referenceType(definition) {
  return typeIdentifier(definition).match(/_([^_]+)_ptr$/)[1];
}

export function toBigNumber(bytes) {
  if (typeof bytes == "string") {
    return new BigNumber(bytes, 16);
  } else if (typeof bytes == "number") {
    return new BigNumber(bytes);
  } else if (bytes.reduce) {
    return bytes.reduce(
      (num, byte) => num.times(0x100).plus(byte),
      new BigNumber(0)
    );
  }
}

export function toSignedBigNumber(bytes) {
  if (bytes[0] < 0b10000000) {  // first bit is 0
    return toBigNumber(bytes);
  } else {
    return toBigNumber(bytes.map( (b) => 0xff - b )).plus(1).negated();
  }
}

/**
 * @param bytes - Uint8Array
 * @param length - desired byte length (pad with zeroes)
 * @param trim - omit leading zeroes
 */
export function toHexString(bytes, length, trim = false) {
  const pad = (s) => `${"00".slice(0, 2 - s.length)}${s}`

  //                                          0  1  2  3  4
  //                                 0  1  2  3  4  5  6  7
  // bytes.length:        5  -  0x(          e5 c2 aa 09 11 )
  // length (preferred):  8  -  0x( 00 00 00 e5 c2 aa 09 11 )
  //                                `--.---'
  //                                     offset 3
  if (bytes.length < length) {
    let prior = bytes;
    bytes = new Uint8Array(length);

    bytes.set(prior.buffer, length - bytes.length);
  }

  debug("bytes: %o");

  let string = bytes.reduce(
    (str, byte) => `${str}${pad(byte.toString(16))}`, ""
  );

  if (trim) {
    string = string.replace(/^(00)+/, "");
  }

  if (string.length == 0) {
    string = "00";
  }

  return `0x${string}`;
}

export function toBytes(number) {
  if (number < 0) {
    return [];
  }

  return new Uint8Array(
    number.toString(16)
      .match(/.{1,2}/g)
      .map( (byte) => parseInt(byte, 16) )
  );
}
<|endoftext|>


<commit_before>var fs = require('fs');
var hydraHeads          = require('./hydraHead'),
    HydraHead           = hydraHeads.HydraHead,
    HydraHeadStatic     = hydraHeads.HydraHeadStatic,
    HydraHeadFilesystem = hydraHeads.HydraHeadFilesystem,
    HydraHeadProxy      = hydraHeads.HydraHeadProxy;
var serveStaticFile = require('./hydraUtils.js').serveStaticFile,
    proxyRequest    = require('./hydraUtils.js').proxyRequest;

var InvalidHydraPluginException = function(message) {
    this.name    = "InvalidHydraPluginException";
    this.message = message || 'Invalid or incomplete plugin';
};
InvalidHydraPluginException.prototype.toString = function() {
    return "InvalidHydraPluginException";
};

var DuplicateHydraPluginException = function(message) {
    this.name    = "DuplicateHydraPluginException";
    this.message = message || 'Duplicate or incomplete plugin';
};
DuplicateHydraPluginException.prototype.toString = function() {
    return "DuplicateHydraPluginException";
};

var DuplicateHydraHeadNameException = function(head) {
    this.name    = "DuplicateHydraHeadNameException";
    this.message = 'Duplicate Hydra head name "' + head + '"';
};
DuplicateHydraHeadNameException.prototype.toString = function() {
    return "DuplicateHydraHeadNameException";
};

var HydraHeadNotFoundException = function(pluginName, headName) {
    this.name    = "HydraHeadNotFoundException";
    this.message = 'Could not find the given head ("' + pluginName +
            '" / "' + headName + '")';
};
HydraHeadNotFoundException.prototype.toString = function() {
    return "HydraHeadNotFoundException";
};

var HydraPluginNotFoundException = function(pluginName) {
    this.name    = "HydraPluginNotFoundException";
    this.message = 'Could not find the given plugin ("' + pluginName + '")';
};
HydraPluginNotFoundException.prototype.toString = function() {
    return "HydraPluginNotFoundException";
};

var InvalidHydraTestException = function(pluginName, testName) {
    this.name    = "InvalidHydraTestException";
    this.message = 'Could not find the given test ("' + pluginName + '" / "' +
            testName + '")';
};
InvalidHydraTestException.prototype.toString = function() {
    return "InvalidHydraTestException";
};

var Hydra = function() {
    this._plugins    = [];
    this._headMap    = {};
    this.currentTest = {plugin: '*default*',
                        test:   '*default*'};
    this._pluginLoadPath = ['hydra/plugins',
                            '/usr/local/share/hydra/plugins',
                            '/usr/share/hydra/plugins'];
    // Register all special plugins
    var adminPlugin = require('./plugins/admin.js');
    adminPlugin.heads = adminPlugin.getBodyParts({hydra: this});
    this._registerPluginObject(adminPlugin);
    this._registerPluginObject({name: '*dynamic*', heads: []});
    this._registerPluginObject({name: '*current-test*', heads: []});
};
Hydra.prototype._validatePluginName = function(pluginName) {
    return (typeof(pluginName) === 'string' &&
                pluginName.match(new RegExp('^[a-z0-9_-]+$', 'i')));
};
Hydra.prototype._addHeadsToPluginStructure = function(heads, pluginStructure) {
    var anonymousHeadCount = 0;
    heads.forEach(function(head) {
        // Calculate head name if not specified
        if (typeof(head.name) === 'undefined') {
            head.name = 'anonymousHydraHead' + anonymousHeadCount;
            anonymousHeadCount++;
            while (pluginStructure.headMap.hasOwnProperty(head.name)) {
                head.name = 'anonymousHydraHead' + anonymousHeadCount;
                anonymousHeadCount++;
            }
        }

        if (pluginStructure.headMap.hasOwnProperty(head.name)) {
            throw new DuplicateHydraHeadNameException(head.name);
        }
        pluginStructure.headMap[head.name] = head;
    });
};
Hydra.prototype._registerPluginObject = function(plugin) {
    var name = plugin.name;
    if (this._plugins.some(function(v) { return v.plugin.name === name; })) {
        throw new DuplicateHydraPluginException();
    }

    var pluginToRegister = {
        name:  plugin.name,
        heads: plugin.heads || [],
        tests: plugin.tests || {}
    };

    var pluginInternalStructure = {headMap: {}, plugin: pluginToRegister};
    this._addHeadsToPluginStructure(pluginToRegister.heads,
                                    pluginInternalStructure);
    this._plugins.push(pluginInternalStructure);
};
Hydra.prototype.registerPluginObject = function(plugin) {
    if (! this._validatePluginName(plugin.name))
        throw new InvalidHydraPluginException("Invalid plugin name '" +
                                              plugin.name + "'");
    var hasHeads = (typeof plugin.heads === 'object') && plugin.heads.length;
    var hasTests = false;
    if (typeof(plugin.tests) === 'object') {
        for (var t in plugin.tests) {
            if (plugin.tests.hasOwnProperty(t)) hasTests = true;
        }
    }
    if (!hasHeads && !hasTests)
        throw new InvalidHydraPluginException("Plugin doesn't have any heads or tests");

    this._registerPluginObject(plugin);
};
Hydra.prototype.registerDynamicHead = function(head) {
    var done = false;
    for (var i = 0, len = this._plugins.length; i < len; i++) {
        if (this._plugins[i].plugin.name === '*dynamic*') {
            this._addHeadsToPluginStructure([head], this._plugins[i]);
            this._plugins[i].plugin.heads.push(head);
            done = true;
        }
    }
    if (! done) {
        throw "Internal error: couldn't find the dynamic head plugin";
    }
};
Hydra.prototype.getPluginNames = function() {
    return this.getPlugins().map(function(p) { return p.name; });
};
Hydra.prototype.getPlugins = function() {
    return this._plugins.map(function(v) { return v.plugin; });
};
Hydra.prototype.getPlugin = function(pluginName) {
    var pluginList = this._plugins.filter(function(p) {
                         return p.plugin.name === pluginName;
                     });
    if (pluginList.length) {
        return pluginList[0].plugin;
    }
    throw new HydraPluginNotFoundException(pluginName);
};
Hydra.prototype.headForPath = function(url, afterHead) {
    var canMatch = !afterHead;
    for (var i = 0, len = this._plugins.length; i < len; i++) {
        var plugin = this._plugins[i].plugin;
        for (var j = 0, len2 = plugin.heads.length; j < len2; j++) {
            if (canMatch && plugin.heads[j].canHandle(url)) {
                return {plugin: plugin, head: plugin.heads[j]};
            }

            if (typeof afterHead === 'object' &&
                    this._plugins[i].plugin.name === afterHead.plugin.name &&
                    plugin.heads[j].name  === afterHead.head.name) {
                canMatch = true;
            }
        }
    }
    return undefined;
};
Hydra.prototype.handle = function(req, res, cb, afterHead) {
    var r = this.headForPath(req.url, afterHead);
    if (typeof(r) !== 'undefined') {
        res.statusCode = 200;
        var self = this;
        r.head.handle(req, res, cb, function(req2, res2, cb2) {
            self.handle(req2, res2, cb2, r);
        });
    } else {
        res.statusCode = 404;
        res.send("Not Found");
        cb();
    }
};
Hydra.prototype.findHead = function(pluginName, headName) {
    for (var i = 0, len = this._plugins.length; i < len; i++) {
        if (this._plugins[i].plugin.name === pluginName) {
            var head = this._plugins[i].headMap[headName];
            if (typeof(head) === 'undefined') {
                throw new HydraHeadNotFoundException(pluginName, headName);
            } else {
                return head;
            }
        }
    }
    throw new HydraHeadNotFoundException(pluginName, headName);
};
Hydra.prototype.detachHead = function(pluginName, headName) {
    this.findHead(pluginName, headName).detach();
};
Hydra.prototype.attachHead = function(pluginName, headName) {
    this.findHead(pluginName, headName).attach();
};
Hydra.prototype.addPluginLoadPath = function(path) {
    this._pluginLoadPath.unshift(path);
};
// Return an object with keys 'module', 'config' and 'name'. The
// 'config' value is another object with keys 'path', 'hydra' and any
// other values from the configuration. It throws an exception
// HydraPluginNotFoundException if the plugin could not be found
Hydra.prototype.requirePlugin = function(name, config, opts) {
    opts = opts || {};
    var rootDir = opts.rootDir ? fs.realpathSync(opts.rootDir) : '';
    var plugin = {};
    var stat = undefined, fullPath;
    for (var i = 0, len = this._pluginLoadPath.length; i < len; i++) {
        try {
            fullPath = rootDir +
                (this._pluginLoadPath[i].indexOf('/') !== 0 ?
                     fs.realpathSync('.') + '/' : '') +
                this._pluginLoadPath[i] + '/' + name;
            stat = fs.statSync(fullPath);
        } catch (e) {
            // It's ok if the plugin is not in this directory
            if (e.code !== 'ENOENT') {
                throw e;
            }
        }
        if (stat && stat.isDirectory()) {
            plugin.module = require(fullPath);
            break;
        }
    }
    if (plugin.module) {
        plugin.config = {hydra: this, path: fullPath};
        for (var p in config) plugin.config[p] = config[p];
        return plugin;
    } else {
        throw new HydraPluginNotFoundException(name);
    }
};
Hydra.prototype.startTest = function(pluginName, testName) {
    this.stopTest();

    var found = false;
    var testPlugin, i, len;
    for (i = 0, len = this._plugins.length; i < len; i++) {
        if (this._plugins[i].plugin.name === pluginName) {
            if (this._plugins[i].plugin.tests.hasOwnProperty(testName)) {
                testPlugin = this._plugins[i].plugin;
                this.currentTest = {plugin: pluginName, test: testName};
                found = true;
            }
        }
    }

    if (found) {
        var done = false;
        for (i = 0, len = this._plugins.length; i < len; i++) {
            if (this._plugins[i].plugin.name === '*current-test*') {
                var heads = testPlugin.tests[testName].heads;
                this._addHeadsToPluginStructure(heads, this._plugins[i]);
                Array.prototype.push.apply(this._plugins[i].plugin.heads,
                                           heads);
                done = true;
            }
        }
        if (! done) {
            throw "Internal error: couldn't find the current test plugin";
        }
    } else {
        throw new InvalidHydraTestException(pluginName, testName);
    }
};
Hydra.prototype.stopTest = function() {
    this.currentTest = {plugin: '*default*', test: '*default*'};

    for (var i = 0, len = this._plugins.length; i < len; i++) {
        if (this._plugins[i].plugin.name === '*current-test*') {
            this._plugins[i] = {headMap: {},
                                plugin: {name: '*current-test*', heads: []}};
        }
    }
};

function headTypeToConstructor(headType) {
    var headTypeMap = {
        'generic': HydraHead,
        'static': HydraHeadStatic,
        'filesystem': HydraHeadFilesystem,
        'proxy': HydraHeadProxy
    };
    return headTypeMap[headType];
}

exports.Hydra           = Hydra;
exports.serveStaticFile = serveStaticFile;
exports.proxyRequest    = proxyRequest;
exports.heads           = hydraHeads;
<commit_msg>Remove useless headTypeToConstructor<commit_after>var fs = require('fs');
var hydraHeads          = require('./hydraHead'),
    HydraHead           = hydraHeads.HydraHead,
    HydraHeadStatic     = hydraHeads.HydraHeadStatic,
    HydraHeadFilesystem = hydraHeads.HydraHeadFilesystem,
    HydraHeadProxy      = hydraHeads.HydraHeadProxy;
var serveStaticFile = require('./hydraUtils.js').serveStaticFile,
    proxyRequest    = require('./hydraUtils.js').proxyRequest;

var InvalidHydraPluginException = function(message) {
    this.name    = "InvalidHydraPluginException";
    this.message = message || 'Invalid or incomplete plugin';
};
InvalidHydraPluginException.prototype.toString = function() {
    return "InvalidHydraPluginException";
};

var DuplicateHydraPluginException = function(message) {
    this.name    = "DuplicateHydraPluginException";
    this.message = message || 'Duplicate or incomplete plugin';
};
DuplicateHydraPluginException.prototype.toString = function() {
    return "DuplicateHydraPluginException";
};

var DuplicateHydraHeadNameException = function(head) {
    this.name    = "DuplicateHydraHeadNameException";
    this.message = 'Duplicate Hydra head name "' + head + '"';
};
DuplicateHydraHeadNameException.prototype.toString = function() {
    return "DuplicateHydraHeadNameException";
};

var HydraHeadNotFoundException = function(pluginName, headName) {
    this.name    = "HydraHeadNotFoundException";
    this.message = 'Could not find the given head ("' + pluginName +
            '" / "' + headName + '")';
};
HydraHeadNotFoundException.prototype.toString = function() {
    return "HydraHeadNotFoundException";
};

var HydraPluginNotFoundException = function(pluginName) {
    this.name    = "HydraPluginNotFoundException";
    this.message = 'Could not find the given plugin ("' + pluginName + '")';
};
HydraPluginNotFoundException.prototype.toString = function() {
    return "HydraPluginNotFoundException";
};

var InvalidHydraTestException = function(pluginName, testName) {
    this.name    = "InvalidHydraTestException";
    this.message = 'Could not find the given test ("' + pluginName + '" / "' +
            testName + '")';
};
InvalidHydraTestException.prototype.toString = function() {
    return "InvalidHydraTestException";
};

var Hydra = function() {
    this._plugins    = [];
    this._headMap    = {};
    this.currentTest = {plugin: '*default*',
                        test:   '*default*'};
    this._pluginLoadPath = ['hydra/plugins',
                            '/usr/local/share/hydra/plugins',
                            '/usr/share/hydra/plugins'];
    // Register all special plugins
    var adminPlugin = require('./plugins/admin.js');
    adminPlugin.heads = adminPlugin.getBodyParts({hydra: this});
    this._registerPluginObject(adminPlugin);
    this._registerPluginObject({name: '*dynamic*', heads: []});
    this._registerPluginObject({name: '*current-test*', heads: []});
};
Hydra.prototype._validatePluginName = function(pluginName) {
    return (typeof(pluginName) === 'string' &&
                pluginName.match(new RegExp('^[a-z0-9_-]+$', 'i')));
};
Hydra.prototype._addHeadsToPluginStructure = function(heads, pluginStructure) {
    var anonymousHeadCount = 0;
    heads.forEach(function(head) {
        // Calculate head name if not specified
        if (typeof(head.name) === 'undefined') {
            head.name = 'anonymousHydraHead' + anonymousHeadCount;
            anonymousHeadCount++;
            while (pluginStructure.headMap.hasOwnProperty(head.name)) {
                head.name = 'anonymousHydraHead' + anonymousHeadCount;
                anonymousHeadCount++;
            }
        }

        if (pluginStructure.headMap.hasOwnProperty(head.name)) {
            throw new DuplicateHydraHeadNameException(head.name);
        }
        pluginStructure.headMap[head.name] = head;
    });
};
Hydra.prototype._registerPluginObject = function(plugin) {
    var name = plugin.name;
    if (this._plugins.some(function(v) { return v.plugin.name === name; })) {
        throw new DuplicateHydraPluginException();
    }

    var pluginToRegister = {
        name:  plugin.name,
        heads: plugin.heads || [],
        tests: plugin.tests || {}
    };

    var pluginInternalStructure = {headMap: {}, plugin: pluginToRegister};
    this._addHeadsToPluginStructure(pluginToRegister.heads,
                                    pluginInternalStructure);
    this._plugins.push(pluginInternalStructure);
};
Hydra.prototype.registerPluginObject = function(plugin) {
    if (! this._validatePluginName(plugin.name))
        throw new InvalidHydraPluginException("Invalid plugin name '" +
                                              plugin.name + "'");
    var hasHeads = (typeof plugin.heads === 'object') && plugin.heads.length;
    var hasTests = false;
    if (typeof(plugin.tests) === 'object') {
        for (var t in plugin.tests) {
            if (plugin.tests.hasOwnProperty(t)) hasTests = true;
        }
    }
    if (!hasHeads && !hasTests)
        throw new InvalidHydraPluginException("Plugin doesn't have any heads or tests");

    this._registerPluginObject(plugin);
};
Hydra.prototype.registerDynamicHead = function(head) {
    var done = false;
    for (var i = 0, len = this._plugins.length; i < len; i++) {
        if (this._plugins[i].plugin.name === '*dynamic*') {
            this._addHeadsToPluginStructure([head], this._plugins[i]);
            this._plugins[i].plugin.heads.push(head);
            done = true;
        }
    }
    if (! done) {
        throw "Internal error: couldn't find the dynamic head plugin";
    }
};
Hydra.prototype.getPluginNames = function() {
    return this.getPlugins().map(function(p) { return p.name; });
};
Hydra.prototype.getPlugins = function() {
    return this._plugins.map(function(v) { return v.plugin; });
};
Hydra.prototype.getPlugin = function(pluginName) {
    var pluginList = this._plugins.filter(function(p) {
                         return p.plugin.name === pluginName;
                     });
    if (pluginList.length) {
        return pluginList[0].plugin;
    }
    throw new HydraPluginNotFoundException(pluginName);
};
Hydra.prototype.headForPath = function(url, afterHead) {
    var canMatch = !afterHead;
    for (var i = 0, len = this._plugins.length; i < len; i++) {
        var plugin = this._plugins[i].plugin;
        for (var j = 0, len2 = plugin.heads.length; j < len2; j++) {
            if (canMatch && plugin.heads[j].canHandle(url)) {
                return {plugin: plugin, head: plugin.heads[j]};
            }

            if (typeof afterHead === 'object' &&
                    this._plugins[i].plugin.name === afterHead.plugin.name &&
                    plugin.heads[j].name  === afterHead.head.name) {
                canMatch = true;
            }
        }
    }
    return undefined;
};
Hydra.prototype.handle = function(req, res, cb, afterHead) {
    var r = this.headForPath(req.url, afterHead);
    if (typeof(r) !== 'undefined') {
        res.statusCode = 200;
        var self = this;
        r.head.handle(req, res, cb, function(req2, res2, cb2) {
            self.handle(req2, res2, cb2, r);
        });
    } else {
        res.statusCode = 404;
        res.send("Not Found");
        cb();
    }
};
Hydra.prototype.findHead = function(pluginName, headName) {
    for (var i = 0, len = this._plugins.length; i < len; i++) {
        if (this._plugins[i].plugin.name === pluginName) {
            var head = this._plugins[i].headMap[headName];
            if (typeof(head) === 'undefined') {
                throw new HydraHeadNotFoundException(pluginName, headName);
            } else {
                return head;
            }
        }
    }
    throw new HydraHeadNotFoundException(pluginName, headName);
};
Hydra.prototype.detachHead = function(pluginName, headName) {
    this.findHead(pluginName, headName).detach();
};
Hydra.prototype.attachHead = function(pluginName, headName) {
    this.findHead(pluginName, headName).attach();
};
Hydra.prototype.addPluginLoadPath = function(path) {
    this._pluginLoadPath.unshift(path);
};
// Return an object with keys 'module', 'config' and 'name'. The
// 'config' value is another object with keys 'path', 'hydra' and any
// other values from the configuration. It throws an exception
// HydraPluginNotFoundException if the plugin could not be found
Hydra.prototype.requirePlugin = function(name, config, opts) {
    opts = opts || {};
    var rootDir = opts.rootDir ? fs.realpathSync(opts.rootDir) : '';
    var plugin = {};
    var stat = undefined, fullPath;
    for (var i = 0, len = this._pluginLoadPath.length; i < len; i++) {
        try {
            fullPath = rootDir +
                (this._pluginLoadPath[i].indexOf('/') !== 0 ?
                     fs.realpathSync('.') + '/' : '') +
                this._pluginLoadPath[i] + '/' + name;
            stat = fs.statSync(fullPath);
        } catch (e) {
            // It's ok if the plugin is not in this directory
            if (e.code !== 'ENOENT') {
                throw e;
            }
        }
        if (stat && stat.isDirectory()) {
            plugin.module = require(fullPath);
            break;
        }
    }
    if (plugin.module) {
        plugin.config = {hydra: this, path: fullPath};
        for (var p in config) plugin.config[p] = config[p];
        return plugin;
    } else {
        throw new HydraPluginNotFoundException(name);
    }
};
Hydra.prototype.startTest = function(pluginName, testName) {
    this.stopTest();

    var found = false;
    var testPlugin, i, len;
    for (i = 0, len = this._plugins.length; i < len; i++) {
        if (this._plugins[i].plugin.name === pluginName) {
            if (this._plugins[i].plugin.tests.hasOwnProperty(testName)) {
                testPlugin = this._plugins[i].plugin;
                this.currentTest = {plugin: pluginName, test: testName};
                found = true;
            }
        }
    }

    if (found) {
        var done = false;
        for (i = 0, len = this._plugins.length; i < len; i++) {
            if (this._plugins[i].plugin.name === '*current-test*') {
                var heads = testPlugin.tests[testName].heads;
                this._addHeadsToPluginStructure(heads, this._plugins[i]);
                Array.prototype.push.apply(this._plugins[i].plugin.heads,
                                           heads);
                done = true;
            }
        }
        if (! done) {
            throw "Internal error: couldn't find the current test plugin";
        }
    } else {
        throw new InvalidHydraTestException(pluginName, testName);
    }
};
Hydra.prototype.stopTest = function() {
    this.currentTest = {plugin: '*default*', test: '*default*'};

    for (var i = 0, len = this._plugins.length; i < len; i++) {
        if (this._plugins[i].plugin.name === '*current-test*') {
            this._plugins[i] = {headMap: {},
                                plugin: {name: '*current-test*', heads: []}};
        }
    }
};

exports.Hydra           = Hydra;
exports.serveStaticFile = serveStaticFile;
exports.proxyRequest    = proxyRequest;
exports.heads           = hydraHeads;
<|endoftext|>


<commit_before>var Bn = ( function(root, $, _) {
  var Brain = {}

  var GetId = ( function (prefix) {
    var keyId = 0
    return function () {return (prefix + (keyId++))}
  }('BrainKids') )

  var Event = {
    // 这是保存内部事件处理器和订阅者的地方
    processors: {},
    subscribers: [],
    maintainSubs: [],

    // 初始化对象的公共接口
    initialization: function() {},

    // 内部处理器的订阅/解除订阅的方法
    on: function(e, method, options) {
      if (!this.processors[e.type] && !this.processors[e.type].length) {
        this.processors[e.type] = []
      }

      this.processors[e.type].push({callback: method, options: options, invokenCounter: 0})
      return method
    },

    off: function(e, method) {
      if (!method) {
        return delete this.processors[e.type]
      }

      if (this.processors[e.type] && this.processors[e.type]) {
        var idx = this.processors[e.type].findIndex( (val) => val.callback === method)
        if (idx<0) return false
        this.processors[e.type].splice(idx, 1)
      }
    }

    // 其他对象的订阅/取消订阅的方法
    listenTo: function(target) {
      try {
        return target.subscribe(this)
      } catch (e) {
        console.log(e.type, e.message);
        return false
      }
    }

    stopListening: function(target) {
      if (!target) return null

      const self = this
      let temp = []
      target.subscribers.forEach(
        function(s) {
          if (s.clientId !== self.clientId) {
            temp.push(s)
          }
        }
      )

      target.subscribers = temp
    }
  }

  return Brain
}(window, window.$, window._) )<commit_msg>fix: 上一版提交中少了几个逗号<commit_after>var Bn = ( function(root, $, _) {
  var Brain = {}

  var GetId = ( function (prefix) {
    var keyId = 0
    return function () {return (prefix + (keyId++))}
  }('BrainKids') )

  var Event = {
    // 这是保存内部事件处理器和订阅者的地方
    processors: {},
    subscribers: [],
    maintainSubs: [],

    // 初始化对象的公共接口
    initialization: function() {},

    // 内部处理器的订阅/解除订阅的方法
    on: function(e, method, options) {
      if (!this.processors[e.type] && !this.processors[e.type].length) {
        this.processors[e.type] = []
      }

      this.processors[e.type].push({callback: method, options: options, invokenCounter: 0})
      return method
    },

    off: function(e, method) {
      if (!method) {
        return delete this.processors[e.type]
      }

      if (this.processors[e.type] && this.processors[e.type]) {
        var idx = this.processors[e.type].findIndex( (val) => val.callback === method)
        if (idx<0) return false
        this.processors[e.type].splice(idx, 1)
      }
    },

    // 其他对象的订阅/取消订阅的方法
    listenTo: function(target) {
      try {
        return target.subscribe(this)
      } catch (e) {
        console.log(e.type, e.message);
        return false
      }
    },

    stopListening: function(target) {
      if (!target) return null

      const self = this
      let temp = []
      target.subscribers.forEach(
        function(s) {
          if (s.clientId !== self.clientId) {
            temp.push(s)
          }
        }
      )

      target.subscribers = temp
    }

  }

  return Brain
}(window, window.$, window._) )<|endoftext|>


<commit_before>var common = require('./common');

var Id3v1 = module.exports = function(stream) {
  this.stream = stream;
  this.parse();
};

Id3v1.prototype = new process.EventEmitter();

Id3v1.prototype.parse = function() {
  var self = this;

  self.stream.on('data', function(data) {
    self.stream.onRealEnd(function() {
      parse(data);
    });
  });

  function parse(data) {
    try {
      var offset = data.length - 128;

      var header = data.toString('ascii', offset, offset += 3);

      if (header !== 'TAG') {
        throw new Error('Expected id3v1.1 header but was not found.');
      }

      var title = data.toString('ascii', offset, offset += 30);
      self.emit('title', title.trim().replace(/\x00/g, ''));

      var artist = data.toString('ascii', offset, offset += 30);
      self.emit('artist', artist.trim().replace(/\x00/g, ''));

      var album = data.toString('ascii', offset, offset += 30);
      self.emit('album', album.trim().replace(/\x00/g, ''));

      var year = data.toString('ascii', offset, offset += 4);
      self.emit('year', year.trim().replace(/\x00/g, ''));

      var comment = data.toString('ascii', offset, offset += 28);
      self.emit('comment', comment.trim().replace(/\x00/g, ''));

      var track = data[data.length - 2];
      self.emit('track', track);

      var genre = common.GENRES[data[data.length - 1]];
      self.emit('genre', genre);

      self.emit('done');

    } catch (exception) {
      self.emit('done', exception);
    }
  }
}<commit_msg>Read data from end of stream correctly. Fixes #4.<commit_after>var common = require('./common');

var Id3v1 = module.exports = function(stream) {
  this.stream = stream;
  this.parse();
};

Id3v1.prototype = new process.EventEmitter();

Id3v1.prototype.parse = function() {
  var self = this,
      endData = null;

  self.stream.on('data', function(data) {
    endData = data;
  });

  self.stream.onRealEnd(function() {
    parse(endData);
  });

  function parse(data) {
    try {
      var offset = data.length - 128;

      var header = data.toString('ascii', offset, offset += 3);

      if (header !== 'TAG') {
        throw new Error('Expected id3v1.1 header but was not found.');
      }

      var title = data.toString('ascii', offset, offset += 30);
      self.emit('title', title.trim().replace(/\x00/g, ''));

      var artist = data.toString('ascii', offset, offset += 30);
      self.emit('artist', artist.trim().replace(/\x00/g, ''));

      var album = data.toString('ascii', offset, offset += 30);
      self.emit('album', album.trim().replace(/\x00/g, ''));

      var year = data.toString('ascii', offset, offset += 4);
      self.emit('year', year.trim().replace(/\x00/g, ''));

      var comment = data.toString('ascii', offset, offset += 28);
      self.emit('comment', comment.trim().replace(/\x00/g, ''));

      var track = data[data.length - 2];
      self.emit('track', track);

      var genre = common.GENRES[data[data.length - 1]];
      self.emit('genre', genre);

      self.emit('done');

    } catch (exception) {
      self.emit('done', exception);
    }
  }
}<|endoftext|>